[
  {
    "path": ".clang-format",
    "content": "---\nAccessModifierOffset: -4\nAllowShortFunctionsOnASingleLine: None\nAllowShortEnumsOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortBlocksOnASingleLine: Never\nAllowShortIfStatementsOnASingleLine: WithoutElse\nAllowShortLambdasOnASingleLine: Inline\nAllowShortLoopsOnASingleLine: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nAllowAllConstructorInitializersOnNextLine : false\nBreakConstructorInitializers: AfterColon\nAlwaysBreakTemplateDeclarations: Yes\nColumnLimit: 130\nContinuationIndentWidth: 4\nCpp11BracedListStyle: false\nBreakBeforeBraces: Custom\nBraceWrapping:\n  AfterCaseLabel: false\n  AfterClass: false\n  AfterControlStatement: Never\n  AfterEnum: false\n  AfterFunction: false\n  AfterNamespace: false\n  AfterStruct: false\n  AfterUnion: false\n  AfterExternBlock: false\n  BeforeCatch: true\n  BeforeElse: true\n  BeforeLambdaBody: false\n  BeforeWhile: false\n  SplitEmptyFunction: false\n  SplitEmptyRecord: false\n  SplitEmptyNamespace: false\nFixNamespaceComments: true\nIncludeBlocks: Regroup\nIndentPPDirectives: None\nIndentWrappedFunctionNames: false\nPenaltyReturnTypeOnItsOwnLine: 6\nPointerAlignment: Left\nSpaceAfterTemplateKeyword: false\nIndentWidth: 4\nTabWidth: 4\nInsertNewlineAtEOF: true\nBasedOnStyle: LLVM\n\n---\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: Continuous Integration\n\non:\n  push:\n    branches:\n      - master\n\n  pull_request:\n    branches:\n      - master\n\n  workflow_dispatch: ~\n\n  schedule:\n    - cron: '0 8 * * 1'\n\njobs:\n  gcc:\n    uses: ./.github/workflows/gcc.yml\n\n  clang:\n    uses: ./.github/workflows/clang.yml\n\n  msvc:\n    uses: ./.github/workflows/msvc.yml\n\n  examples:\n    uses: ./.github/workflows/examples.yml\n  \n  module_tests:\n    uses: ./.github/workflows/module-tests.yml\n"
  },
  {
    "path": ".github/workflows/clang.yml",
    "content": "env:\n  CTEST_OUTPUT_ON_FAILURE: 1\n  LIBCXX_STANDARDS_JSON: '[\"20\",\"23\"]'\n\non:\n  workflow_call:\n\njobs:\n  clang:\n    name: clang-${{ matrix.version }} (C++${{ matrix.standard }})\n\n    strategy:\n      fail-fast: false\n      matrix:\n        version: [17]\n        standard: [11, 14, 17, 20, 23]\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v1\n    \n      - name: Install Ninja\n        uses: seanmiddleditch/gha-setup-ninja@master\n\n      - name: Install Clang ${{ matrix.version }}\n        run: sudo apt-get install -y clang-${{ matrix.version }}\n      \n      - name: Install libc++ Clang ${{ matrix.version }}\n        if: contains(fromJson(env.LIBCXX_STANDARDS_JSON), matrix.standard)\n        run: sudo apt-get install -y libc++-${{ matrix.version }}-dev libc++abi-${{ matrix.version }}-dev\n\n      - name: Configure (install)\n        env:\n          CC: clang-${{ matrix.version }}\n          CXX: clang-${{ matrix.version }}\n        run: |\n          sudo apt-get update && sudo apt-get install -y jq\n          STDS=$(echo '${{ env.LIBCXX_STANDARDS_JSON }}' | jq -r '.[]' | xargs)\n          if echo \"$STDS\" | grep -wq \"${{ matrix.standard }}\"; then\n            CXX_FLAGS='-stdlib=libc++'\n          else\n            CXX_FLAGS=''\n          fi\n          cmake -S . -B build/install -G Ninja \\\n            -D CMAKE_CXX_STANDARD:STRING=${{ matrix.standard }} \\\n            -D CMAKE_CXX_FLAGS=$CXX_FLAGS \\\n            -D CPP_LAZY_INSTALL:BOOL=ON \\\n            -D CMAKE_BUILD_TYPE:STRING=Release \\\n            -D CMAKE_INSTALL_PREFIX=build/prefix \\\n            -D CMAKE_CXX_COMPILER=clang++-${{ matrix.version }}\n\n      - name: Build (install)\n        run: cmake --build build/install -j $(nproc)\n\n      - name: Install\n        run: cmake --install build/install\n\n      - name: Configure tests\n        run: |\n          sudo apt-get update && sudo apt-get install -y jq\n          STDS=$(echo '${{ env.LIBCXX_STANDARDS_JSON }}' | jq -r '.[]' | xargs)\n          if echo \"$STDS\" | grep -wq \"${{ matrix.standard }}\"; then\n            CXX_FLAGS='-stdlib=libc++'\n          else\n            CXX_FLAGS=''\n          fi\n          cmake -S tests -B build/tests -G Ninja \\\n            -D CMAKE_BUILD_TYPE:STRING=Release \\\n            -D \"CMAKE_CXX_FLAGS=$CXX_FLAGS\" \\\n            -D CPP_LAZY_DOCTEST_VERSION:STRING=${{ vars.DOCTEST_VERSION }} \\\n            -D TEST_INSTALLED_VERSION:BOOL=YES \\\n            -D CMAKE_CXX_COMPILER=clang++-${{ matrix.version }} \\\n            -D CMAKE_INSTALL_PREFIX=build/prefix \\\n            -D CMAKE_CXX_STANDARD:STRING=${{ matrix.standard }}\n\n      - name: Build tests\n        run: cmake --build build/tests -j $(nproc)\n\n      - name: Run tests\n        working-directory: build/tests\n        run: ctest -VV --output-on-failure -j $(nproc)\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ \"master\", dev ]\n  pull_request:\n    branches: [ \"master\" ]\n\nenv:\n  DOCTEST_VERSION: '2.4.12'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'cpp' ]\n        standard: [ '11', '20' ]\n        version: [ '13' ]\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v3\n\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v3\n      with:\n        languages: ${{ matrix.language }}\n\n    - name: Install GCC ${{ matrix.version }}\n      run: sudo apt-get install -y gcc-${{ matrix.version }} g++-${{ matrix.version }}\n\n    - name: Configure (install)\n      env:\n        CC: gcc-${{ matrix.version }}\n        CXX: g++-${{ matrix.version }}\n      run: cmake -S . -B build/install -G Ninja\n        -D CPP_LAZY_INSTALL:BOOL=ON\n        -D CMAKE_CXX_STANDARD:STRING=${{ matrix.standard }}\n        -D CMAKE_BUILD_TYPE:STRING=Release\n        -D CMAKE_INSTALL_PREFIX=build/prefix\n\n    - name: Build (install)\n      run: cmake --build build/install -j $(nproc)\n\n    - name: Install\n      run: cmake --install build/install\n\n    - name: Configure tests\n      run: cmake -S tests -B build/tests -G Ninja\n        -D CMAKE_BUILD_TYPE:STRING=Release\n        -D CPP_LAZY_DOCTEST_VERSION:STRING=${{ env.DOCTEST_VERSION }}\n        -D TEST_INSTALLED_VERSION:BOOL=YES\n        -D CMAKE_INSTALL_PREFIX=build/prefix\n        -D CMAKE_CXX_STANDARD:STRING=${{ matrix.standard }}\n\n    - name: Build tests\n      run: cmake --build build/tests -j $(nproc)\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v3\n"
  },
  {
    "path": ".github/workflows/examples.yml",
    "content": "on:\n  workflow_call:\n\njobs:\n  examples:\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        version: [11, 14, 17, 20, 23]\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v1\n\n      - name: Install Ninja\n        uses: seanmiddleditch/gha-setup-ninja@master\n\n      - name: Configure\n        run: cmake -S examples -B build/examples -G Ninja\n          -D CMAKE_BUILD_TYPE:STRING=Release\n          -D CMAKE_CXX_STANDARD:STRING=${{ matrix.version }}\n\n      - name: Build\n        run: cmake --build build/examples -j $(nproc)\n"
  },
  {
    "path": ".github/workflows/gcc.yml",
    "content": "env:\n  CTEST_OUTPUT_ON_FAILURE: 1\n\non:\n  workflow_call:\n\njobs:\n  gcc:\n    name: gcc-${{ matrix.version }} (C++${{ matrix.standard }})\n\n    strategy:\n      fail-fast: false\n      matrix:\n        version: [11, 13]\n        standard: [11, 14, 17, 20, 23]\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v1\n\n      - name: Install Ninja\n        uses: seanmiddleditch/gha-setup-ninja@master\n\n      - name: Install GCC ${{ matrix.version }}\n        run: |\n          sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y\n          sudo apt update\n          sudo apt-get install -y gcc-${{ matrix.version }} g++-${{ matrix.version }}\n\n      - name: Configure (install)\n        env:\n          CC: gcc-${{ matrix.version }}\n          CXX: g++-${{ matrix.version }}\n        run: cmake -S . -B build/install -G Ninja\n          -D CMAKE_CXX_STANDARD:STRING=${{ matrix.standard }}\n          -D CMAKE_BUILD_TYPE:STRING=Release\n          -D CPP_LAZY_INSTALL:BOOL=ON\n          -D CMAKE_INSTALL_PREFIX=build/prefix\n          -D CMAKE_CXX_COMPILER=g++-${{ matrix.version }}\n\n      - name: Build (install)\n        run: cmake --build build/install -j $(nproc)\n\n      - name: Install\n        run: cmake --install build/install\n\n      - name: Configure tests\n        run: cmake -S tests -B build/tests -G Ninja\n          -D CMAKE_BUILD_TYPE:STRING=Release\n          -D CPP_LAZY_DOCTEST_VERSION:STRING=${{ vars.DOCTEST_VERSION }}\n          -D TEST_INSTALLED_VERSION:BOOL=YES\n          -D CMAKE_INSTALL_PREFIX=build/prefix\n          -D CMAKE_CXX_STANDARD:STRING=${{ matrix.standard }}\n          -D CMAKE_CXX_COMPILER=g++-${{ matrix.version }}\n\n      - name: Build tests\n        run: cmake --build build/tests -j $(nproc)\n\n      - name: Run tests\n        working-directory: build/tests\n        run: ctest --output-on-failure -VV -j $(nproc)\n"
  },
  {
    "path": ".github/workflows/make-release.yml",
    "content": "name: Make release package\npermissions:\n  contents: write\n\non:\n  release:\n    types: [published]\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      \n      - name: Configure project\n        run: cmake -E tar c cpp-lazy-src.zip --format=zip -- src/ include/ CMakeLists.txt LICENSE.md cmake/\n      \n      - name: Add package to release\n        uses: svenstaro/upload-release-action@v2\n        with:\n          repo_token: ${{ secrets.GITHUB_TOKEN }}\n          file: cpp-lazy-src.zip\n          tag: ${{ github.ref }}\n"
  },
  {
    "path": ".github/workflows/module-tests.yml",
    "content": "on:\n  workflow_call:\n\njobs:\n  module_tests:\n    runs-on: ubuntu-latest\n    \n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v1\n\n      - name: Install Ninja\n        uses: seanmiddleditch/gha-setup-ninja@master\n      \n      - name: Add LLVM apt repo and install Clang 18\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y wget lsb-release gnupg\n          wget https://apt.llvm.org/llvm.sh\n          chmod +x llvm.sh\n          sudo ./llvm.sh 18\n          clang++-18 --version\n\n      - name: Configure module tests\n        env:\n          CC: clang-18\n          CXX: clang++-18\n        run: cmake -S tests/module_tests -B tests/module_tests/build -G Ninja\n          -D CMAKE_CXX_STANDARD:STRING=20\n          -D CPP_LAZY_USE_MODULES=ON\n          -D CMAKE_CXX_COMPILER=clang++-18\n\n      - name: Build module tests\n        run: cmake --build tests/module_tests/build\n      \n      - name: Run module tests\n        working-directory: tests/module_tests/build\n        run: ./module_test\n  \n  module_tests_find_package:\n    runs-on: ubuntu-latest\n    \n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v1\n\n      - name: Install Ninja\n        uses: seanmiddleditch/gha-setup-ninja@master\n      \n      - name: Add LLVM apt repo and install Clang 18\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y wget lsb-release gnupg\n          wget https://apt.llvm.org/llvm.sh\n          chmod +x llvm.sh\n          sudo ./llvm.sh 18\n          clang++-18 --version\n      \n      - name: Configure cpp-lazy\n        env:\n          CC: clang-18\n          CXX: clang++-18\n        run: cmake -S . -B build -G Ninja\n            -D CMAKE_CXX_STANDARD:STRING=20\n            -D CMAKE_BUILD_TYPE:STRING=Release\n            -D CMAKE_INSTALL_PREFIX=tests/module_tests/build/install\n            -D CPP_LAZY_INSTALL:BOOL=ON\n            -D CPP_LAZY_USE_MODULES=ON\n\n      - name: Build & install cpp-lazy\n        env:\n          CC: clang-18\n          CXX: clang++-18\n        run: cmake --build build --target install\n\n      - name: Configure module tests (find_package)\n        env:\n          CC: clang-18\n          CXX: clang++-18\n        run: cmake -S tests/module_tests -B tests/module_tests/build -G Ninja\n          -D CMAKE_PREFIX_PATH=${{ github.workspace }}/tests/module_tests/build/install\n          -D CMAKE_CXX_STANDARD:STRING=20\n          -D CPP_LAZY_USE_MODULES_FIND_PACKAGE=ON\n\n      - name: Build module tests (find_package)\n        run: cmake --build tests/module_tests/build\n      \n      - name: Run module tests (find_package)\n        working-directory: tests/module_tests/build\n        run: ./module_test\n"
  },
  {
    "path": ".github/workflows/msvc.yml",
    "content": "env:\n  CTEST_OUTPUT_ON_FAILURE: 1\n\non:\n  workflow_call:\n\njobs:\n  msvc:\n    name: msvc-${{ matrix.os }} (C++${{ matrix.standard }})\n\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ windows-2022, windows-2025 ]\n        standard: [11, 14, 17, 20, 23]\n\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v1\n\n      - name: Configure (install)\n        run: cmake -S . -B build/install\n          -D CMAKE_BUILD_TYPE:STRING=Release\n          -D CPP_LAZY_INSTALL:BOOL=ON\n          -D CMAKE_INSTALL_PREFIX=build/prefix\n          -D CMAKE_CXX_STANDARD:STRING=${{ matrix.standard }}\n          -D CMAKE_CXX_COMPILER:STRING=cl.exe\n\n      - name: Build (install)\n        run: cmake --build build/install --config Release -j $env:NUMBER_OF_PROCESSORS\n\n      - name: Install\n        run: cmake --install build/install --config Release\n\n      - name: Configure tests\n        run: cmake -S tests -B build/tests\n          -D CPP_LAZY_DOCTEST_VERSION:STRING=${{ vars.DOCTEST_VERSION }}\n          -D TEST_INSTALLED_VERSION:BOOL=YES\n          -D CMAKE_INSTALL_PREFIX=build/prefix\n          -D CMAKE_CXX_STANDARD:STRING=${{ matrix.standard }}\n          -D CMAKE_CXX_COMPILER:STRING=cl.exe\n\n      - name: Build tests\n        run: cmake --build build/tests --config Release -j $env:NUMBER_OF_PROCESSORS\n\n      - name: Run tests\n        working-directory: build/tests\n        run: ctest -C Release --output-on-failure -VV -j $(nproc)\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\r\n## files generated by popular Visual Studio add-ons.\r\n##\r\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\r\n\r\n# User-specific files\r\n*.rsuser\r\n*.suo\r\n*.user\r\n*.userosscache\r\n*.sln.docstates\r\n\r\n# User-specific files (MonoDevelop/Xamarin Studio)\r\n*.userprefs\r\n\r\n# Mono auto generated files\r\nmono_crash.*\r\n\r\n# Build results\r\n*-build/\r\nbuild-*/\r\n[Dd]ebug/\r\n[Dd]ebugPublic/\r\n[Rr]elease/\r\n[Rr]eleases/\r\nx64/\r\nx86/\r\n[Ww][Ii][Nn]32/\r\n[Aa][Rr][Mm]/\r\n[Aa][Rr][Mm]64/\r\nbld/\r\n[Bb]in/\r\n[Oo]bj/\r\n[Ll]og/\r\n[Ll]ogs/\r\n\r\n[bB]uild-[Rr]elease/\r\n[bB]uild-[Dd]ebug/\r\n\r\n_CPack_Packages/\r\n*.exe\r\n\r\n# Visual Studio 2015/2017 cache/options directory\r\n.vs/\r\n\r\n.vscode/\r\n# Uncomment if you have tasks that create the project's static files in wwwroot\r\n#wwwroot/\r\n\r\n# Visual Studio 2017 auto generated files\r\nGenerated\\ Files/\r\n\r\n# MSTest test Results\r\n[Tt]est[Rr]esult*/\r\n[Bb]uild[Ll]og.*\r\n\r\n# NUnit\r\n*.VisualState.xml\r\nTestResult.xml\r\nnunit-*.xml\r\n\r\n# Build Results of an ATL Project\r\n[Dd]ebugPS/\r\n[Rr]eleasePS/\r\ndlldata.c\r\n\r\n# Benchmark Results\r\nBenchmarkDotNet.Artifacts/\r\n\r\n# .NET Core\r\nproject.lock.json\r\nproject.fragment.lock.json\r\nartifacts/\r\n\r\n# ASP.NET Scaffolding\r\nScaffoldingReadMe.txt\r\n\r\n# StyleCop\r\nStyleCopReport.xml\r\n\r\n# Files built by Visual Studio\r\n*_i.c\r\n*_p.c\r\n*_h.h\r\n*.ilk\r\n*.meta\r\n*.obj\r\n*.iobj\r\n*.pch\r\n*.pdb\r\n*.ipdb\r\n*.pgc\r\n*.pgd\r\n*.rsp\r\n*.sbr\r\n*.tlb\r\n*.tli\r\n*.tlh\r\n*.tmp\r\n*.tmp_proj\r\n*_wpftmp.csproj\r\n*.log\r\n*.vspscc\r\n*.vssscc\r\n.builds\r\n*.pidb\r\n*.svclog\r\n*.scc\r\n\r\n# Chutzpah Test files\r\n_Chutzpah*\r\n\r\n# Visual C++ cache files\r\nipch/\r\n*.aps\r\n*.ncb\r\n*.opendb\r\n*.opensdf\r\n*.sdf\r\n*.cachefile\r\n*.VC.db\r\n*.VC.VC.opendb\r\n\r\n# Visual Studio profiler\r\n*.psess\r\n*.vsp\r\n*.vspx\r\n*.sap\r\n\r\n# Visual Studio Trace Files\r\n*.e2e\r\n\r\n# TFS 2012 Local Workspace\r\n$tf/\r\n\r\n# Guidance Automation Toolkit\r\n*.gpState\r\n\r\n# ReSharper is a .NET coding add-in\r\n_ReSharper*/\r\n*.[Rr]e[Ss]harper\r\n*.DotSettings.user\r\n\r\n# TeamCity is a build add-in\r\n_TeamCity*\r\n\r\n# DotCover is a Code Coverage Tool\r\n*.dotCover\r\n\r\n# AxoCover is a Code Coverage Tool\r\n.axoCover/*\r\n!.axoCover/settings.json\r\n\r\n# Coverlet is a free, cross platform Code Coverage Tool\r\ncoverage*[.json, .xml, .info]\r\n\r\n# Visual Studio code coverage results\r\n*.coverage\r\n*.coveragexml\r\n\r\n# NCrunch\r\n_NCrunch_*\r\n.*crunch*.local.xml\r\nnCrunchTemp_*\r\n\r\n# MightyMoose\r\n*.mm.*\r\nAutoTest.Net/\r\n\r\n# Web workbench (sass)\r\n.sass-cache/\r\n\r\n# Installshield output folder\r\n[Ee]xpress/\r\n\r\n# DocProject is a documentation generator add-in\r\nDocProject/buildhelp/\r\nDocProject/Help/*.HxT\r\nDocProject/Help/*.HxC\r\nDocProject/Help/*.hhc\r\nDocProject/Help/*.hhk\r\nDocProject/Help/*.hhp\r\nDocProject/Help/Html2\r\nDocProject/Help/html\r\n\r\n# Click-Once directory\r\npublish/\r\n\r\n# Publish Web Output\r\n*.[Pp]ublish.xml\r\n*.azurePubxml\r\n# Note: Comment the next line if you want to checkin your web deploy settings,\r\n# but database connection strings (with potential passwords) will be unencrypted\r\n*.pubxml\r\n*.publishproj\r\n\r\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\r\n# checkin your Azure Web App publish settings, but sensitive information contained\r\n# in these scripts will be unencrypted\r\nPublishScripts/\r\n\r\n# NuGet Packages\r\n*.nupkg\r\n# NuGet Symbol Packages\r\n*.snupkg\r\n# The packages folder can be ignored because of Package Restore\r\n**/[Pp]ackages/*\r\n# takewhile build/, which is used as an MSBuild target.\r\n!**/[Pp]ackages/build/\r\n# Uncomment if necessary however generally it will be regenerated when needed\r\n#!**/[Pp]ackages/repositories.config\r\n# NuGet v3's project.json files produces more ignorable files\r\n*.nuget.props\r\n*.nuget.targets\r\n\r\n# Microsoft Azure Build Output\r\ncsx/\r\n*.build.csdef\r\n\r\n# Microsoft Azure Emulator\r\necf/\r\nrcf/\r\n\r\n# Windows Store app package directories and files\r\nAppPackages/\r\nBundleArtifacts/\r\nPackage.StoreAssociation.xml\r\n_pkginfo.txt\r\n*.appx\r\n*.appxbundle\r\n*.appxupload\r\n\r\n# Visual Studio cache files\r\n# files ending in .cache can be ignored\r\n*.[Cc]ache\r\n# but keep track of directories ending in .cache\r\n!?*.[Cc]ache/\r\n\r\n# Others\r\nClientBin/\r\n~$*\r\n*~\r\n*.dbmdl\r\n*.dbproj.schemaview\r\n*.jfm\r\n*.pfx\r\n*.publishsettings\r\norleans.codegen.cs\r\n\r\n# Including strong name files can present a security risk\r\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\r\n#*.snk\r\n\r\n# Since there are multiple workflows, uncomment next line to ignore bower_components\r\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\r\n#bower_components/\r\n\r\n# RIA/Silverlight projects\r\nGenerated_Code/\r\n\r\n# Backup & report files from converting an old project file\r\n# to a newer Visual Studio version. Backup files are not needed,\r\n# because we have git ;-)\r\n_UpgradeReport_Files/\r\nBackup*/\r\nUpgradeLog*.XML\r\nUpgradeLog*.htm\r\nServiceFabricBackup/\r\n*.rptproj.bak\r\n\r\n# SQL Server files\r\n*.mdf\r\n*.ldf\r\n*.ndf\r\n\r\n# Business Intelligence projects\r\n*.rdl.data\r\n*.bim.layout\r\n*.bim_*.settings\r\n*.rptproj.rsuser\r\n*- [Bb]ackup.rdl\r\n*- [Bb]ackup ([0-9]).rdl\r\n*- [Bb]ackup ([0-9][0-9]).rdl\r\n\r\n# Microsoft Fakes\r\nFakesAssemblies/\r\n\r\n# GhostDoc plugin setting file\r\n*.GhostDoc.xml\r\n\r\n# Node.js Tools for Visual Studio\r\n.ntvs_analysis.dat\r\nnode_modules/\r\n\r\n# Visual Studio 6 build log\r\n*.plg\r\n\r\n# Visual Studio 6 workspace options file\r\n*.opt\r\n\r\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\r\n*.vbw\r\n\r\n# Visual Studio LightSwitch build output\r\n**/*.HTMLClient/GeneratedArtifacts\r\n**/*.DesktopClient/GeneratedArtifacts\r\n**/*.DesktopClient/ModelManifest.xml\r\n**/*.Server/GeneratedArtifacts\r\n**/*.Server/ModelManifest.xml\r\n_Pvt_Extensions\r\n\r\n# Paket dependency manager\r\n.paket/paket.exe\r\npaket-files/\r\n\r\n# FAKE - F# Make\r\n.fake/\r\n\r\n# CodeRush personal settings\r\n.cr/personal\r\n\r\n# Python Tools for Visual Studio (PTVS)\r\n__pycache__/\r\n*.pyc\r\n\r\n# Cake - Uncomment if you are using it\r\n# tools/**\r\n# !tools/packages.config\r\n\r\n# Tabs Studio\r\n*.tss\r\n\r\n# Telerik's JustMock configuration file\r\n*.jmconfig\r\n\r\n# BizTalk build output\r\n*.btp.cs\r\n*.btm.cs\r\n*.odx.cs\r\n*.xsd.cs\r\n\r\n# OpenCover UI analysis results\r\nOpenCover/\r\n\r\n# Azure Stream Analytics local run output\r\nASALocalRun/\r\n\r\n# MSBuild Binary and Structured Log\r\n*.binlog\r\n\r\n# NVidia Nsight GPU debugger configuration file\r\n*.nvuser\r\n\r\n# MFractors (Xamarin productivity tool) working folder\r\n.mfractor/\r\n\r\n# Local History for Visual Studio\r\n.localhistory/\r\n\r\n# BeatPulse healthcheck filter database\r\nhealthchecksdb\r\n\r\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\r\nMigrationBackup/\r\n\r\n# Ionide (cross platform F# VS Code tools) working folder\r\n.ionide/\r\n\r\n# Fody - auto-generated XML schema\r\nFodyWeavers.xsd\r\n\r\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\r\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\r\n\r\n# User-specific stuff\r\n.idea/**/workspace.xml\r\n.idea/**/tasks.xml\r\n.idea/**/usage.statistics.xml\r\n.idea/**/dictionaries\r\n.idea/**/shelf\r\n\r\n# Generated files\r\n.idea/**/contentModel.xml\r\n\r\n# Sensitive or high-churn files\r\n.idea/**/dataSources/\r\n.idea/**/dataSources.ids\r\n.idea/**/dataSources.local.xml\r\n.idea/**/sqlDataSources.xml\r\n.idea/**/dynamic.xml\r\n.idea/**/uiDesigner.xml\r\n.idea/**/dbnavigator.xml\r\n\r\n# Gradle\r\n.idea/**/gradle.xml\r\n.idea/**/libraries\r\n\r\n# Gradle and Maven with auto-import\r\n# When using Gradle or Maven with auto-import, you should exclude module files,\r\n# since they will be recreated, and may cause churn.  Uncomment if using\r\n# auto-import.\r\n# .idea/artifacts\r\n# .idea/compiler.xml\r\n# .idea/jarRepositories.xml\r\n# .idea/modules.xml\r\n.idea/*.iml\r\n# .idea/modules\r\n*.iml\r\n# *.ipr\r\n\r\n# CMake\r\n**/cmake-build-*/\r\n\r\n# Mongo Explorer plugin\r\n.idea/**/mongoSettings.xml\r\n\r\n# File-based project format\r\n*.iws\r\n\r\n# IntelliJ\r\nout/\r\n\r\n# mpeltonen/sbt-idea plugin\r\n.idea_modules/\r\n\r\n# JIRA plugin\r\natlassian-ide-plugin.xml\r\n\r\n# Cursive Clojure plugin\r\n.idea/replstate.xml\r\n\r\n# Crashlytics plugin (for Android Studio and IntelliJ)\r\ncom_crashlytics_export_strings.xml\r\ncrashlytics.properties\r\ncrashlytics-build.properties\r\nfabric.properties\r\n\r\n# Editor-based Rest Client\r\n.idea/httpRequests\r\n\r\n# Android studio 3.1+ serialized cache file\r\n.idea/caches/build_file_checksums.ser\r\n\r\n# No docs, users must create it themselves\r\ndocs/*\r\n!*docs/.gitkeep\r\n\r\n# Remove extern bench\r\nextern/benchmark/\r\n\r\n# Created by .ignore support plugin (hsz.mobi)\r\n### Python template\r\n# Byte-compiled / optimized / DLL files\r\n__pycache__/\r\n*.py[cod]\r\n*$py.class\r\n\r\n# C extensions\r\n*.so\r\n\r\n# Distribution / packaging\r\n.Python\r\nenv/\r\nbuild/\r\ndevelop-eggs/\r\ndist/\r\ndownloads/\r\neggs/\r\n.eggs/\r\nlib/\r\nlib64/\r\nparts/\r\nsdist/\r\nvar/\r\n*.egg-info/\r\n.installed.cfg\r\n*.egg\r\n\r\n# PyInstaller\r\n#  Usually these files are written by a python script from a template\r\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\r\n*.manifest\r\n*.spec\r\n\r\n# Installer logs\r\npip-log.txt\r\npip-delete-this-directory.txt\r\n\r\n# Unit test / coverage reports\r\nhtmlcov/\r\n.tox/\r\n.coverage\r\n.coverage.*\r\n.cache\r\nnosetests.xml\r\ncoverage.xml\r\n*,cover\r\n.hypothesis/\r\n\r\n# Translations\r\n*.mo\r\n*.pot\r\n\r\n# Django stuff:\r\n*.log\r\nlocal_settings.py\r\n\r\n# Flask stuff:\r\ninstance/\r\n.webassets-cache\r\n\r\n# Scrapy stuff:\r\n.scrapy\r\n\r\n# Sphinx documentation\r\ndocs/_build/\r\n\r\n# PyBuilder\r\ntarget/\r\n\r\n# IPython Notebook\r\n.ipynb_checkpoints\r\n\r\n# pyenv\r\n.python-version\r\n\r\n# celery beat schedule file\r\ncelerybeat-schedule\r\n\r\n# dotenv\r\n.env\r\n\r\n# virtualenv\r\nvenv/*\r\nENV/*\r\n\r\n# Spyder project settings\r\n.spyderproject\r\n\r\n# Rope project settings\r\n.ropeproject\r\n### VirtualEnv template\r\n# Virtualenv\r\n# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/\r\n.venv\r\npip-selfcheck.json\r\n### JetBrains template\r\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm\r\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\r\n\r\n# User-specific stuff:\r\n.idea/workspace.xml\r\n.idea/tasks.xml\r\n.idea/dictionaries\r\n.idea/vcs.xml\r\n.idea/jsLibraryMappings.xml\r\n\r\n# Sensitive or high-churn files:\r\n.idea\r\n.idea/\r\n\r\n## File-based project format:\r\n*.iws\r\n\r\n## Plugin-specific files:\r\n\r\n# IntelliJ\r\n/out/\r\n\r\n# mpeltonen/sbt-idea plugin\r\n.idea_modules/\r\n\r\n# JIRA plugin\r\natlassian-ide-plugin.xml\r\n\r\n# Crashlytics plugin (for Android Studio and IntelliJ)\r\ncom_crashlytics_export_strings.xml\r\ncrashlytics.properties\r\ncrashlytics-build.properties\r\nfabric.properties\r\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\r\n\r\nproject(cpp-lazy\r\n        VERSION 9.0.0\r\n        DESCRIPTION\r\n        \"A fast C++11/14/17/20 header only library for lazy evaluation and function tools\"\r\n        HOMEPAGE_URL \"https://github.com/Kaaserne/cpp-lazy\"\r\n        LANGUAGES CXX)\r\n\r\n# ---- Warning guard ----\r\n# Protect dependents from this project's warnings if the guard isn't disabled\r\nset(cpp-lazy_warning_guard \"SYSTEM\")\r\nif(cpp-lazy_INCLUDE_WITHOUT_SYSTEM)\r\n    set(cpp-lazy_warning_guard \"\")\r\nendif()\r\n\r\ninclude(GNUInstallDirs)\r\nset(CPP_LAZY_INCLUDE_DIR \"${CMAKE_INSTALL_INCLUDEDIR}\" CACHE STRING \"Installation directory of the include files\")\r\n\r\noption(CPP_LAZY_USE_STANDALONE \"Standalone library without {fmt}\" NO)\r\noption(CPP_LAZY_USE_INSTALLED_FMT \"Import {fmt} using find_package\" NO)\r\noption(CPP_LAZY_USE_MODULES \"Enable C++20's modules\" NO)\r\n\r\nif (NOT DEFINED CPP_LAZY_DEBUG_ASSERTIONS)\r\n    if (CMAKE_BUILD_TYPE STREQUAL \"Debug\")\r\n        set(CPP_LAZY_DEBUG_ASSERTIONS ON)\r\n    else()\r\n        set(CPP_LAZY_DEBUG_ASSERTIONS OFF)\r\n    endif()\r\nendif()\r\noption(CPP_LAZY_DEBUG_ASSERTIONS \"Debug assertions\" ${CPP_LAZY_DEBUG_ASSERTIONS})\r\n\r\nif (CPP_LAZY_USE_MODULES)\r\n\tmessage(STATUS \"cpp-lazy: using C++20 modules\")\r\n\tset(CMAKE_DEBUG_POSTFIX \"d\")\r\n\tset(CPP_LAZY_SOURCE_FILES \"src/lz.cppm\")\r\n\tset(CPP_LAZY_LINK_VISIBILITY \"PUBLIC\")\r\n\tset(CPP_LAZY_COMPILE_FEATURES_VISIBLITY \"PUBLIC\")\r\n\tset(CPP_LAZY_COMPILE_DEFINITIONS_VISIBLITY \"PRIVATE\")\r\n\tset(CPP_LAZY_COMPILE_INCLUDE_VISIBLITY \"PUBLIC\")\r\n\tset(CMAKE_CXX_SCAN_FOR_MODULES ON)\r\nelse()\r\n\tset(CPP_LAZY_LIB_TYPE \"INTERFACE\")\r\n\tset(CPP_LAZY_LINK_VISIBILITY \"INTERFACE\")\r\n\tset(CPP_LAZY_COMPILE_FEATURES_VISIBLITY \"INTERFACE\")\r\n\tset(CPP_LAZY_COMPILE_DEFINITIONS_VISIBLITY \"INTERFACE\")\r\n\tset(CPP_LAZY_COMPILE_INCLUDE_VISIBLITY \"INTERFACE\")\r\nendif()\r\n\r\n# ---- Import {fmt} ----\r\nif (CPP_LAZY_USE_STANDALONE)\r\n\tmessage(STATUS \"cpp-lazy: using standalone version\")\r\nelse()\r\n\tif (CPP_LAZY_USE_INSTALLED_FMT)\r\n\t\tmessage(STATUS \"cpp-lazy: using external {fmt} library\")\r\n\t\tset(CPP_LAZY_FMT_DEP_VERSION \"\" CACHE STRING \"Version of {fmt} to use\")\r\n\t\tfind_package(fmt ${CPP_LAZY_FMT_DEP_VERSION} REQUIRED CONFIG)\r\n\telse()\r\n\t\tmessage(STATUS \"cpp-lazy: using bundled {fmt} library\")\r\n\t\tset(FMT_OS YES CACHE INTERNAL \"\")\r\n\t\tset(FMT_INSTALL YES CACHE INTERNAL \"\" FORCE)\r\n\t\tinclude(FetchContent)\r\n\t\tset(CPP_LAZY_FMT_DEP_VERSION 12.1.0 CACHE STRING \"Version of {fmt} to use\")\r\n\t\tFetchContent_Declare(fmt \r\n\t\t\tURL https://github.com/fmtlib/fmt/archive/refs/tags/${CPP_LAZY_FMT_DEP_VERSION}.tar.gz \r\n\t\t\tUPDATE_DISCONNECTED YES \r\n\t\t\tDOWNLOAD_EXTRACT_TIMESTAMP TRUE\r\n\t\t)\r\n\t\tFetchContent_MakeAvailable(fmt)\r\n\tendif()\r\nendif()\r\n\r\nmessage(STATUS \"cpp-lazy: standalone version: ${CPP_LAZY_USE_STANDALONE}.\")\r\nmessage(STATUS \"cpp-lazy: debug assertions: ${CPP_LAZY_DEBUG_ASSERTIONS}.\")\r\n\r\n# ---- Declare library ----\r\nadd_library(cpp-lazy ${CPP_LAZY_LIB_TYPE} \"${CPP_LAZY_SOURCE_FILES}\")\r\nadd_library(cpp-lazy::cpp-lazy ALIAS cpp-lazy)\r\n\r\nif (CPP_LAZY_USE_MODULES)\r\n\ttarget_sources(cpp-lazy PUBLIC\r\n\t\tFILE_SET cxx_modules\r\n\t\tTYPE\r\n\t\t\tCXX_MODULES\r\n\t\tFILES \"${CPP_LAZY_SOURCE_FILES}\")\r\n\ttarget_compile_features(cpp-lazy PRIVATE cxx_std_20)\r\nendif()\r\n\r\n# Link lib stacktrace if debug assertions are enabled, on Linux with C++23 or later\r\nset(IS_CXX_23 $<VERSION_GREATER_EQUAL:${CMAKE_CXX_STANDARD},23>)\r\nset(IS_GNU_LIKE_COMPILER $<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>)\r\nset(LZ_LINK_LIBBACKTRACE_CONDITION\r\n    $<AND:$<BOOL:${CPP_LAZY_DEBUG_ASSERTIONS}>,${IS_GNU_LIKE_COMPILER},${IS_CXX_23}>\r\n)\r\ntarget_link_libraries(cpp-lazy ${CPP_LAZY_LINK_VISIBILITY}\r\n\t$<$<NOT:$<BOOL:${CPP_LAZY_USE_STANDALONE}>>:fmt::fmt>\r\n\t$<${LZ_LINK_LIBBACKTRACE_CONDITION}:stdc++_libbacktrace>\r\n)\r\ntarget_compile_definitions(cpp-lazy ${CPP_LAZY_COMPILE_DEFINITIONS_VISIBLITY}\r\n\t$<$<BOOL:${CPP_LAZY_USE_STANDALONE}>:LZ_STANDALONE>\r\n\t$<$<BOOL:${CPP_LAZY_DEBUG_ASSERTIONS}>:LZ_DEBUG_ASSERTIONS>\r\n)\r\n\r\ntarget_include_directories(cpp-lazy\t${cpp-lazy_warning_guard}\r\n\t${CPP_LAZY_COMPILE_INCLUDE_VISIBLITY}\r\n        \"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>\"\r\n\t\t\"$<INSTALL_INTERFACE:${CPP_LAZY_INCLUDE_DIR}>\"\r\n)\r\n\r\n# ---- Install ----\r\ninclude(CMakePackageConfigHelpers)\r\n\r\nset(cpp-lazy_install_cmakedir \"${CMAKE_INSTALL_LIBDIR}/cmake/cpp-lazy\")\r\n\r\noption(CPP_LAZY_INSTALL \"Install cpp-lazy\" OFF)\r\n\r\nif (CPP_LAZY_USE_MODULES AND CPP_LAZY_INSTALL)\r\n\tinstall(\r\n\t\tTARGETS cpp-lazy\r\n        EXPORT cpp-lazyTargets\r\n        INCLUDES DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\r\n        FILE_SET cxx_modules\r\n\t\tDESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\r\n\t)\r\nelseif (CPP_LAZY_INSTALL)\r\n\tinstall(\r\n\t\tTARGETS cpp-lazy\r\n\t\tEXPORT cpp-lazyTargets\r\n\t\tINCLUDES DESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}\"\r\n\t)\r\nendif()\r\n\r\nif (CPP_LAZY_INSTALL)\r\n\t# ---- pkg-config (.pc) ----\r\n\tset(PC_REQUIRES \"\")\r\n\tif (NOT CPP_LAZY_USE_STANDALONE)\r\n\t\tset(PC_REQUIRES \"fmt\")\r\n\tendif()\r\n\r\n\tset(PC_LIBS \"\")\r\n\tset(PC_LIBS_PRIVATE \"\")\r\n\tif (CPP_LAZY_USE_MODULES)\r\n\t\tset(PC_LIBS \"-lcpp-lazy\")\r\n\tendif()\r\n\tif (CPP_LAZY_DEBUG_ASSERTIONS AND IS_GNU_LIKE_COMPILER)\r\n\t\tset(PC_LIBS_PRIVATE \"${PC_LIBS_PRIVATE} -lstdc++_libbacktrace\")\r\n\tendif()\r\n\r\n\tconfigure_file(\r\n\t\t\"${PROJECT_SOURCE_DIR}/cmake/cpp-lazy.pc.in\"\r\n\t\t\"${PROJECT_BINARY_DIR}/cpp-lazy.pc\"\r\n\t\t@ONLY\r\n\t)\r\n\r\n\tinstall(\r\n\t\tFILES \"${PROJECT_BINARY_DIR}/cpp-lazy.pc\"\r\n\t\tDESTINATION \"${CMAKE_INSTALL_LIBDIR}/pkgconfig\"\r\n\t)\r\n\r\n\t# ---- CMake config files ----\r\n\twrite_basic_package_version_file(\r\n\t\tcpp-lazyConfigVersion.cmake\r\n\t\tVERSION ${PROJECT_VERSION}\r\n\t\tCOMPATIBILITY SameMajorVersion\r\n\t\tARCH_INDEPENDENT\r\n\t)\r\n\tconfigure_package_config_file(\r\n\t\t\"${PROJECT_SOURCE_DIR}/cmake/cpp-lazyConfig.cmake.in\"\r\n\t\t\"${PROJECT_BINARY_DIR}/cpp-lazyConfig.cmake\"\r\n\t\tINSTALL_DESTINATION \"${cpp-lazy_install_cmakedir}\"\r\n\t)\r\n\r\n\tinstall(EXPORT cpp-lazyTargets\r\n\t\t\tNAMESPACE cpp-lazy::\r\n\t\t\tDESTINATION \"${cpp-lazy_install_cmakedir}\"\r\n\t)\r\n\tinstall(FILES\r\n\t\t\t\"${PROJECT_BINARY_DIR}/cpp-lazyConfig.cmake\"\r\n\t\t\t\"${PROJECT_BINARY_DIR}/cpp-lazyConfigVersion.cmake\"\r\n\t\t\tDESTINATION \"${cpp-lazy_install_cmakedir}\"\r\n\t)\r\n\t\r\n\tinstall(DIRECTORY \"${PROJECT_SOURCE_DIR}/include/\"\r\n\t\t\tDESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}\"\r\n\t)\r\n\tinclude(CPack)\r\n\tset(CPACK_PACKAGE_INSTALL_DIRECTORY \"${CPACK_PACKAGE_NAME}/${CPACK_PACKAGE_VERSION}\")\r\n\tset(CPACK_RESOURCE_FILE_LICENSE \"${PROJECT_SOURCE_DIR}/LICENSE.md\")\r\nendif()\r\n"
  },
  {
    "path": "Doxyfile",
    "content": "# Doxyfile 1.8.18\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#---------------------------------------------------------------------------\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           = cpp-lazy\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         = 9.0.0\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          = \"C++11/14/17/20 lazy evaluation library\"\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           =\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/cpp-lazy\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# 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.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\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, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all generated output in the proper direction.\n# Possible values are: None, LTR, RTL and Context.\n# The default value is: None.\n\nOUTPUT_TEXT_DIRECTION  = None\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# 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\"\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:\". You can put \\n's in the value part of an alias to insert\n# newlines (in the resulting output). You can put ^^ in the value part of an\n# alias to insert a newline as if a physical newline was in the original file.\n# When you need a literal { or } or , in the value part of an alias you have to\n# escape them by means of a backslash (\\), this can lead to conflicts with the\n# commands \\{ and \\} for these it is advised to use the version @{ and @} or use\n# 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++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,\n# 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.\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# 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#---------------------------------------------------------------------------\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            = NO\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 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# has no effect 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# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\n# names in lower-case letters. If set to YES, upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# (including Cygwin) ands Mac users are advised to set this option to NO.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = NO\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_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.\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 not documenting some parameters\n# in a documented function, or documenting parameters that don't exist or using\n# markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = 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 or incomplete\n# parameter documentation, but not about the absence of documentation. If\n# EXTRACT_ALL is set to YES then this flag will automatically be disabled.\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered.\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# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\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).\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                  = C:\\Users\\marcd\\CLionProjects\\cpp-lazy\\include\\Lz\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: https://www.gnu.org/software/libiconv/) for the list of\n# possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\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# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\n# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,\n# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),\n# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen\n# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,\n# *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\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                         *.cs \\\n                         *.d \\\n                         *.php \\\n                         *.php4 \\\n                         *.php5 \\\n                         *.phtml \\\n                         *.inc \\\n                         *.m \\\n                         *.markdown \\\n                         *.md \\\n                         *.mm \\\n                         *.dox \\\n                         *.doc \\\n                         *.txt \\\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              = NO\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# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */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 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#---------------------------------------------------------------------------\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         = NO\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: http://clang.llvm.org/) for more accurate parsing at the\n# cost of reduced performance. This can be particularly helpful with template\n# rich C++ code for which doxygen's built-in parser lacks the necessary type\n# 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 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 compilation database (see:\n# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files\n# were built. This is equivalent to specifying the \"-p\" option to a clang tool,\n# such as clang-check. These options will then be passed to the parser.\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 COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\n# which the alphabetical index list will be split.\n# Minimum value: 1, maximum value: 20, default value: 5.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\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). For an example see the 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_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 colorwheel, 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 grayscales 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_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to YES can help to show when doxygen was last run and thus if the\n# documentation is up to date.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = NO\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# 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: https://developer.apple.com/xcode/), introduced with OSX\n# 10.5 (Leopard). To create a documentation set, doxygen will generate a\n# Makefile in the HTML output directory. Running make will produce the docset in\n# that directory and 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 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# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on\n# Windows.\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 master .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# 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: 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: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-\n# 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: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\n# 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: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\n# 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 of Qt's\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\n# 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. As an example, the default style\n# sheet generated by doxygen has an example that shows how to put an image at\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\n# the same information as the tab index, you could consider setting\n# 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# 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 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 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# Use the FORMULA_TRANSPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\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# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. See the MathJax site (see:\n# http://docs.mathjax.org/en/latest/output.html) for more details.\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility), NativeMML (i.e. MathML) 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.\n# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = https://cdn.jsdelivr.net/npm/mathjax@2\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# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\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: http://docs.mathjax.org/en/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: 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: https://xapian.org/). See the section \"External Indexing and\n# Searching\" for 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         = YES\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 personal LaTeX header for the\n# generated LaTeX document. The header should contain everything until the first\n# chapter. If it is left blank doxygen will generate a standard header. See\n# section \"Doxygen usage\" for information on how to let doxygen write the\n# default header to a separate file.\n#\n# Note: Only use a user-defined header if you know what you are doing! The\n# following commands have a special meaning inside the header: $title,\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\n# string, for the replacement values of the other commands the user is referred\n# to HTML_HEADER.\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 personal LaTeX footer for the\n# generated LaTeX document. The footer should contain everything after the last\n# 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.\n#\n# Note: Only use a user-defined footer if you know what you are 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 pdflatex to generate\n# the PDF file directly from the LaTeX files. Set this option to YES, to get a\n# higher quality PDF documentation.\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# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help. This option is also used\n# when generating formulas in HTML.\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# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\n# code with syntax highlighting in the LaTeX output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_SOURCE_CODE      = 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# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_TIMESTAMP        = NO\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# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\n# with syntax highlighting in the RTF output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_SOURCE_CODE        = NO\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           = NO\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# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\n# program listings (including syntax highlighting and cross-referencing\n# information) to the DOCBOOK output. Note that enabling this will significantly\n# increase the size of the DOCBOOK output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_PROGRAMLISTING = NO\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 http://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 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.\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 class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# 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 modules 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 the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\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# 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# http://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# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\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 then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to 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.\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.\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 HAVE_DOT is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\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.\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.\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.\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 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# http://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# 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\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. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# 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_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\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# 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 dot\n# files that are used to generate the various graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2025 Marc Dirven\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": "README.md",
    "content": "[![Build status](https://github.com/Kaaserne/cpp-lazy/workflows/Continuous%20Integration/badge.svg)](https://github.com/Kaaserne/cpp-lazy/actions)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n[![CodeQL](https://github.com/Kaaserne/cpp-lazy/actions/workflows/codeql.yml/badge.svg)](https://github.com/Kaaserne/cpp-lazy/actions/workflows/codeql.yml)\r\n\r\n![](https://i.ibb.co/ccn2V8N/Screenshot-2021-05-05-Make-A-High-Quality-Logo-In-Just-5-Minutes-For-Under-30-v1-cropped.png)\r\n\r\nExamples can be found [here](https://github.com/Kaaserne/cpp-lazy/tree/master/examples). Installation can be found [here](https://github.com/Kaaserne/cpp-lazy#installation).\r\n\r\n# cpp-lazy\r\n`cpp-lazy` is an easy and fast lazy evaluation library for C++11/14/17/20. The library tries to reduce redundant data usage for begin/end iterator pairs. For instance: `lz::random_iterable::end()` will return a `lz::default_sentinel_t` to prevent duplicate data that is also present in `lz::random_iterable::begin()`. If a 'symmetrical' end-begin iterator pair is needed, one can use `lz::common` or `lz::common_random`. Generally, `lz` *forward* iterators will return a `lz::default_sentinel_t` (or if the input iterable is sentinelled) because forward iterators can only go forward, so there is no need to store the end iterator, is the philosophy. Lz random access iterators can also return a `default_sentinel` if the internal data of `begin` can already decide whether end is reached, such as `lz::repeat`.\r\n\r\nThe library uses one optional dependency: the library `{fmt}`, more of which can be found out in the [installation section](https://github.com/Kaaserne/cpp-lazy#Installation). This dependency is only used for printing and formatting.\r\n\r\n# Features\r\n- C++11/14/17/20 compatible\r\n- C++20's module compatible\r\n- `pkgconfig` compatible\r\n- Easy printing/formatting using `lz::format`, `fmt::print` or `std::cout`\r\n- One optional dependency ([`{fmt}`](https://github.com/fmtlib/fmt)), can be turned off by using option `CPP_LAZY_USE_STANDALONE=TRUE`/`set(CPP_LAZY_USE_STANDALONE TRUE)` in CMake\r\n- STL compatible (if the input iterable is not sentinelled, otherwise use `lz::*` equivalents)\r\n- Little overhead, as little data usage as possible\r\n- Any compiler with at least C++11 support should be suitable\r\n- [Easy installation](https://github.com/Kaaserne/cpp-lazy#installation)\r\n- [Clear Examples](https://github.com/Kaaserne/cpp-lazy/tree/master/examples)\r\n- Piping/chaining using `|` operator\r\n- [Tested with very strict GCC/Clang/MSVC flags](https://github.com/Kaaserne/cpp-lazy/blob/11a1fd957cba49c57b9bbca7e69f53bd7210e323/tests/CMakeLists.txt#L87)\r\n- Bidirectional sentinelled iterables can be reversed using `lz::common`\r\n\r\n# What is lazy?\r\nLazy evaluation is an evaluation strategy which holds the evaluation of an expression until its value is needed. In this library, this is the case for all iterables/iteartors. It holds all the elements that are needed for the operation:\r\n```cpp\r\nstd::vector<int> vec = {1, 2, 3, 4, 5};\r\n\r\n// No evaluation is done here, function is stored and a reference to vec\r\nauto mapped = lz::map(vec, [](int i) { return i * 2; });\r\nfor (auto i : mapped) { // Evaluation is done here\r\n  std::cout << i << \" \"; // prints \"2 4 6 8 10 \"\r\n}\r\n```\r\n\r\n\r\n# Basic usage\r\n```cpp\r\n#include <Lz/map.hpp>\r\n#include <vector>\r\n\r\nint main() {\r\n  std::array<int, 4> arr = {1, 2, 3, 4};\r\n  auto result = lz::map(arr, [](int i) { return i + 1; }) \r\n                       | lz::to<std::vector>(); // == {2, 3, 4, 5}\r\n  // or\r\n  auto result = arr | lz::map([](int i) { return i + 1; })\r\n                    | lz::to<std::vector>(); // == {2, 3, 4, 5}\r\n\r\n  // Some iterables will return sentinels, for instance:\r\n  // (specific rules about when sentinels are returned can be found in the documentation):\r\n  std::vector<int> vec = {1, 2, 3, 4};\r\n  auto forward = lz::c_string(\"Hello World\"); // .end() returns default_sentinel_t\r\n\r\n  // inf_loop = {1, 2, 3, 4, 1, 2, 3, 4, ...}\r\n  auto inf_loop = lz::loop(vec); // .end() returns default_sentinel_t\r\n\r\n  // random = {random number between 0 and 32, total of 4 numbers}\r\n  auto random = lz::random(0, 32, 4); // .end() returns default_sentinel_t\r\n}\r\n```\r\n\r\n## Philosophy behind cpp-lazy\r\nCpp-lazy is implemented in such a way, that it tries to reduce redundant data usage as much as possible. For instance, if an iterable can determine whether the end is reached by only using the begin iterator ánd is less than a bidirectional iterator, it will return a sentinel instead of storing the end iterator as well. Or, if the iterable is already sentinelled, it will return the same sentinel type as the input iterable.\r\n\r\nThis is the case for `lz::take(std::forward_list<>{})` for instance, but not for `lz::take(std::list<>{})` or `lz::take(std::vector<>{})`. This is done to reduce memory usage. If you need a symmetrical `begin`/`end` iterator pair, you can use `lz::common`. Use `lz::common` as late as possible in your chain, because every `lz` iterable has the potential to return sentinels instead of storing the `end` iterator. \r\n\r\n```cpp\r\na | lz::map(...) | lz::filter(...) | lz::common; // OK\r\na | lz::common | lz::map(...) | lz::filter(...); // Not recommended, map or filter could have returned sentinels\r\n```\r\n\r\n`lz::common` tries to make the `begin`/`end` iterator pair symmetrical. Only as last resort it will use `common_iterator<I, S>` which is basically a fancy `[std::]variant` internally. It depends on the following conditions whether `lz::common` will use `common_iterator`:\r\n- If `begin` and `end` are already the same type, the same instance will be returned;\r\n- If the iterable passed is random access, `begin + (size_t)distance(begin, end)` will be used to create the `end` iterator;\r\n- If `begin` is assignable from `end`, assign `end` to `begin` to create the `end` iterator. It will keep its `.size()` if applicable;\r\n- Otherwise, `common_iterator<I, S>` will be used. As will be the case for all `std::` sentinelled iterables, but not for `lz` sentinelled iterables.\r\n\r\n## Ownership\r\n`lz` iterables will hold a reference to the input iterable if the input iterable is *not* inherited from `lz::lazy_view`. This means that the `lz` iterables will hold a reference to (but not excluded to) containers such as `std::vector`, `std::array` and `std::string`, as they do not inherit from `lz::lazy_view`. This is done by the class `lz::maybe_owned`. This can be altered using `lz::copied` or `lz::as_copied`. This will copy the input iterable instead of holding a reference to it. This is useful for cheap to copy iterables that are not inherited from `lz::lazy_view` (for example `boost::iterator_range`).\r\n\r\n```cpp\r\nstruct non_lz_iterable {\r\n  int* _begin{};\r\n  int* _end{};\r\n\r\n  non_lz_iterable(int* begin, int* end) : _begin{ begin }, _end{ end } {\r\n  }\r\n\r\n  int* begin() {\r\n    return _begin;\r\n  }\r\n  int* end() {\r\n    return _end;\r\n  }\r\n};\r\n\r\nint main() {\r\n  std::vector<int> vec = {1, 2, 3, 4};\r\n  // mapped will hold a reference to vec\r\n  auto mapped = lz::map(vec, [](int i) { return i + 1; });\r\n  // filtered does NOT hold a reference to mapped, but mapped still holds a reference to vec\r\n  auto filtered = lz::filter(mapped, [](int i) { return i % 2 == 0; });\r\n\r\n  auto random = lz::random(0, 32, 4);\r\n  // str will *not* hold a reference to random, because random is a lazy iterable and is trivial to copy\r\n  auto str = lz::map(random, [](int i) { return std::to_string(i); });\r\n\r\n  lz::maybe_owned<std::vector<int>> ref{ vec }; // Holds a reference to vec\r\n  lz::maybe_owned<std::vector<int>>::holds_reference; // true\r\n\r\n  using random_iterable = decltype(random);\r\n  lz::maybe_owned<random_iterable> ref2{ random }; // Does NOT hold a reference to random\r\n  lz::maybe_owned<random_iterable>::holds_reference; // false\r\n\r\n  non_lz_iterable non_lz(vec.data(), vec.data() + vec.size());\r\n  lz::maybe_owned<non_lz_iterable> ref{ non_lz }; // Holds a reference of non_lz! Watch out for this!\r\n  lz::maybe_owned<non_lz_iterable>::holds_reference; // true\r\n\r\n  // Instead, if you don't want this behaviour, you can use `lz::copied`:\r\n  lz::copied<non_lz_iterable> copied{ non_lz }; // Holds a copy of non_lz = cheap to copy\r\n  lz::copied<non_lz_iterable>::holds_reference; // false\r\n  // Or use the helper function:\r\n  copied = lz::as_copied(non_lz); // Holds a copy of non_lz = cheap to copy\r\n}\r\n```\r\n\r\n## Iterating\r\nIterating over iterables with sentinels using range-based for loops is possible. However, a workaround for C++ versions < 17 is needed.\r\n\r\n```cpp\r\n#include <Lz/c_string.hpp>\r\n#include <Lz/algorithm/algorithm.hpp>\r\n\r\nint main() {\r\n  auto iterable_with_sentinel = lz::c_string(\"Hello World\");\r\n  // Possible in C++17 and higher\r\n  for (auto i : iterable_with_sentinel) {\r\n    std::cout << i; // prints \"Hello World\"\r\n  }\r\n\r\n  // Possible in C++11 - 14\r\n  lz::for_each(iterable_with_sentinel, [](char i) { std::cout << i; }); // prints \"Hello World\"\r\n}\r\n```\r\n\r\n## Formatting\r\nFormatting is done using `{fmt}` or `<format>`. If neither is available, it will use `std::cout`/`std::ostringstream`:\r\n\r\n```cpp\r\n#include <Lz/stream.hpp>\r\n#include <Lz/filter.hpp>\r\n#include <vector>\r\n\r\nint main() {\r\n  std::vector<int> vec = {1, 2, 3, 4};\r\n  auto filtered = vec | lz::filter([](int i) { return i % 2 == 0; }); // == {2, 4}\r\n\r\n  // To a stream\r\n  std::cout << filtered; // prints \"2 4\" (only works for lz iterables)\r\n  lz::format(filtered, std::cout, \", \", \"{:02d}\"); // prints \"02, 04\" (only with {fmt} installed or C++20's <format>)\r\n  lz::format(filtered, std::cout, \", \"); // prints \"2, 4\"\r\n  fmt::print(\"{}\", fmt::join(filtered, \", \")); // prints \"2, 4\" (only with {fmt} installed)\r\n\r\n  filtered | lz::format(std::cout, \", \"); // prints \"2, 4\"\r\n  filtered | lz::format; // prints \"2, 4\"\r\n  filtered | lz::format(std::cout, \", \", \"{:02d}\"); // prints \"02, 04\" (only with {fmt} installed or C++20's <format>)\r\n}\r\n```\r\n\r\n# Installation\r\n## Options\r\nThe following CMake options are available, all of which are optional:\r\n- `CPP_LAZY_USE_STANDALONE`: Use the standalone version of cpp-lazy. This will not use the library `{fmt}`. Default is `FALSE`\r\n- `CPP_LAZY_LZ_USE_MODULES`: Use C++20 modules. Default is `FALSE`\r\n- `CPP_LAZY_DEBUG_ASSERTIONS`: Enable debug assertions. Default is `TRUE` for debug mode, `FALSE` for release.\r\n- `CPP_LAZY_USE_INSTALLED_FMT`: Use the system installed version of `{fmt}`. This will not use the bundled version. Default is `FALSE` and uses `find_package`. Use `CPP_LAZY_FMT_DEP_VERSION` to specify the version of `{fmt}` to use if needed.\r\n- `CPP_LAZY_INSTALL`: Install cpp-lazy targets and config files. Default is `FALSE`.\r\n- `CPP_LAZY_FMT_DEP_VERSION`: version of `{fmt}` to use. Used if `CPP_LAZY_USE_INSTALLED_FMT` is `TRUE` or `CPP_LAZY_USE_STANDALONE` is `FALSE`. May be empty.\r\n\r\n### Using `FetchContent`\r\nThe following way is recommended (cpp-lazy version >= 5.0.1). Note that you choose the cpp-lazy-src.zip, and not the source-code.zip/source-code.tar.gz. This prevents you from downloading stuff that you don't need, and thus preventing pollution of the cmake build directory:\r\n```cmake\r\n\r\n# Uncomment this line to use the cpp-lazy standalone version or use -D CPP_LAZY_USE_STANDALONE=TRUE\r\n# set(CPP_LAZY_USE_STANDALONE TRUE)\r\n\r\ninclude(FetchContent)\r\nFetchContent_Declare(cpp-lazy\r\n        URL https://github.com/Kaaserne/cpp-lazy/releases/download/<TAG_HERE E.G. v9.0.0>/cpp-lazy-src.zip\r\n        # Below is optional\r\n        # URL_MD5 <MD5 HASH OF cpp-lazy-src.zip>\r\n        # If using CMake >= 3.24, preferably set <bool> to TRUE\r\n        # DOWNLOAD_EXTRACT_TIMESTAMP <bool>\r\n)\r\n# Optional settings:\r\n# set(CPP_LAZY_USE_STANDALONE NO CACHE BOOL \"\") # Use {fmt}\r\n# set(CPP_LAZY_USE_INSTALLED_FMT NO CACHE BOOL \"\") # Use bundled {fmt}, NO means use bundled, YES means use system installed {fmt}\r\n# set(CPP_LAZY_USE_MODULES NO CACHE BOOL \"\") # Do not use C++20 modules\r\n# set(CPP_LAZY_DEBUG_ASSERTIONS NO CACHE BOOL \"\") # Disable debug assertions in release mode\r\n# set(CPP_LAZY_FMT_DEP_VERSION \"\" CACHE STRING \"\") # Specify version of {fmt} to use if needed\r\nFetchContent_MakeAvailable(cpp-lazy)\r\n\r\nadd_executable(${PROJECT_NAME} main.cpp)\r\ntarget_link_libraries(${PROJECT_NAME} cpp-lazy::cpp-lazy)\r\n```\r\n\r\nAn alternative ('less' recommended), add to your `CMakeLists.txt` the following:\r\n```cmake\r\ninclude(FetchContent)\r\nFetchContent_Declare(cpp-lazy\r\n        GIT_REPOSITORY https://github.com/Kaaserne/cpp-lazy\r\n        GIT_TAG ... # Commit hash\r\n        # If using CMake >= 3.24, preferably set <bool> to TRUE\r\n        # DOWNLOAD_EXTRACT_TIMESTAMP <bool>\r\n)\r\n# Optional settings:\r\n# set(CPP_LAZY_USE_STANDALONE NO CACHE BOOL \"\") # Use {fmt}\r\n# set(CPP_LAZY_USE_INSTALLED_FMT NO CACHE BOOL \"\") # Use bundled {fmt}, NO means use bundled, YES means use system installed {fmt}\r\n# set(CPP_LAZY_USE_MODULES NO CACHE BOOL \"\") # Do not use C++20 modules\r\n# set(CPP_LAZY_DEBUG_ASSERTIONS NO CACHE BOOL \"\") # Disable debug assertions in release mode\r\n# set(CPP_LAZY_FMT_DEP_VERSION \"\" CACHE STRING \"\") # Specify version of {fmt} to use. Only relevant if CPP_LAZY_USE_INSTALLED_FMT is YES\r\nFetchContent_MakeAvailable(cpp-lazy)\r\n\r\nadd_executable(${PROJECT_NAME} main.cpp)\r\ntarget_link_libraries(${PROJECT_NAME} cpp-lazy::cpp-lazy)\r\n```\r\n\r\n## Using find_package\nFirst, install the library. Then:\r\n```cmake\r\nfind_package(cpp-lazy 9.0.0 REQUIRED CONFIG)\r\n```\r\n\r\n## Without CMake\r\n### Without `{fmt}`\r\n- Download the source code from the releases page\r\n- Specify the include directory to `cpp-lazy/include`.\r\n- Include files as follows:\r\n\r\n```cpp\r\n// Important, preprocessor macro 'LZ_STANDALONE' has to be defined already\r\n// or\r\n// #define LZ_STANDALONE\r\n#include <Lz/map.hpp>\r\n#include <vector>\r\n\r\nint main() {\r\n  std::array<int, 4> arr = {1, 2, 3, 4};\r\n  auto result = lz::map(arr, [](int i) { return i + 1; }) | lz::to<std::vector>(); // == {2, 3, 4, 5}\r\n  // or\r\n  auto result = lz::to<std::vector>(arr | lz::map([](int i) { return i + 1; })); // == {2, 3, 4, 5}\r\n}\r\n```\r\n\r\n### With `{fmt}`\r\n- Clone the repository\r\n- Specify the include directory to `cpp-lazy/include` and `fmt/include`.\r\n- Define `FMT_HEADER_ONLY` before including any `lz` files.\r\n- Include files as follows:\r\n\r\n```cpp\r\n#define FMT_HEADER_ONLY\r\n\r\n#include <Lz/map.hpp>\r\n#include <vector>\r\n\r\nint main() {\r\n  std::array<int, 4> arr = {1, 2, 3, 4};\r\n  auto result = lz::map(arr, [](int i) { return i + 1; }) | lz::to<std::vector>(); // == {2, 3, 4, 5}\r\n  // or\r\n  auto result = lz::to<std::vector>(arr | lz::map([](int i) { return i + 1; })); // == {2, 3, 4, 5}\r\n}\r\n```\r\n\r\n### Using `git clone`\r\nClone the repository using `git clone https://github.com/Kaaserne/cpp-lazy/` and add to `CMakeLists.txt` the following:\r\n```cmake\r\nadd_subdirectory(cpp-lazy)\r\nadd_executable(${PROJECT_NAME} main.cpp)\r\n\r\ntarget_link_libraries(${PROJECT_NAME} cpp-lazy::cpp-lazy)\r\n```\r\n\r\n# Benchmarks\r\nThe time is equal to one iteration. One iteration excludes the creation of the iterable and includes one iteration of that iterable. Compiled with: gcc version 13.3.0.\r\n\r\n<div style=\"text-align:center\"><img src=\"https://github.com/Kaaserne/cpp-lazy/blob/master/bench/benchmarks-iterators-23.png?raw=true\" />\r\n</div>\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "all/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.12)\n\nproject(build_all LANGUAGES CXX)\n\nadd_subdirectory(\"${CMAKE_CURRENT_LIST_DIR}/../bench\" \"${CMAKE_BINARY_DIR}/bench\")\nadd_subdirectory(\"${CMAKE_CURRENT_LIST_DIR}/../tests\" \"${CMAKE_BINARY_DIR}/tests\")\nadd_subdirectory(\"${CMAKE_CURRENT_LIST_DIR}/../examples\" \"${CMAKE_BINARY_DIR}/examples\")\n"
  },
  {
    "path": "bench/.gitignore",
    "content": "venv/\nbuild*/"
  },
  {
    "path": "bench/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10...3.12.4)\r\n\r\nproject(Benchmark)\r\n\r\nadd_executable(Benchmark\r\n        ${CMAKE_CURRENT_SOURCE_DIR}/benchmarks-iterators.cpp)\r\n\r\n# Add cpp-lazy\r\ninclude(FetchContent)\r\nFetchContent_Declare(cpp-lazy SOURCE_DIR \"${CMAKE_CURRENT_LIST_DIR}/..\")\r\nFetchContent_MakeAvailable(cpp-lazy)\r\n\r\n# Enable Google Benchmark to add dependencies for their tests\r\nset(BENCHMARK_DOWNLOAD_DEPENDENCIES TRUE)\r\nFetchContent_Declare(benchmark\r\n    URL https://github.com/google/benchmark/archive/refs/tags/v1.9.1.tar.gz\r\n    UPDATE_DISCONNECTED YES\r\n    DOWNLOAD_EXTRACT_TIMESTAMP TRUE\r\n)\r\nFetchContent_MakeAvailable(benchmark)\r\n\r\ntarget_link_libraries(Benchmark\r\n    PRIVATE\r\n        cpp-lazy::cpp-lazy\r\n        benchmark::benchmark\r\n)\r\n\r\nmath(EXPR CXX_STANDARD_INT \"${CMAKE_CXX_STANDARD}\")\r\ntarget_compile_definitions(Benchmark PRIVATE CMAKE_CXX_STANDARD=${CXX_STANDARD_INT})\r\n\r\n# Run as: ./Benchmark --benchmark_out=benchmark-iterators.json --benchmark_out_format=json\r\n"
  },
  {
    "path": "bench/bar_chart_maker.py",
    "content": "import matplotlib.pyplot as plt\r\nfrom matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas\r\nfrom matplotlib.figure import Figure\r\nimport numpy as np\r\nimport shutil\r\nimport time\r\nimport os\r\nimport sys\r\nimport json\r\nfrom collections import namedtuple\r\nimport asyncio\r\nfrom typing import Iterable\r\n\r\n\r\nos.system(\"\")\r\nplt.rcdefaults()\r\nfilename = 'benchmarks-iterators-{}.png'\r\n\r\nclass BenchmarkResult:\r\n    def __init__(self, real_time: str, time_unit: str):\r\n        self.library_name = ''\r\n        self.real_time = real_time\r\n        self.time_unit = time_unit\r\n    \r\n    def __repr__(self):\r\n        return f'BenchmarkResult(name={self.library_name}, real_time={self.real_time}, time_unit={self.time_unit})'\r\n\r\n\r\nclass Table:\r\n    def __init__(self):\r\n        self._rows: dict[str, list[BenchmarkResult]] = {}\r\n        self._implementation_names: list[str] = []\r\n\r\n    def update_row(self, name: str, result: BenchmarkResult):\r\n        last_underscore = name.rfind('_')\r\n        prefix, postfix = name[:last_underscore], name[last_underscore + 1:]\r\n        result.library_name = postfix\r\n        if not prefix in self._rows:\r\n            self._rows[prefix] = []\r\n        self._rows[prefix].append(result)\r\n    \r\n    @property\r\n    def implementation_names(self):\r\n        return self._implementation_names\r\n    \r\n    @property\r\n    def rows(self) -> Iterable[tuple[str, list[BenchmarkResult]]]:\r\n        return self._rows.items()\r\n    \r\n    def create(self):\r\n        largest_key = max(enumerate(self._rows.values()), key=lambda x: len(x[1]))\r\n        self._implementation_names = list(map(lambda x: x.library_name, largest_key[1]))\r\n\r\n        for benchmark_results in self._rows.values():\r\n            for implementation in self._implementation_names:\r\n                if implementation in map(lambda br: br.library_name, benchmark_results):\r\n                    continue\r\n                benchmark_result = BenchmarkResult('-', '-')\r\n                benchmark_result.library_name = implementation\r\n                benchmark_results.append(benchmark_result)\r\n\r\n    def __repr__(self):\r\n        return f'rows={self.rows}'\r\n\r\n\r\ndef get_benchmarks(bench_dict: dict) -> Table:\r\n    table = Table()\r\n\r\n    for benchmark in bench_dict['benchmarks']:\r\n        name = benchmark['name']\r\n        table.update_row(name, BenchmarkResult(benchmark['real_time'], benchmark['time_unit']))\r\n\r\n    table.create()\r\n    return table\r\n\r\n\r\ndef make_table(benchmark_records: Table, cxx_version: str, n_iterations: int):\r\n    fig_border = 'steelblue'\r\n    \r\n    data = []\r\n    row_headers = []\r\n    for it_name, impls in benchmark_records.rows:\r\n        data.append(list(map(lambda r: f'{round(r.real_time / n_iterations, 4)} {r.time_unit} / {round(r.real_time, 4)} {r.time_unit}'\r\n                             if isinstance(r.real_time, float) else r.real_time, impls)))\r\n        row_headers.append(it_name)\r\n\r\n    fig = Figure(linewidth=2, edgecolor=fig_border)\r\n    canvas = FigureCanvas(fig)\r\n    ax = fig.add_subplot(111)\r\n    ax.axis('off')\r\n\r\n    fig = Figure(linewidth=2, edgecolor=fig_border)\r\n    canvas = FigureCanvas(fig)\r\n    ax = fig.add_subplot(111)\r\n    ax.axis('off')\r\n\r\n    row_colors = plt.get_cmap('BuPu')(np.full(len(row_headers), 0.1))\r\n    col_colors = plt.get_cmap('BuPu')(np.full(len(data), 0.1))\r\n\r\n    the_table = ax.table(cellText=data,\r\n                        rowLabels=row_headers,\r\n                        rowColours=row_colors,\r\n                        colColours=col_colors,\r\n                        cellLoc='center',\r\n                        colLabels=list(map(lambda n: f'{n} (1 iteration / {n_iterations} iterations)', benchmark_records.implementation_names)),\r\n                        loc='center')\r\n    the_table.scale(1.5, 1.5)\r\n\r\n    fig.text(.90, 0.05, '', horizontalalignment='center', size=8, weight='light')\r\n\r\n    canvas.print_figure(f'benchmarks-iterators-{cxx_version}.png', bbox_inches='tight', dpi=150)\r\n\r\ndef make_chart(cxx_version: str, path: str):\r\n    print(f'Creating chart for \"CXX{cxx_version}\" from \"{path}/benchmark-iterators.json\"')\r\n\r\n    assert os.path.exists(path), f'Path \"{path}\" does not exist'\r\n\r\n    with open(os.path.join(path, 'benchmark-iterators.json'), 'r') as benchmarks_file:\r\n        bench_json = json.load(benchmarks_file)\r\n        bench_records = get_benchmarks(bench_json)\r\n\r\n        iterations = 32\r\n        make_table(bench_records, cxx_version, iterations)\r\n\r\n    green = '\\033[32m'\r\n    print(f'{green}Successfully created {filename.format(cxx_version)}')\r\n\r\n\r\nasync def main():\r\n    cxx_version = None\r\n    try:\r\n        cxx_version = sys.argv[1]\r\n    except IndexError:\r\n        print('No CXX version provided, using CMakeCache.txt.')\r\n        with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'build', 'CMakeCache.txt'), 'r') as cmake_cache:\r\n            for line in cmake_cache:\r\n                if not line.startswith('CMAKE_CXX_STANDARD:'):\r\n                    continue\r\n                cxx_version = line.split('=')[1].strip()\r\n                break\r\n        if cxx_version is None:\r\n            print('No CXX version provided and CMakeCache.txt does not contain CMAKE_CXX_STANDARD.')\r\n            sys.exit(1)\r\n        print('Found CXX version in CMakeCache.txt:', cxx_version)\r\n\r\n    make_chart(cxx_version, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'build'))\r\n    \r\n\r\n\r\nif __name__ == '__main__':\r\n    asyncio.run(main())\r\n"
  },
  {
    "path": "bench/benchmarks-iterators.cpp",
    "content": "#include <Lz/any_iterable.hpp>\r\n#include <Lz/as_iterator.hpp>\r\n#include <Lz/c_string.hpp>\r\n#include <Lz/cached_size.hpp>\r\n#include <Lz/cartesian_product.hpp>\r\n#include <Lz/chunk_if.hpp>\r\n#include <Lz/chunks.hpp>\r\n#include <Lz/common.hpp>\r\n#include <Lz/concatenate.hpp>\r\n#include <Lz/detail/iterator.hpp>\r\n#include <Lz/drop.hpp>\r\n#include <Lz/drop_while.hpp>\r\n#include <Lz/duplicates.hpp>\r\n#include <Lz/enumerate.hpp>\r\n#include <Lz/except.hpp>\r\n#include <Lz/exclude.hpp>\r\n#include <Lz/exclusive_scan.hpp>\r\n#include <Lz/filter.hpp>\r\n#include <Lz/flatten.hpp>\r\n#include <Lz/generate.hpp>\r\n#include <Lz/generate_while.hpp>\r\n#include <Lz/group_by.hpp>\r\n#include <Lz/inclusive_scan.hpp>\r\n#include <Lz/interleave.hpp>\r\n#include <Lz/intersection.hpp>\r\n#include <Lz/iter_tools.hpp>\r\n#include <Lz/join_where.hpp>\r\n#include <Lz/loop.hpp>\r\n#include <Lz/map.hpp>\r\n#include <Lz/pairwise.hpp>\r\n#include <Lz/random.hpp>\r\n#include <Lz/range.hpp>\r\n#include <Lz/regex_split.hpp>\r\n#include <Lz/repeat.hpp>\r\n#include <Lz/reverse.hpp>\r\n#include <Lz/rotate.hpp>\r\n#include <Lz/slice.hpp>\r\n#include <Lz/split.hpp>\r\n#include <Lz/stream.hpp>\r\n#include <Lz/take.hpp>\r\n#include <Lz/take_every.hpp>\r\n#include <Lz/take_while.hpp>\r\n#include <Lz/unique.hpp>\r\n#include <Lz/util/optional.hpp>\r\n#include <Lz/util/string_view.hpp>\r\n#include <Lz/zip.hpp>\r\n#include <Lz/zip_longest.hpp>\r\n#include <array>\r\n#include <benchmark/benchmark.h>\r\n\r\n#if LZ_HAS_INCLUDE(<ranges>)\r\n\r\n#include <ranges>\r\n\r\n#endif\r\n\r\nnamespace {\r\nconstexpr std::size_t size_policy = 32;\r\n\r\nvoid any_iterable_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int i : lz::make_any_iterable(arr)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n    }\r\n}\r\n\r\nvoid c_string_lz(benchmark::State& state) {\r\n    constexpr const char* str = \"this is a 32 char long stringggg\";\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (char c : lz::c_string(str)) {\r\n            benchmark::DoNotOptimize(c);\r\n        }\r\n#else\r\n        auto c_str = lz::c_string(str);\r\n        lz::for_each(c_str, [](char c) { benchmark::DoNotOptimize(c); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid cached_reverse_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int c : lz::cached_reverse(arr)) {\r\n            benchmark::DoNotOptimize(c);\r\n        }\r\n    }\r\n}\r\n\r\nvoid cartesian_product_lz(benchmark::State& state) {\r\n    std::array<int, size_policy / 8> a;\r\n    std::array<char, size_policy / 4> b;\r\n\r\n    for (auto _ : state) {\r\n        for (auto&& tup : lz::cartesian_product(a, b)) {\r\n            benchmark::DoNotOptimize(tup);\r\n        }\r\n    }\r\n}\r\n\r\nvoid chunk_if_lz(benchmark::State& state) {\r\n    auto a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    constexpr static auto half = static_cast<int>(size_policy / 2);\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (auto&& x : lz::chunk_if(a, [](int i) noexcept { return i == half; })) {\r\n            for (int y : x) {\r\n                benchmark::DoNotOptimize(y);\r\n            }\r\n        }\r\n#else\r\n        auto chunk_if = lz::chunk_if(a, [](int i) noexcept { return i == half; });\r\n        using ref = lz::detail::ref_iterable_t<decltype(chunk_if)>;\r\n        lz::for_each(chunk_if, [](ref chunk) {\r\n            for (int x : chunk) {\r\n                benchmark::DoNotOptimize(x);\r\n            }\r\n        });\r\n#endif\r\n    }\r\n}\r\n\r\n#if defined(__cpp_lib_ranges_chunk_by) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nvoid chunk_if_std(benchmark::State& state) {\r\n    auto a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    constexpr static auto half = static_cast<int>(size_policy / 2);\r\n\r\n    auto eq = [](int, int cur) {\r\n        return half != cur;\r\n    };\r\n\r\n    for (auto _ : state) {\r\n        for (auto&& x : std::ranges::views::chunk_by(a, eq)) {\r\n            for (int y : x) {\r\n                benchmark::DoNotOptimize(y);\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n#endif\r\n\r\nvoid chunks_lz(benchmark::State& state) {\r\n    auto a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    for (auto _ : state) {\r\n        for (auto&& chunk : lz::chunks(a, 8)) {\r\n            for (int x : chunk) {\r\n                benchmark::DoNotOptimize(x);\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n#if defined(__cpp_lib_ranges_chunk) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nvoid chunks_std(benchmark::State& state) {\r\n    auto a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    for (auto _ : state) {\r\n        for (auto&& chunk : std::ranges::views::chunk(a, 8)) {\r\n            for (int x : chunk) {\r\n                benchmark::DoNotOptimize(x);\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n#endif\r\n\r\nvoid common_lz(benchmark::State& state) {\r\n    auto a = lz::generate([]() noexcept { return 0; }, size_policy);\r\n\r\n    for (auto _ : state) {\r\n        for (int i : lz::common(a)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n    }\r\n}\r\n\r\nvoid concatenate_lz(benchmark::State& state) {\r\n    std::string a(size_policy / 2, '0');\r\n    std::string b(size_policy / 2, '1');\r\n\r\n    for (auto _ : state) {\r\n        for (char c : lz::concat(a, b)) {\r\n            benchmark::DoNotOptimize(c);\r\n        }\r\n    }\r\n}\r\n\r\nvoid duplicates_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (std::pair<char, std::size_t> c : lz::duplicates(arr)) {\r\n            benchmark::DoNotOptimize(c);\r\n        }\r\n    }\r\n}\r\n\r\nvoid enumerate_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto pair : lz::enumerate(arr)) {\r\n            benchmark::DoNotOptimize(pair);\r\n        }\r\n    }\r\n}\r\n\r\nvoid except_lz(benchmark::State& state) {\r\n    auto large_arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto large_arr_except = lz::range(static_cast<int>(size_policy) / 2) | lz::to<std::array<int, size_policy / 2>>();\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (auto excepted : lz::except(large_arr, large_arr_except)) {\r\n            benchmark::DoNotOptimize(excepted);\r\n        }\r\n#else\r\n        auto except = lz::except(large_arr, large_arr_except);\r\n        lz::for_each(except, [](int i) { benchmark::DoNotOptimize(i); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid exclude_lz(benchmark::State& state) {\r\n    auto a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (int i : lz::exclude(a, 5, 10)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n#else\r\n        auto exclude = lz::exclude(a, 5, 10);\r\n        lz::for_each(exclude, [](int i) { benchmark::DoNotOptimize(i); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid exclusive_scan_lz(benchmark::State& state) {\r\n    auto array = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (int i : lz::exclusive_scan(array, 0)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n#else\r\n        auto scan = lz::exclusive_scan(array, 0);\r\n        lz::for_each(scan, [](int i) { benchmark::DoNotOptimize(i); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid filter_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int filtered : lz::filter(arr, [](const int i) noexcept { return i == 0; })) {\r\n            benchmark::DoNotOptimize(filtered);\r\n        }\r\n    }\r\n}\r\n\r\nvoid flatten_lz(benchmark::State& state) {\r\n    std::array<std::array<int, size_policy / 4>, size_policy / 8> arr =\r\n        lz::generate([]() { return lz::range(static_cast<int>(size_policy / 4)) | lz::to<std::array<int, size_policy / 4>>(); },\r\n                     size_policy / 8) |\r\n        lz::to<std::array<std::array<int, size_policy / 4>, size_policy / 8>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int val : lz::flatten(arr)) {\r\n            benchmark::DoNotOptimize(val);\r\n        }\r\n    }\r\n}\r\n\r\nvoid generate_while_lz(benchmark::State& state) {\r\n    for (auto _ : state) {\r\n        std::size_t cnt = 0;\r\n#ifdef LZ_HAS_CXX_17\r\n        for (auto i : lz::generate_while([&cnt]() -> std::pair<bool, std::size_t> {\r\n                 const auto old_value = cnt++;\r\n                 return { cnt, old_value < size_policy };\r\n             })) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n#else\r\n        auto gen = lz::generate_while([&cnt]() -> std::pair<bool, std::size_t> {\r\n            const auto old_value = cnt++;\r\n            return { cnt, old_value < size_policy };\r\n        });\r\n        lz::for_each(gen, [](std::size_t i) { benchmark::DoNotOptimize(i); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid generate_lz(benchmark::State& state) {\r\n    size_t cnt = 0;\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (auto i : lz::generate([&cnt]() noexcept { return cnt++; }, size_policy)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n#else\r\n        auto gen = lz::generate([&cnt]() noexcept { return cnt++; }, size_policy);\r\n        lz::for_each(gen, [](int i) { benchmark::DoNotOptimize(i); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid groupy_by_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (auto&& group : lz::group_by(arr, [](int a, int b) noexcept { return a == b; })) {\r\n            benchmark::DoNotOptimize(group.first);\r\n            for (auto&& pair : group.second) {\r\n                benchmark::DoNotOptimize(pair);\r\n            }\r\n        }\r\n#else\r\n        auto group_by = lz::group_by(arr, [](int a, int b) noexcept { return a == b; });\r\n        using ref = lz::detail::ref_iterable_t<decltype(group_by)>;\r\n        lz::for_each(group_by, [](ref group) {\r\n            benchmark::DoNotOptimize(group.first);\r\n            lz::for_each(group.second, [](int i) { benchmark::DoNotOptimize(i); });\r\n        });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid inclusive_scan_lz(benchmark::State& state) {\r\n    auto array = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (auto i : lz::exclusive_scan(array)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n#else\r\n        auto scan = lz::inclusive_scan(array);\r\n        lz::for_each(scan, [](int i) { benchmark::DoNotOptimize(i); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid interleave_lz(benchmark::State& state) {\r\n    std::array<int, size_policy / 2> arr_a =\r\n        lz::range(static_cast<int>(size_policy) / 2) | lz::to<std::array<int, size_policy / 2>>();\r\n    std::array<int, size_policy / 2> arr_b =\r\n        lz::range(static_cast<int>(size_policy) / 2) | lz::to<std::array<int, size_policy / 2>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto i : lz::interleave(arr_a, arr_b)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n    }\r\n}\r\n\r\nvoid intersection_lz(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n\r\n        for (auto val : lz::intersection(arr_a, arr_b)) {\r\n            benchmark::DoNotOptimize(val);\r\n        }\r\n\r\n#else\r\n        auto intersection = lz::intersection(arr_a, arr_b);\r\n        using ref = lz::detail::ref_iterable_t<decltype(intersection)>;\r\n        lz::for_each(intersection, [](ref val) { benchmark::DoNotOptimize(val); });\r\n\r\n#endif\r\n    }\r\n}\r\n\r\nvoid join_where_lz(benchmark::State& state) {\r\n    std::vector<int> arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };\r\n    std::vector<int> to_join = { 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 };\r\n\r\n    auto random_index = lz::random(static_cast<std::size_t>(0), to_join.size() - 1, size_policy / 2);\r\n    to_join[*random_index.begin()] = arr[*random_index.begin()]; // Create a value where both values are equal\r\n    std::sort(to_join.begin(), to_join.end());\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n\r\n        for (std::tuple<int, int> val : lz::join_where(\r\n                 arr, to_join, [](int i) noexcept { return i; }, [](int i) noexcept { return i; },\r\n                 [](int a, int b) noexcept { return std::make_tuple(a, b); })) {\r\n            benchmark::DoNotOptimize(val);\r\n        }\r\n\r\n#else\r\n        auto join_where = lz::join_where(\r\n            arr, to_join, [](int i) noexcept { return i; }, [](int i) noexcept { return i; },\r\n            [](int a, int b) noexcept { return std::make_tuple(a, b); });\r\n        lz::for_each(join_where, [](std::tuple<int, int> val) { benchmark::DoNotOptimize(val); });\r\n\r\n#endif\r\n    }\r\n}\r\n\r\nvoid loop_lz(benchmark::State& state) {\r\n    std::array<int, size_policy / 2> arr =\r\n        lz::range(static_cast<int>(size_policy) / 2) | lz::to<std::array<int, size_policy / 2>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int i : lz::loop(arr, 2)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n    }\r\n}\r\n\r\nvoid map_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int mapped : lz::map(arr, [](const int i) noexcept { return i == 0 ? 10 : 5; })) {\r\n            benchmark::DoNotOptimize(mapped);\r\n        }\r\n    }\r\n}\r\n\r\nvoid pairwise_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto pair : lz::pairwise(arr, 3)) {\r\n            benchmark::DoNotOptimize(pair);\r\n        }\r\n    }\r\n}\r\n\r\nvoid random_lz(benchmark::State& state) {\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (int i : lz::random(0u, 32u, size_policy)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n#else\r\n        auto random = lz::random(0u, 32u, size_policy);\r\n        lz::for_each(random, [](int i) { benchmark::DoNotOptimize(i); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid common_random_lz(benchmark::State& state) {\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (int i : lz::common_random(0u, 32u, size_policy)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n#else\r\n        auto random = lz::common_random(0u, 32u, size_policy);\r\n        lz::for_each(random, [](int i) { benchmark::DoNotOptimize(i); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid range_lz(benchmark::State& state) {\r\n    for (auto _ : state) {\r\n        for (int i : lz::range(static_cast<int>(size_policy))) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n    }\r\n}\r\n\r\nvoid std_regex_split_lz(benchmark::State& state) {\r\n    std::string to_split = \"hello hello hello hello hello he\";\r\n    std::regex r(\" \");\r\n\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (lz::string_view substring : lz::regex_split(to_split, r)) {\r\n            benchmark::DoNotOptimize(substring);\r\n        }\r\n#else\r\n        auto split = lz::regex_split(to_split, r);\r\n        lz::for_each(split, [](lz::string_view substring) { benchmark::DoNotOptimize(substring); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid repeat_lz(benchmark::State& state) {\r\n    for (auto _ : state) {\r\n#ifdef LZ_HAS_CXX_17\r\n        for (int r : lz::repeat(0, size_policy)) {\r\n            benchmark::DoNotOptimize(r);\r\n        }\r\n#else\r\n        auto repeat = lz::repeat(0, size_policy);\r\n        lz::for_each(repeat, [](int i) { benchmark::DoNotOptimize(i); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid rotate_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int i : lz::rotate(arr, 5)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n    }\r\n}\r\n\r\nvoid slice_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int i : lz::slice(arr, 0, size_policy)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n    }\r\n}\r\n\r\nvoid split_multiple_lz(benchmark::State& state) {\r\n    std::string to_split = \"hello hello hello hello hello he\";\r\n\r\n    for (auto _ : state) {\r\n        using ref = lz::detail::ref_iterable_t<decltype(lz::split(to_split, \"o \"))>;\r\n#ifdef LZ_HAS_CXX_17\r\n        for (ref substring : lz::split(to_split, \"o \")) {\r\n            benchmark::DoNotOptimize(substring);\r\n        }\r\n#else\r\n        auto split = lz::split(to_split, \"o \");\r\n        lz::for_each(split, [](ref substring) { benchmark::DoNotOptimize(substring); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid split_single_lz(benchmark::State& state) {\r\n    std::string to_split = \"hello hello hello hello hello he\";\r\n\r\n    for (auto _ : state) {\r\n        using ref = lz::detail::ref_iterable_t<decltype(lz::split(to_split, ' '))>;\r\n#ifdef LZ_HAS_CXX_17\r\n        for (ref substring : lz::split(to_split, ' ')) {\r\n            benchmark::DoNotOptimize(substring);\r\n        }\r\n#else\r\n        auto split = lz::split(to_split, ' ');\r\n        lz::for_each(split, [](ref substring) { benchmark::DoNotOptimize(substring); });\r\n#endif\r\n    }\r\n}\r\n\r\nvoid take_every_lz(benchmark::State& state) {\r\n    constexpr size_t offset = 2;\r\n    std::array<int, size_policy * offset> array;\r\n\r\n    for (auto _ : state) {\r\n        for (int taken : lz::take_every(array, offset)) {\r\n            benchmark::DoNotOptimize(taken);\r\n        }\r\n    }\r\n}\r\n\r\n#if defined(__cpp_lib_ranges_stride) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nvoid take_every_std(benchmark::State& state) {\r\n    constexpr size_t offset = 2;\r\n    std::array<int, size_policy * offset> array;\r\n\r\n    for (auto _ : state) {\r\n        for (int taken : std::views::stride(array, offset)) {\r\n            benchmark::DoNotOptimize(taken);\r\n        }\r\n    }\r\n}\r\n\r\n#endif\r\n\r\nvoid take_while_lz(benchmark::State& state) {\r\n    auto array = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int taken : lz::take_while(array, [](const int i) noexcept { return i != size_policy - 1; })) {\r\n            benchmark::DoNotOptimize(taken);\r\n        }\r\n    }\r\n}\r\n\r\nvoid take_iterable_lz(benchmark::State& state) {\r\n    auto array = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int taken : lz::take(array, size_policy)) {\r\n            benchmark::DoNotOptimize(taken);\r\n        }\r\n    }\r\n}\r\n\r\nvoid take_iterator_lz(benchmark::State& state) {\r\n    auto array = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int taken : lz::take(array.begin(), size_policy)) {\r\n            benchmark::DoNotOptimize(taken);\r\n        }\r\n    }\r\n}\r\n\r\nvoid unique_lz(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (char c : lz::unique(arr)) {\r\n            benchmark::DoNotOptimize(c);\r\n        }\r\n    }\r\n}\r\n\r\nvoid zip4_lz(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_c = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_d = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto tuple : lz::zip(arr_a, arr_b, arr_c, arr_d)) {\r\n            benchmark::DoNotOptimize(tuple);\r\n        }\r\n    }\r\n}\r\n\r\nvoid zip3_lz(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_c = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto tuple : lz::zip(arr_a, arr_b, arr_c)) {\r\n            benchmark::DoNotOptimize(tuple);\r\n        }\r\n    }\r\n}\r\n\r\nvoid zip2_lz(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto tuple : lz::zip(arr_a, arr_b)) {\r\n            benchmark::DoNotOptimize(tuple);\r\n        }\r\n    }\r\n}\r\n\r\nvoid zip_longest4_lz(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy) - 1) | lz::to<std::array<int, size_policy - 1>>();\r\n    auto arr_c = lz::range(static_cast<int>(size_policy) - 2) | lz::to<std::array<int, size_policy - 2>>();\r\n    auto arr_d = lz::range(static_cast<int>(size_policy) - 3) | lz::to<std::array<int, size_policy - 3>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto tuple : lz::zip_longest(arr_a, arr_b, arr_c, arr_d)) {\r\n            benchmark::DoNotOptimize(tuple);\r\n        }\r\n    }\r\n}\r\n\r\nvoid zip_longest3_lz(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy) - 1) | lz::to<std::array<int, size_policy - 1>>();\r\n    auto arr_c = lz::range(static_cast<int>(size_policy) - 2) | lz::to<std::array<int, size_policy - 2>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto tuple : lz::zip_longest(arr_a, arr_b, arr_c)) {\r\n            benchmark::DoNotOptimize(tuple);\r\n        }\r\n    }\r\n}\r\n\r\nvoid zip_longest2_lz(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy) - 1) | lz::to<std::array<int, size_policy - 1>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto tuple : lz::zip_longest(arr_a, arr_b)) {\r\n            benchmark::DoNotOptimize(tuple);\r\n        }\r\n    }\r\n}\r\n\r\n#if LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 20\r\n\r\nvoid common_std(benchmark::State& state) {\r\n    auto a = lz::generate([]() noexcept { return 0; }, size_policy);\r\n\r\n    for (auto _ : state) {\r\n        for (int i : std::views::common(a)) {\r\n            benchmark::DoNotOptimize(i);\r\n        }\r\n    }\r\n}\r\n\r\nvoid filter_std(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int filtered : std::views::filter(arr, [](const int i) noexcept { return i == 0; })) {\r\n            benchmark::DoNotOptimize(filtered);\r\n        }\r\n    }\r\n}\r\n\r\nvoid flatten_std(benchmark::State& state) {\r\n    std::array<std::array<int, size_policy / 4>, size_policy / 8> arr =\r\n        lz::generate([]() { return lz::range(static_cast<int>(size_policy / 4)) | lz::to<std::array<int, size_policy / 4>>(); },\r\n                     size_policy / 8) |\r\n        lz::to<std::array<std::array<int, size_policy / 4>, size_policy / 8>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int val : std::views::join(arr)) {\r\n            benchmark::DoNotOptimize(val);\r\n        }\r\n    }\r\n}\r\n\r\nvoid map_std(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int mapped : std::views::transform(arr, [](const int i) noexcept { return i == 0 ? 10 : 5; })) {\r\n            benchmark::DoNotOptimize(mapped);\r\n        }\r\n    }\r\n}\r\n\r\nvoid split_single_std(benchmark::State& state) {\r\n    std::string to_split = \"hello hello hello hello hello he\";\r\n\r\n    for (auto _ : state) {\r\n        for (auto substring : std::views::lazy_split(to_split, ' ')) {\r\n            benchmark::DoNotOptimize(substring);\r\n        }\r\n    }\r\n}\r\n\r\nvoid split_multiple_std(benchmark::State& state) {\r\n    std::string to_split = \"hello hello hello hello hello he\";\r\n\r\n    for (auto _ : state) {\r\n        for (auto substring : std::views::lazy_split(to_split, \"o \")) {\r\n            benchmark::DoNotOptimize(substring);\r\n        }\r\n    }\r\n}\r\n\r\n#if defined(__cpp_lib_ranges_cartesian_product) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 20\r\n\r\nvoid cartesian_product_std(benchmark::State& state) {\r\n    std::array<int, size_policy / 8> a;\r\n    std::array<char, size_policy / 4> b;\r\n\r\n    for (auto _ : state) {\r\n        for (auto&& tup : std::views::cartesian_product(a, b)) {\r\n            benchmark::DoNotOptimize(tup);\r\n        }\r\n    }\r\n}\r\n\r\n#endif\r\n\r\n#if defined(__cpp_lib_ranges_slide) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 20\r\n\r\nvoid pairwise_std(benchmark::State& state) {\r\n    auto arr = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto pair : std::views::slide(arr, 3)) {\r\n            benchmark::DoNotOptimize(pair);\r\n        }\r\n    }\r\n}\r\n\r\n#endif\r\n\r\n#if defined(__cpp_lib_ranges_repeat) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 20\r\n\r\nvoid repeat_std(benchmark::State& state) {\r\n    for (auto _ : state) {\r\n        for (int r : std::views::repeat(0, size_policy)) {\r\n            benchmark::DoNotOptimize(r);\r\n        }\r\n    }\r\n}\r\n\r\n#endif\r\n\r\nvoid take_iterable_std(benchmark::State& state) {\r\n    auto array = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int taken : std::views::take(array, size_policy)) {\r\n            benchmark::DoNotOptimize(taken);\r\n        }\r\n    }\r\n}\r\n\r\nvoid take_iterator_std(benchmark::State& state) {\r\n    auto array = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (int taken : std::views::counted(array.begin(), size_policy)) {\r\n            benchmark::DoNotOptimize(taken);\r\n        }\r\n    }\r\n}\r\n\r\n#if defined(__cpp_lib_ranges_zip) && CMAKE_CXX_STANDARD >= 20\r\n\r\nvoid zip4_std(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_c = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_d = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto tuple : std::views::zip(arr_a, arr_b, arr_c, arr_d)) {\r\n            benchmark::DoNotOptimize(tuple);\r\n        }\r\n    }\r\n}\r\n\r\nvoid zip3_std(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_c = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto tuple : std::views::zip(arr_a, arr_b, arr_c)) {\r\n            benchmark::DoNotOptimize(tuple);\r\n        }\r\n    }\r\n}\r\n\r\nvoid zip2_std(benchmark::State& state) {\r\n    auto arr_a = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n    auto arr_b = lz::range(static_cast<int>(size_policy)) | lz::to<std::array<int, size_policy>>();\r\n\r\n    for (auto _ : state) {\r\n        for (auto tuple : std::views::zip(arr_a, arr_b)) {\r\n            benchmark::DoNotOptimize(tuple);\r\n        }\r\n    }\r\n}\r\n\r\n#endif\r\n\r\n#endif\r\n\r\n} // namespace\r\n\r\nBENCHMARK(any_iterable_lz);\r\nBENCHMARK(c_string_lz);\r\nBENCHMARK(cached_reverse_lz);\r\nBENCHMARK(cartesian_product_lz);\r\nBENCHMARK(chunk_if_lz);\r\nBENCHMARK(chunks_lz);\r\nBENCHMARK(common_lz);\r\nBENCHMARK(concatenate_lz);\r\nBENCHMARK(duplicates_lz);\r\nBENCHMARK(enumerate_lz);\r\nBENCHMARK(except_lz);\r\nBENCHMARK(exclude_lz);\r\nBENCHMARK(exclusive_scan_lz);\r\nBENCHMARK(filter_lz);\r\nBENCHMARK(flatten_lz);\r\nBENCHMARK(generate_while_lz);\r\nBENCHMARK(generate_lz);\r\nBENCHMARK(groupy_by_lz);\r\nBENCHMARK(inclusive_scan_lz);\r\nBENCHMARK(interleave_lz);\r\nBENCHMARK(intersection_lz);\r\nBENCHMARK(join_where_lz);\r\nBENCHMARK(loop_lz);\r\nBENCHMARK(map_lz);\r\nBENCHMARK(pairwise_lz);\r\nBENCHMARK(random_lz);\r\nBENCHMARK(common_random_lz);\r\nBENCHMARK(range_lz);\r\nBENCHMARK(std_regex_split_lz);\r\nBENCHMARK(repeat_lz);\r\nBENCHMARK(rotate_lz);\r\nBENCHMARK(slice_lz);\r\nBENCHMARK(split_multiple_lz);\r\nBENCHMARK(split_single_lz);\r\nBENCHMARK(take_every_lz);\r\nBENCHMARK(take_while_lz);\r\nBENCHMARK(take_iterable_lz);\r\nBENCHMARK(take_iterator_lz);\r\nBENCHMARK(unique_lz);\r\nBENCHMARK(zip_longest4_lz);\r\nBENCHMARK(zip_longest3_lz);\r\nBENCHMARK(zip_longest2_lz);\r\nBENCHMARK(zip4_lz);\r\nBENCHMARK(zip3_lz);\r\nBENCHMARK(zip2_lz);\r\n\r\n#if defined(__cpp_lib_ranges_cartesian_product) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nBENCHMARK(cartesian_product_std);\r\n\r\n#endif\r\n\r\n#if defined(__cpp_lib_ranges_chunk) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nBENCHMARK(chunks_std);\r\n\r\n#endif\r\n\r\n#if defined(__cpp_lib_ranges_chunk_by) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nBENCHMARK(chunk_if_std);\r\n\r\n#endif\r\n\r\n#if LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 20\r\n\r\nBENCHMARK(common_std);\r\nBENCHMARK(filter_std);\r\nBENCHMARK(flatten_std);\r\nBENCHMARK(take_iterable_std);\r\nBENCHMARK(take_iterator_std);\r\nBENCHMARK(map_std);\r\nBENCHMARK(split_single_std);\r\nBENCHMARK(split_multiple_std);\r\n\r\n#endif\r\n\r\n#if defined(__cpp_lib_ranges_stride) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nBENCHMARK(take_every_std);\r\n\r\n#endif\r\n\r\n#if defined(__cpp_lib_ranges_slide) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nBENCHMARK(pairwise_std);\r\n\r\n#endif\r\n\r\n#if defined(__cpp_lib_ranges_repeat) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nBENCHMARK(repeat_std);\r\n\r\n#endif\r\n\r\n#if defined(__cpp_lib_ranges_zip) && LZ_HAS_INCLUDE(<ranges>) && CMAKE_CXX_STANDARD >= 23\r\n\r\nBENCHMARK(zip4_std);\r\nBENCHMARK(zip3_std);\r\nBENCHMARK(zip2_std);\r\n\r\n#endif\r\n\r\nBENCHMARK_MAIN();\r\n"
  },
  {
    "path": "bench/lz/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10...3.12.4)\r\n\r\nproject(CompeTimeBenchmark)\r\n\r\ninclude(FetchContent)\r\n\r\nFetchContent_Declare(cpp-lazy\r\n    SOURCE_DIR \"${CMAKE_CURRENT_LIST_DIR}/../../\"\r\n)\r\nset(CPP_LAZY_USE_STANDALONE ON CACHE BOOL \"\" FORCE)\r\nFetchContent_MakeAvailable(cpp-lazy)\r\n\r\nadd_executable(CompileTimeBenchmarks compile-times.cpp)\r\ntarget_compile_options(CompileTimeBenchmarks\r\n    PRIVATE\r\n        $<$<CXX_COMPILER_ID:GNU>:-ftime-report>\r\n        $<$<CXX_COMPILER_ID:Clang>:-ftime-report -ftime-trace>\r\n)\r\ntarget_link_libraries(CompileTimeBenchmarks PRIVATE cpp-lazy::cpp-lazy)\r\ntarget_compile_features(CompileTimeBenchmarks PRIVATE cxx_std_23)\r\n"
  },
  {
    "path": "bench/lz/compile-times.cpp",
    "content": "#include <Lz/filter.hpp>\n#include <Lz/zip.hpp>\n#include <vector>\n\nint main() {\n    std::vector<int> vec;\n    static_cast<void>(lz::zip(vec, vec, vec));\n    static_cast<void>(lz::filter(vec, [](int) { return true; }));\n}\n"
  },
  {
    "path": "bench/requirements.txt",
    "content": "matplotlib>=3.10.3\n"
  },
  {
    "path": "bench/std/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10...3.12.4)\r\n\r\nproject(CompeTimeBenchmark)\r\n\r\ninclude(FetchContent)\r\n\r\nadd_executable(CompileTimeBenchmarks compile-times.cpp)\r\ntarget_compile_options(CompileTimeBenchmarks\r\n    PRIVATE\r\n        $<$<CXX_COMPILER_ID:GNU>:-ftime-report>\r\n        $<$<CXX_COMPILER_ID:Clang>:-ftime-report -ftime-trace>\r\n)\r\ntarget_compile_features(CompileTimeBenchmarks PRIVATE cxx_std_23)\r\n"
  },
  {
    "path": "bench/std/compile-times.cpp",
    "content": "#include <ranges>\n#include <vector>\n\nint main() {\n    std::vector<int> vec;\n    static_cast<void>(std::ranges::views::zip(vec, vec, vec));\n    static_cast<void>(std::ranges::views::filter(vec, [](int){ return true; }));\n}\n"
  },
  {
    "path": "cmake/cpp-lazy.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nlibdir=${prefix}/@CMAKE_INSTALL_LIBDIR@\nincludedir=${prefix}/@CPP_LAZY_INCLUDE_DIR@\n\nName: cpp-lazy\nDescription: A fast C++ header-only library for lazy evaluation and function tools\nVersion: @PROJECT_VERSION@\nRequires: @PC_REQUIRES@\nLibs: -L${libdir} @PC_LIBS@\nLibs.private: @PC_LIBS_PRIVATE@\nCflags: -I${includedir}\n"
  },
  {
    "path": "cmake/cpp-lazyConfig.cmake.in",
    "content": "@PACKAGE_INIT@\n\ninclude(CMakeFindDependencyMacro)\n\nif (NOT CPP_LAZY_USE_STANDALONE)\n    if (CPP_LAZY_USE_INSTALLED_FMT)\n        message(VERBOSE \"Using system installed {fmt}\")\n        set(FMT_VER_FIND_PACKAGE \"${CPP_LAZY_FMT_DEP_VERSION}\")\n    else()\n        message(VERBOSE \"Using bundled {fmt}: ${CMAKE_CURRENT_LIST_DIR}/../fmt. Version @CPP_LAZY_FMT_DEP_VERSION@\")\n        set(fmt_DIR \"${CMAKE_CURRENT_LIST_DIR}/../fmt\" CACHE STRING \"\" FORCE)\n        set_and_check(fmt_DIR \"${fmt_DIR}\")\n        set(FMT_VER_FIND_PACKAGE \"@CPP_LAZY_FMT_DEP_VERSION@\")\n    endif()\n    find_dependency(fmt ${FMT_VER_FIND_PACKAGE} REQUIRED CONFIG)\nendif()\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/cpp-lazyTargets.cmake\")\n"
  },
  {
    "path": "docs/.gitkeep",
    "content": ""
  },
  {
    "path": "examples/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\r\n\r\nproject(examples LANGUAGES CXX)\r\n\r\ninclude(FetchContent)\r\nFetchContent_Declare(cpp-lazy SOURCE_DIR \"${CMAKE_CURRENT_LIST_DIR}/..\")\r\nFetchContent_MakeAvailable(cpp-lazy)\r\n\r\nset(examples\r\n    algorithm\r\n    any_iterable\r\n    as_iterator\r\n    basic_iterable\r\n    c_string\r\n    cached_reverse\r\n    cached_size\r\n    cartesian_product\r\n    chunk_if\r\n    chunks\r\n    common\r\n    concatenate\r\n    drop_while\r\n    drop\r\n    enumerate\r\n    except\r\n    exclude\r\n    exclusive_scan\r\n    filter\r\n    flatten\r\n    generate_while\r\n    generate\r\n    group_by\r\n    inclusive_scan\r\n    interleave\r\n    intersection\r\n    iter_tools\r\n    join_where\r\n    loop\r\n    map\r\n    maybe_owned\r\n    pairwise\r\n    pipe\r\n    print_and_format\r\n    random\r\n    range\r\n    regex_split\r\n    repeat\r\n    rotate\r\n    slice\r\n    split\r\n    take_every\r\n    take_while\r\n    take\r\n    to_container\r\n    unique\r\n    zip_longest\r\n    zip\r\n)\r\n\r\nforeach(name IN LISTS examples)\r\n    add_executable(example_${name} ${name}.cpp)\r\n    target_link_libraries(example_${name} PRIVATE cpp-lazy::cpp-lazy)\r\n    target_compile_options(example_${name} PRIVATE -ftemplate-backtrace-limit=0)\r\nendforeach()\r\n"
  },
  {
    "path": "examples/algorithm.cpp",
    "content": "#include <Lz/algorithm/algorithm.hpp>\n#include <Lz/c_string.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    // All algorithms will try to use its std equivalent if possible. For instance:\n\n    // std::vector<int> v = {1, 2, 3};\n    // std::cout << (*lz::find(v, 2)) << '\\n'; // Will call std::find\n\n    // auto c_str = lz::c_string(\"Hello, world!\");\n    // std::cout << (*lz::find(c_str, 'w')) << '\\n'; // Will not call std::find\n\n    // But this for example, will call std::find again\n    // auto common = lz::common(c_str);\n    // std::cout << (*lz::find(common, 'w')) << '\\n'; // Will call std::find\n\n    // In short: if the iterable returns a sentinel, then std::* variants cannot be used.\n    // If you want to use std::* variants, you must use lz::common first.\n    std::cout << std::boolalpha;\n\n    {\n        std::cout << \"Empty\\n\";\n        std::vector<int> v;\n        std::cout << lz::empty(v) << '\\n'; // true\n        v = {1, 2, 3};\n        std::cout << lz::empty(v) << \"\\n\\n\"; // false\n    }\n    {\n        std::cout << \"Has one\\n\";\n        std::vector<int> v;\n        std::cout << lz::has_one(v) << '\\n'; // false\n        v = {1};\n        std::cout << lz::has_one(v) << '\\n'; // true\n        v = {1, 2};\n        std::cout << lz::has_one(v) << \"\\n\\n\"; // false\n    }\n    {\n        std::cout << \"Has many\\n\";\n        std::vector<int> v;\n        std::cout << lz::has_many(v) << '\\n'; // false\n        v = {1};\n        std::cout << lz::has_many(v) << '\\n'; // false\n        v = {1, 2};\n        std::cout << lz::has_many(v) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Front\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::front(v) << '\\n'; // 1\n        v = {};\n        // std::cout << lz::front(v) << '\\n'; // Undefined behavior\n    }\n    {\n        std::cout << \"Back\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::back(v) << '\\n'; // 3\n        v = {};\n        // std::cout << lz::back(v) << '\\n'; // Undefined behavior\n    }\n    {\n        std::cout << \"Front or\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::front_or(v, 0) << '\\n'; // 1\n        v = {};\n        std::cout << lz::front_or(v, 0) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Back or\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::back_or(v, 0) << '\\n'; // 3\n        v = {};\n        std::cout << lz::back_or(v, 0) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Accumulate\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::accumulate(v, 0) << '\\n'; // 6\n        std::cout << lz::accumulate(v, 0, std::plus<int>()) << \"\\n\\n\"; // 6\n    }\n    {\n        std::cout << \"Max element\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << *lz::max_element(v) << '\\n'; // 3\n        std::cout << *lz::max_element(v, std::greater<int>()) << \"\\n\\n\"; // 1\n    }\n    {\n        std::cout << \"Min element\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << *lz::min_element(v) << '\\n'; // 1\n        std::cout << *lz::min_element(v, std::greater<int>()) << \"\\n\\n\"; // 3\n    }\n    {\n        std::cout << \"Find if\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << (*lz::find_if(v, [](int i) { return i == 2; })) << '\\n'; // 2\n        std::cout << (lz::find_if(v, [](int i) { return i == 4; }) == std::end(v)) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Find\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << (*lz::find(v, 2)) << '\\n'; // 2\n        std::cout << (lz::find(v, 4) == std::end(v)) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Find last\\n\";\n        std::vector<int> v = {1, 2, 3, 2};\n        std::cout << (*lz::find_last(v, 2)) << '\\n'; // 2\n        std::cout << (lz::find_last(v, 4) == std::end(v)) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Find last if\\n\";\n        std::vector<int> v = {1, 2, 3, 2};\n        std::cout << (*lz::find_last_if(v, [](int i) { return i == 2; })) << '\\n'; // 2\n        std::cout << (lz::find_last_if(v, [](int i) { return i == 4; }) == std::end(v)) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Search\\n\";\n        std::vector<int> v = {1, 2, 3, 2};\n        std::vector<int> v2 = {2, 3};\n        auto pos = lz::search(v, v2);\n        std::cout << *pos.first << ' ' << *pos.second << '\\n'; // 1 2\n        v2 = {4, 5};\n        pos = lz::search(v, v2);\n        std::cout << (pos.first == std::end(v)) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Find if not\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << (*lz::find_if_not(v, [](int i) { return i == 2; })) << '\\n'; // 1\n        std::cout << (lz::find_if_not(v, [](int i) { return i == 4; }) == std::end(v)) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Find last if not\\n\";\n        std::vector<int> v = {1, 2, 3, 2};\n        std::cout << (*lz::find_last_if_not(v, [](int i) { return i == 2; })) << '\\n'; // 3\n        std::cout << (lz::find_last_if_not(v, [](int i) { return i == 4; }) == std::end(v)) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Find or default\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::find_or_default(v, 2, 0) << '\\n'; // 2\n        std::cout << lz::find_or_default(v, 4, 0) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Find or default if\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::find_or_default_if(v, [](int i) { return i == 2; }, 0) << '\\n'; // 2\n        std::cout << lz::find_or_default_if(v, [](int i) { return i == 4; }, 0) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Find last or default\\n\";\n        std::vector<int> v = {1, 2, 3, 2};\n        std::cout << lz::find_last_or_default(v, 2, 0) << '\\n'; // 2\n        std::cout << lz::find_last_or_default(v, 4, 0) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Find last or default if\\n\";\n        std::vector<int> v = {1, 2, 3, 2};\n        std::cout << lz::find_last_or_default_if(v, [](int i) { return i == 2; }, 0) << '\\n'; // 2\n        std::cout << lz::find_last_or_default_if(v, [](int i) { return i == 4; }, 0) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Find last or default not\\n\";\n        std::vector<int> v = {1, 2, 3, 2};\n        std::cout << lz::find_last_or_default_not(v, 2, 0) << '\\n'; // 3\n        std::cout << lz::find_last_or_default_not(v, 4, 0) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Find last or default if not\\n\";\n        std::vector<int> v = {1, 2, 3, 2};\n        std::cout << lz::find_last_or_default_if_not(v, [](int i) { return i == 2; }, 0) << '\\n'; // 3\n        std::cout << lz::find_last_or_default_if_not(v, [](int i) { return i == 4; }, 0) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Index of\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::index_of(v, 2) << '\\n'; // 1\n        std::cout << lz::index_of(v, 4) << \"\\n\\n\"; // lz::npos\n    }\n    {\n        std::cout << \"Index of if\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::index_of_if(v, [](int i) { return i == 2; }) << '\\n'; // 1\n        std::cout << lz::index_of_if(v, [](int i) { return i == 4; }) << \"\\n\\n\"; // lz::npos\n    }\n    {\n        std::cout << \"Contains\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::contains(v, 2) << '\\n'; // true\n        std::cout << lz::contains(v, 4) << \"\\n\\n\"; // false\n    }\n    {\n        std::cout << \"Starts with\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::vector<int> v2 = {1, 2};\n        std::cout << lz::starts_with(v, v2) << '\\n'; // true\n        v2 = {2, 3};\n        std::cout << lz::starts_with(v, v2) << \"\\n\\n\"; // false\n    }\n    {\n        std::cout << \"Ends with\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::vector<int> v2 = {2, 3};\n        std::cout << lz::ends_with(v, v2) << '\\n'; // true\n        v2 = {1, 2};\n        std::cout << lz::ends_with(v, v2) << \"\\n\\n\"; // false\n    }\n    {\n        std::cout << \"Partition\\n\";\n        std::vector<int> v = {1, 2, 3, 4, 5};\n        auto it = lz::partition(v, [](int i) { return i % 2 == 0; });\n        for (auto i = std::begin(v); i != it; ++i) {\n            std::cout << *i << ' ';\n        }\n        // 1 3 5\n        std::cout << '\\n';\n        for (auto i = it; i != std::end(v); ++i) {\n            std::cout << *i << ' ';\n        }\n        // 2 4\n        std::cout << \"\\n\\n\";\n    }\n    {\n        std::cout << \"Mean\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::mean(v) << '\\n'; // 2\n        std::cout << lz::mean(v, std::plus<int>()) << \"\\n\\n\"; // 6\n    }\n    {\n        std::cout << \"For each\\n\";\n        std::vector<int> v = {1, 2, 3};\n        lz::for_each(v, [](int i) { std::cout << i << ' '; });\n        // 1 2 3\n        std::cout << \"\\n\\n\";\n    }\n    {\n        std::cout << \"For each while\\n\";\n        std::vector<int> v = {1, 2, 3};\n        lz::for_each_while(v, [](int i) { \n            std::cout << i << ' ';\n            return i != 2;\n        });\n        // 1 2\n        std::cout << \"\\n\\n\";\n    }\n    {\n        std::cout << \"Copy\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::vector<int> v2;\n        lz::copy(v, std::back_inserter(v2));\n        for (auto i : v2) {\n            std::cout << i << ' ';\n        }\n        // 1 2 3\n        std::cout << \"\\n\\n\";\n    }\n    {\n        std::cout << \"Transform\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::vector<int> v2;\n        lz::transform(v, std::back_inserter(v2), [](int i) { return i * 2; });\n        for (auto i : v2) {\n            std::cout << i << ' ';\n        }\n        // 2 4 6\n        std::cout << \"\\n\\n\";\n    }\n    {\n        std::cout << \"Equal\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::vector<int> v2 = {1, 2, 3};\n        std::cout << lz::equal(v, v2) << '\\n'; // true\n        v2 = {1, 2, 4};\n        std::cout << lz::equal(v, v2) << \"\\n\\n\"; // false\n    }\n    {\n        std::cout << \"Lower bound\\n\";\n        std::vector<int> v = {1, 2, 3}; // Must be sorted\n        std::cout << *lz::lower_bound(v, 2) << '\\n'; // 2\n        std::cout << *lz::lower_bound(v, 3, std::greater<int>()) << '\\n'; // 1\n        std::cout << (lz::lower_bound(v, 4) == std::end(v)) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Upper bound\\n\";\n        std::vector<int> v = {1, 2, 3}; // Must be sorted\n        std::cout << *lz::upper_bound(v, 2) << '\\n'; // 3\n        std::cout << *lz::upper_bound(v, 3, std::greater<int>()) << '\\n'; // 1\n        std::cout << (lz::upper_bound(v, 4) == std::end(v)) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Binary Search\\n\";\n        std::vector<int> v = {1, 2, 3}; // Must be sorted\n        std::cout << lz::binary_search(v, 2) << '\\n'; // true\n        std::cout << lz::binary_search(v, 4) << \"\\n\\n\"; // false\n    }\n    {\n        std::cout << \"All of\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::all_of(v, [](int i) { return i > 0; }) << '\\n'; // true\n        std::cout << lz::all_of(v, [](int i) { return i > 1; }) << \"\\n\\n\"; // false\n    }\n    {\n        std::cout << \"Any of\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::any_of(v, [](int i) { return i == 2; }) << '\\n'; // true\n        std::cout << lz::any_of(v, [](int i) { return i == 4; }) << \"\\n\\n\"; // false\n    }\n    {\n        std::cout << \"None of\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::none_of(v, [](int i) { return i == 2; }) << '\\n'; // false\n        std::cout << lz::none_of(v, [](int i) { return i == 4; }) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Adjacent find\\n\";\n        std::vector<int> v = {1, 2, 2, 3, 2};\n        auto pos = lz::adjacent_find(v);\n        auto distance = lz::distance(v.begin(), pos);\n        std::cout << *pos << \" with distance \" << distance << '\\n'; // 2 with distance 1\n        ++pos;\n        lz::sized_iterable<decltype(v.begin())> iterable(pos, v.end());\n        auto new_pos = lz::adjacent_find(iterable);\n        std::cout << (new_pos == iterable.end()) << \"\\n\\n\"; // true\n    }\n    {\n        std::cout << \"Count if\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::count_if(v, [](int i) { return i == 2; }) << '\\n'; // 1\n        std::cout << lz::count_if(v, [](int i) { return i == 4; }) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Count\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::count(v, 2) << '\\n'; // 1\n        std::cout << lz::count(v, 4) << \"\\n\\n\"; // 0\n    }\n    {\n        std::cout << \"Is sorted\\n\";\n        std::vector<int> v = {1, 2, 3};\n        std::cout << lz::is_sorted(v) << '\\n'; // true\n        v = {3, 2, 1};\n        std::cout << lz::is_sorted(v) << \"\\n\\n\"; // false\n        std::cout << lz::is_sorted(v, std::greater<int>()) << '\\n'; // true\n    }\n}\n"
  },
  {
    "path": "examples/any_iterable.cpp",
    "content": "#include <Lz/any_iterable.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/map.hpp>\n#include <iostream>\n#include <vector>\n\nnamespace {\n\n// First template parmeter is the value_type. The second the reference type.\n// Because we do no return any references here, we need to specify a second template parameter: an int by copy:\nlz::any_iterable<int, int> filter_map_copy_value(const std::vector<int>& vec) {\n    return vec | lz::filter([](int i) { return i % 2 == 0; }) | lz::map([](int i) { return i * 2; });\n}\n\n// First template parmeter is the value_type. The second the reference type.\n// Implicitly, this is value_type&, so in our case int&, if that is not correct then you need to specify it.\n// Because we return a reference here, we do not need to specify a second template parameter:\nlz::any_iterable<int> filter_map_reference(std::vector<int>& vec) {\n    return vec | lz::filter([](int& i) { return i % 2 == 0; }) | lz::map([](int& i) -> int& { return i; });\n}\n\n} // namespace\n\nint main() {\n    std::vector<int> vec = { 1, 2, 3, 4, 5 };\n    lz::any_iterable<int, int> view = filter_map_copy_value(vec);\n    for (int i : view) {\n        std::cout << i << ' ';\n    }\n    std::cout << '\\n';\n    // output:\n    // 4 8\n\n    auto second_view = filter_map_reference(vec);\n    for (int& i : second_view) {\n        std::cout << i << ' ';\n    }\n    std::cout << '\\n';\n\n    // output:\n    // 2 4\n    return 0;\n}\n"
  },
  {
    "path": "examples/as_iterator.cpp",
    "content": "#include <Lz/as_iterator.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> vec = { 1, 2, 3, 4, 5 };\n    auto iter = lz::as_iterator(vec);\n\n    using vector_iter = typename decltype(iter)::value_type;\n\n    for (const vector_iter vector_iterator : iter) {\n        std::cout << *vector_iterator << \" \"; // prints: 1 2 3 4 5\n    }\n    std::cout << std::endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "examples/basic_iterable.cpp",
    "content": "#include <Lz/basic_iterable.hpp>\n#include <Lz/c_string.hpp>\n#include <vector>\n#include <iostream>\n\nint main() {\n    // - Iterable as template parameter\n    // - Iterable as function argument\n    // - Iterable has a .size() method because arr has a size\n    int arr[] = { 1, 2, 3, 4, 5 };\n    lz::basic_iterable<int[5]> iterable_iterable(arr);\n    std::cout << iterable_iterable.size() << '\\n'; // Output: 5\n\n    // - Iterator as template parameter\n    // - Iterable as function argument\n    // - Iterable has size() method because std::begin(arr) and std::end(arr) are random access iterators\n    lz::basic_iterable<int*> iterator_iterable(arr);\n    std::cout << iterator_iterable.size() << '\\n'; // Output: 5\n\n    // - Iterable as template parameter\n    // - Iterator as function argument\n    // - Iterable has size() method because std::begin(arr) and std::end(arr) are random access iterators\n    lz::basic_iterable<int[5]> iterable_iterator(std::begin(arr), std::end(arr));\n    std::cout << iterable_iterator.size() << '\\n'; // Output: 5\n\n    // - Iterator as template parameter\n    // - Iterator as function argument\n    // - Iterable has size() method because std::begin(arr) and std::end(arr) are random access iterators\n    lz::basic_iterable<int*> iterator_iterator(std::begin(arr), std::end(arr));\n    std::cout << iterator_iterator.size() << '\\n'; // Output: 5\n\n    // You can also work with sentinels:\n    auto cstr = lz::c_string(\"Hello, world!\");\n    using it = decltype(cstr.begin());\n    using sentinel = decltype(cstr.end());\n    lz::basic_iterable<it, sentinel> iterable(cstr.begin(), cstr.end());\n    std::cout << \"c_string iterable does not contain a size() method\\n\";\n    // iterable does not contain a size() method because cstr does not have a size and is also not random access\n\n    // The following would get an error because cstr is not random access and there's no way to get the size\n    // lz::sized_iterable<it, sentinel> sized_cstr_iterable(cstr.begin(), cstr.end());\n\n    // With STL containers you can use the following:\n    std::vector<int> vec = { 1, 2, 3, 4, 5 };\n    lz::basic_iterable<std::vector<int>::iterator> vec_iterable(vec.begin(), vec.end());\n    std::cout << vec_iterable.size() << '\\n'; // Output: 5\n}\n"
  },
  {
    "path": "examples/c_string.cpp",
    "content": "#include <Lz/c_string.hpp>\n#include <Lz/algorithm/for_each.hpp>\n#include <iostream>\n\n\nint main() {\n    const char* my_str = \"Hello, World!\";\n\n#ifdef LZ_HAS_CXX_17\n    for (const char& c : lz::c_string(my_str)) {\n        std::cout << c;\n        // or use fmt::print(\"{}\\n\", c);\n    }\n    // Output: Hello, World!\n    std::cout << '\\n';\n\n    const char my_str_iterable[] = \"Hello, World!\";\n    for (const char& c : my_str_iterable | lz::c_string) {\n        std::cout << c;\n        // or use fmt::print(\"{}\\n\", c);\n    }\n    // Output: Hello, World!\n    std::cout << '\\n';\n#else\n    lz::for_each(lz::c_string(my_str), [](const char& c) {\n        std::cout << c;\n        // or use fmt::print(\"{}\\n\", c);\n    });\n    // Output: Hello, World!\n\n    std::cout << '\\n';\n    const char my_str_iterable[] = \"Hello, World!\";\n    lz::for_each(my_str_iterable | lz::c_string, [](const char& c) {\n        std::cout << c;\n        // or use fmt::print(\"{}\\n\", c);\n    });\n// Output: Hello, World!\n#endif\n}\n"
  },
  {
    "path": "examples/cached_reverse.cpp",
    "content": "#include <Lz/filter.hpp>\n#include <Lz/reverse.hpp>\n#include <iostream>\n#include <numeric>\n#include <vector>\n\nint main() {\n    std::vector<int> v = { 1, 2, 3, 4, 5 };\n    auto reversed = lz::reverse(v);\n    for (const auto& elem : reversed) {\n        std::cout << elem << \" \";\n    }\n    std::cout << std::endl;\n    // Output: 5 4 3 2 1\n\n    /**\n    Reverses the iterable with a caching mechanism. Normally, the std::reverse_iterator uses an operator-- in its operator*\n    to access the previous element, which can be inefficient for some iterables such as iterables that traverses multiple elements\n    in its operator-- (such as `lz::filter` or `lz::take_every`). Say filter_iterator::operator-- traverses 10 elements to get\n    to the previous element, then this means 20 elements will be traversed when operator-- and operator* are called in a row. This\n    iterable prevents that by using a caching mechanism.\n    */\n    std::vector<int> large_vector(10000000);\n    std::iota(large_vector.begin(), large_vector.end(), 0); // Fill with 0 to 9.999.999\n    auto reversed_large = large_vector | lz::filter([](int i) { return i < 3; }) | lz::cached_reverse;\n    for (const auto& elem : reversed_large) {\n        std::cout << elem << \" \";\n    }\n    std::cout << std::endl;\n    // Output: 2 1 0 (only the first three elements, reversed)\n}\n"
  },
  {
    "path": "examples/cached_size.cpp",
    "content": "#include <Lz/cached_size.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/take_every.hpp>\n#include <Lz/zip.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    /**\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     */\n    std::vector<int> to_filter = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n    // filter isn't sized; it does not contain size method\n    auto filtered = lz::filter(to_filter, [](int i) { return i % 2 == 0; }); // {0, 2, 4, 6, 8}\n    // const auto size = filtered.size(); // ERROR filtered.size() does not compile, since it is not sized\n\n    // Here, filtered is traversed only once O(n) to get the size, which is 5, then cached\n    auto filtered_cached_size = filtered | lz::cache_size; // filtered_cached_size now contains a size method\n    // Therefore, calling size() is O(1)\n    std::cout << \"Size of filtered (O(1)): \" << filtered_cached_size.size() << '\\n'; // 5, cheap operation\n\n    // lz::zip also requires a size, so it will use the cached size, instead of traversing the filtered iterable again\n    auto zipped = lz::zip(filtered_cached_size, filtered_cached_size); // O(1) operation when calling end()\n    auto zipped_slow = lz::zip(filtered, filtered);                    // O(n) operation when calling end()\n\n    std::cout << \"Size of zipped (O(1)): \" << zipped.size() << '\\n'; // 5, cheap operation\n    // Because filtered isn't sized, the example below will not compile\n    // std::cout << \"Size of zipped_slow: \" << zipped_slow.size() << '\\n';\n    // instead you can use lz::eager_size\n    std::cout << \"Size of zipped_slow using eager size (O(n)): \" << lz::eager_size(zipped_slow) << '\\n'; // 5, O(n) operation\n\n    // Again, zipped is sized because filtered_cached_size is sized\n    auto take_every = lz::take_every(zipped, 2); // O(1) operation when calling end(); { {0, 2}, {4, 6}, {8, 10} }\n    std::cout << \"Size of take_every (O(1)): \" << take_every.size() << '\\n'; // 3, cheap operation\n\n    // zipped_slow is not sized, so it will traverse the entire zipped_slow iterable\n    auto take_every_slow = lz::take_every(zipped_slow, 2); // O(n) operation; { {0, 0}, {2, 2}, {4, 4}, {6, 6}, {8, 8} }\n    std::cout << \"Size of take_every_slow (O(n)): \" << lz::eager_size(take_every_slow) << '\\n'; // 5, O(n) operation\n\n    /** Use lz::cache_size if:\n     * - Your iterable is exactly bidirectional (so forward and random access excluded) and;\n     * - Your iterable is not sized and (like lz::filter) either OR\n     * - You use *multiple* / a combination of the following iterables:\n     * - Your iterable is not sentinelled\n     *   - `lz::chunks`\n     *   - `lz::enumerate`\n     *   - `lz::exclude`\n     *   - `lz::interleave`\n     *   - `lz::rotate`\n     *   - `lz::take`\n     *   - `lz::take_every`\n     *   - `lz::zip_longest`\n     *   - `lz::zip`\n     * - OR Are planning to call begin() or end() multiple times on the same instance (with one or more of the above iterable\n     * combinations)\n     */\n    // So, by calling .end() on the same instance, each time .end() is called, the size is calculated again\n    auto zipped_non_cached = lz::zip(to_filter, to_filter);\n    auto end = zipped_non_cached.end(); // O(n) operation, since to_filter is not sized and bidirectional\n\n    static_cast<void>(end); // to avoid unused variable warning\n\n    end = zipped_non_cached.end(); // another O(n) operation, since to_filter is not sized and bidirectional\n\n    static_cast<void>(end); // to avoid unused variable warning\n}\n"
  },
  {
    "path": "examples/cartesian_product.cpp",
    "content": "#include <Lz/cartesian_product.hpp>\n#include <iostream>\n#include <vector>\n\n\nint main() {\n    std::vector<int> vec = {1, 2, 3};\n    std::vector<char> chars = {'a', 'b', 'c'};\n\n    auto cartesian = lz::cartesian_product(vec, chars);\n\n    for (auto&& tup : cartesian) {\n        auto&& first = std::get<0>(tup);\n        auto&& snd = std::get<1>(tup);\n\n        std::cout << first << ' ' << snd << '\\n';\n        // or use fmt::print(\"{} {}\\n\", first, snd);\n    }\n    // Output:\n    // 1 a\n    // 1 b\n    // 1 c\n    // 2 a\n    // 2 b\n    // 2 c\n    // 3 a\n    // 3 b\n    // 3 c\n    std::cout << '\\n';\n    // or\n    cartesian = vec | lz::cartesian_product(chars);\n\n    for (auto&& tup : cartesian) {\n        auto&& first = std::get<0>(tup);\n        auto&& snd = std::get<1>(tup);\n\n        std::cout << first << ' ' << snd << '\\n';\n        // or use fmt::print(\"{} {}\\n\", first, snd);\n    }\n    // Output:\n    // 1 a\n    // 1 b\n    // 1 c\n    // 2 a\n    // 2 b\n    // 2 c\n    // 3 a\n    // 3 b\n    // 3 c\n}"
  },
  {
    "path": "examples/chunk_if.cpp",
    "content": "#include <Lz/chunk_if.hpp>\n#include <Lz/algorithm/for_each.hpp>\n#include <iostream>\n#include <vector>\n\n\nint main() {\n\tstd::string s = \"hello world; this is a message;\";\n\tauto chunked = lz::chunk_if(s, [](const char c) { return c == ';'; });\n\n#ifdef LZ_HAS_CXX_17\n    for (auto chunk : chunked) {\n        for (char& c : chunk) {\n            std::cout << c;\n            // or use fmt::print(\"{}\", c);\n        }\n        // or use fmt::print(\"\\n\");\n        std::cout << '\\n';\n    }\n    // Output:\n    // hello world\n    //  this is a message\n\n    for (auto chunk : s | lz::chunk_if([](const char c) { return c == ';'; })) {\n        for (char& c : chunk) {\n            std::cout << c;\n            // or use fmt::print(\"{}\", c);\n        }\n        // or use fmt::print(\"\\n\");\n        std::cout << '\\n';\n    }\n    // Output:\n    // hello world\n    //  this is a message\n\n    // With std::string[_view]\n    auto chunked_sv = s | lz::sv_chunk_if([](const char c) { return c == ';'; });\n    for (auto chunk : chunked_sv) {\n        std::cout.write(chunk.data(), static_cast<std::streamsize>(chunk.size()));\n        std::cout << '\\n';\n    }\n    // Output:\n    // hello world\n    //  this is a message\n\n    // with custom types\n    auto chunked_vector = s | lz::t_chunk_if<std::vector<char>>([](const char c) { return c == ';'; });\n    for (auto vec_chunk : chunked_vector) {\n        for (char& c : vec_chunk) {\n            std::cout << c;\n            // or use fmt::print(\"{}\", c);\n        }\n        // or use fmt::print(\"\\n\");\n        std::cout << '\\n';\n    }\n#else\n    using string_iterable = decltype(chunked)::value_type;\n    lz::for_each(chunked, [](string_iterable chunk) {\n        for (char c : chunk) {\n            std::cout << c;\n            // or use fmt::print(\"{}\", c);\n        }\n        std::cout << '\\n';\n    });\n    // Output:\n    // hello world\n    //  this is a message\n\n    lz::for_each(s | lz::chunk_if([](const char c) { return c == ';'; }), [](string_iterable chunk) {\n        for (char c : chunk) {\n            std::cout << c;\n            // or use fmt::print(\"{}\", c);\n        }\n        std::cout << '\\n';\n    });\n    // Output:\n    // hello world\n    //  this is a message\n\n    // With lz::string[_view]\n    lz::for_each(s | lz::sv_chunk_if([](const char c) { return c == ';'; }), [](const lz::string_view chunk) {\n        std::cout.write(chunk.data(), static_cast<std::streamsize>(chunk.size()));\n        std::cout << '\\n';\n    });\n    // Output:\n    // hello world\n    //  this is a message\n\n#ifdef LZ_HAS_CXX_11\n\n    // with custom types\n    lz::for_each(s | lz::t_chunk_if<std::vector<char>>{}([](const char c) { return c == ';'; }),\n                 [](const std::vector<char>& vec_chunk) {\n                     for (char c : vec_chunk) {\n                         std::cout << c;\n                         // or use fmt::print(\"{}\", c);\n                     }\n                     std::cout << '\\n';\n                 });\n    // Output:\n    // hello world\n    //  this is a message\n\n#else\n\n\n    // with custom types\n    lz::for_each(s | lz::t_chunk_if<std::vector<char>>([](const char c) { return c == ';'; }),\n                 [](const std::vector<char>& vec_chunk) {\n                     for (char c : vec_chunk) {\n                         std::cout << c;\n                         // or use fmt::print(\"{}\", c);\n                     }\n                     std::cout << '\\n';\n                 });\n    // Output:\n    // hello world\n    //  this is a message\n\n#endif // LZ_HAS_CXX_11\n\n#endif\n}\n"
  },
  {
    "path": "examples/chunks.cpp",
    "content": "#include <Lz/chunks.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n\tstd::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};\n\tauto chunks = lz::chunks(v, 3);\n\n\tfor (auto&& chunk : chunks) {\n        std::cout << \"This chunk has length \" << lz::distance(chunk) << '\\n';\n        // or use fmt::print(\"This chunk has length {}\\n\", std::distance(chunk.begin(), chunk.end()));\n        for (int& i : chunk) {\n            std::cout << i << ' ';\n            // or use fmt::print(\"{} \", i);\n        }\n        std::cout << '\\n';\n        // or use fmt::print(\"\\n\");\n    }\n    std::cout << '\\n';\n    // Output\n    // This chunk has length 3\n    // 1 2 3\n    // This chunk has length 3\n    // 4 5 6\n    // This chunk has length 2\n    // 7 8\n\n    for (auto&& chunk : v | lz::chunks(3)) {\n        std::cout << \"This chunk has length \" << lz::distance(chunk) << '\\n';\n        // or use fmt::print(\"This chunk has length {}\\n\", std::distance(chunk.begin(), chunk.end()));\n        for (int& i : chunk) {\n            std::cout << i << ' ';\n            // or use fmt::print(\"{} \", i);\n        }\n        std::cout << '\\n';\n        // or use fmt::print(\"\\n\");\n    }\n}"
  },
  {
    "path": "examples/common.cpp",
    "content": "#include <Lz/algorithm/algorithm.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/common.hpp>\n#include <iostream>\n\nint main() {\n    auto iterable_with_end_sentinel = lz::c_string(\"hello world\");\n    // .end() is a sentinel (lz::default_sentinel_t) meaning it is a different type than its .begin() type\n    // To make it a common one, use:\n    auto iterable = iterable_with_end_sentinel | lz::common;\n    // or\n    auto iterable2 = lz::common(iterable_with_end_sentinel);\n\n    // The following would give us a compiler error (normally) because the types are different:\n    // std::find_if(iterable_with_end_sentinel.begin(), iterable_with_end_sentinel.end(), [](char c) { return c == 'o'; });\n\n    // But with lz::common, it works:\n    auto pos = std::find_if(iterable.begin(), iterable.end(), [](char c) { return c == 'o'; });\n    auto pos2 = std::find_if(iterable2.begin(), iterable2.end(), [](char c) { return c == 'o'; });\n\n    std::cout << \"found o in (common)iterable with std::find_if at \" << std::distance(iterable.begin(), pos) << '\\n';    // o\n    std::cout << \"found o in (common)iterable2 with std::find_if at \" << std::distance(iterable2.begin(), pos2) << '\\n'; // o\n\n    // Alternatively, you can use lz::find_if (in combination with lz::distance):\n    auto pos3 = lz::find_if(iterable_with_end_sentinel, [](char c) { return c == 'o'; });\n    std::cout << \"found o in iterable_with_end_sentinel with lz::find_if at \"\n              << lz::distance(iterable_with_end_sentinel.begin(), pos3) << '\\n'; // o\n}\n"
  },
  {
    "path": "examples/concatenate.cpp",
    "content": "#include <Lz/concatenate.hpp>\n#include <Lz/range.hpp>\n#include <array>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::array<int, 4> a{1, 2, 3, 4};\n    std::array<int, 4> b{5, 6, 7, 8};\n\n    // Because all containers are mutable, int& is used as the reference type.\n    const auto concat = lz::concat(a, b);\n    for (int& i : concat) {\n        std::cout << i << ' ';\n        // or fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3 4 5 6 7 8\n    std::cout << '\\n';\n\n    std::array<int, 4> c{9, 10, 11, 12};\n    std::array<int, 4> d{13, 14, 15, 16};\n\n    const auto concat2 = a | lz::concat(b, c, d);\n    for (int& i : concat2) {\n        std::cout << i << ' ';\n        // or fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16\n\n    // However, if one of the containers is const, all references will be const.\n    const std::array<int, 4> e{ 17, 18, 19, 20 };\n    const auto concat3 = a | lz::concat(b, e);\n    for (const int& i : concat3) {\n        std::cout << i << ' ';\n        // or fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3 4 5 6 7 8 17 18 19 20\n    std::cout << '\\n';\n\n    // And if one of the iterables yields elements by value, all elements will be yielded by value.\n    std::vector<int> f{ 21, 22, 23, 24 }; // normally yields by reference\n    auto range = lz::range(5);       // range yields by value\n    for (int i : f | lz::concat(range)) { // so, the common reference type is int\n        std::cout << i << ' ';\n        // or fmt::print(\"{} \", i);\n    }\n    // Output: 21 22 23 24 0 1 2 3 4\n    std::cout << '\\n';\n\n    std::vector<std::reference_wrapper<int>> g{ std::ref(a[0]), std::ref(a[1]) };\n    auto concat4 = g | lz::concat(f);\n    for (std::reference_wrapper<int> i : concat4) {\n        std::cout << i << ' ';\n        // or fmt::print(\"{} \", i);\n    }\n\n}\n"
  },
  {
    "path": "examples/drop.cpp",
    "content": "#include <Lz/drop.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7 };\n    for (int& i : lz::drop(vec, 4)) {\n        std::cout << i << ' ';\n        // Or: fmt::print(\"{} \", i);\n    }\n    // Output: 5 6 7\n\n    std::cout << '\\n';\n\n    for (int& i : vec | lz::drop(4)) {\n        std::cout << i << ' ';\n        // Or: fmt::print(\"{} \", i);\n    }\n    // Output: 5 6 7\n}\n"
  },
  {
    "path": "examples/drop_while.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/drop_while.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7 };\n    for (int& i : lz::drop_while(vec, [](int i) { return i < 5; })) {\n        std::cout << i << ' ';\n        // Or: fmt::print(\"{} \", i);\n    }\n    // Output: 5 6 7\n\n    std::cout << '\\n';\n\n    for (int& i : vec | lz::drop_while([](int i) { return i < 5; })) {\n        std::cout << i << ' ';\n        // Or: fmt::print(\"{} \", i);\n    }\n    // Output: 5 6 7\n}\n"
  },
  {
    "path": "examples/duplicates.cpp",
    "content": "#include <Lz/duplicates.hpp>\n#include <iostream>\n\nint main() {\n    std::vector<int> input = { 1, 2, 2, 3, 4, 4, 5, 6, 6, 6 };\n    std::sort(input.begin(), input.end());\n    auto dupes = lz::duplicates(input);\n    for (const auto& pair : dupes) {\n        std::cout << \"Element: \" << pair.first << \", Count: \" << pair.second << '\\n';\n    }\n    // Example output:\n    // Element: 1, Count: 1\n    // Element: 2, Count: 2\n    // Element: 3, Count: 1\n    // Element: 4, Count: 2\n    // Element: 5, Count: 1\n    // Element: 6, Count: 3\n}\n"
  },
  {
    "path": "examples/enumerate.cpp",
    "content": "#include <Lz/enumerate.hpp>\n#include <vector>\n#include <iostream>\n\n\nint main() {\n    std::vector<int> to_enumerate = {1, 2, 3, 4, 5};\n\n    std::cout << \"By value\\n\";\n    for (std::pair<int, int> pair : lz::enumerate(to_enumerate)) {\n        std::cout << pair.first << ' ' << pair.second << '\\n';\n        // Or use fmt::print(\"{} {}\\n\", pair.first, pair.second);\n    }\n    // yields:\n    // [(index element)by value] [(container element)]\n    //          0                               1\n    //          1                               2\n    //          2                               3\n    //          3                               4\n    //          4                               5\n    std::cout << '\\n';\n    for (std::pair<int, int> pair : to_enumerate | lz::enumerate) {\n        std::cout << pair.first << ' ' << pair.second << '\\n';\n        // Or use fmt::print(\"{} {}\\n\", pair.first, pair.second);\n    }\n    // yields:\n    // [(index element)by value] [(container element)]\n    //          0                               1\n    //          1                               2\n    //          2                               3\n    //          3                               4\n    //          4                               5\n\n    std::cout << \"\\nBy reference\\n\";\n    for (std::pair<int, int&> pair : to_enumerate |  lz::enumerate(1)) {\n        std::cout << pair.first << ' ' << pair.second << '\\n';\n        // Or use fmt::print(\"{} {}\\n\", pair.first, pair.second);\n    }\n    // yields:\n    // [(index element)by value] [(container element by reference)]\n    //          1                               1\n    //          2                               2\n    //          3                               3\n    //          4                               4\n    //          5                               5\n}"
  },
  {
    "path": "examples/except.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/except.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> vec = { 1, 2, 3, 4, 5 };\n    std::vector<int> to_except = { 5, 3 };\n    std::sort(to_except.begin(), to_except.end());\n\n    auto excepted = lz::except(vec, to_except); // excepted = { 1, 2, 4 }\n\n#ifndef LZ_HAS_CXX_17\n\n    lz::for_each(excepted, [](int i) { std::cout << i << \" \"; });\n\n    // Output: 1 2 4\n\n#else\n\n    for (const auto& i : excepted) {\n        std::cout << i << \" \";\n    }\n\n    // Output: 1 2 4\n\n#endif\n\n    std::cout << std::endl;\n}\n"
  },
  {
    "path": "examples/exclude.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/except.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> values = {1, 2, 3, 4, 5};\n    std::vector<int> to_except = {2, 3, 4};\n\n    std::sort(to_except.begin(), to_except.end()); // Always sort values to except before creating except object\n    const auto except = lz::except(values, to_except);\n\n#ifdef LZ_HAS_CXX_17\n    for (int& i : except) {\n        std::cout << i << ' ';\n        // or use fmt::print(\"{} \", i);\n    }\n    std::cout << '\\n';\n    // Output:\n    // 1 5\n\n    for (int& i : values | lz::except(to_except)) {\n        std::cout << i << ' ';\n        // or use fmt::print(\"{} \", i);\n    }\n    std::cout << '\\n';\n    // Output:\n    // 1 5\n#else\n    lz::for_each(except, [](int i) {\n        std::cout << i << ' ';\n        // or use fmt::print(\"{} \", i);\n    });\n    std::cout << '\\n';\n    // Output:\n    // 1 5\n\n    lz::for_each(values | lz::except(to_except), [](int i) {\n        std::cout << i << ' ';\n        // or use fmt::print(\"{} \", i);\n    });\n    // Output:\n    // 1 5\n#endif\n}\n"
  },
  {
    "path": "examples/exclusive_scan.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/exclusive_scan.hpp>\n#include <iostream>\n\nint main() {\n    int array[] = {3, 5, 2, 3, 4, 2, 3};\n    // start the scan from 0\n    // operator+ is required or add a custom operator+ in its third parameter\n    auto scan = lz::exclusive_scan(array, 0);\n\n#ifdef LZ_HAS_CXX_17\n    for (int& i : scan) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // prints 0 3 8 10 13 17 19 22\n\n    // essentially it's:\n    // 0 (0)\n    // 0 + 3 (3)\n    // 0 + 3 + 5 (8)\n    // 0 + 3 + 5 + 2 (10)\n    // 0 + 3 + 5 + 2 + 3 (13)\n    // 0 + 3 + 5 + 2 + 3 + 4 (17)\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 (19)\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 + 3 (22)\n\n    std::cout << '\\n';\n    // The 0 here is important. Cannot be left empty otherwise it will lead to compilation errors because the initial value cannot\n    // be deduced.\n    // operator+ is required or add a custom operator+ in its second parameter\n    for (int& i : array | lz::exclusive_scan(0)) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // prints 0 3 8 10 13 17 19 22\n\n    // essentially it's:\n    // 0 (0)\n    // 0 + 3 (3)\n    // 0 + 3 + 5 (8)\n    // 0 + 3 + 5 + 2 (10)\n    // 0 + 3 + 5 + 2 + 3 (13)\n    // 0 + 3 + 5 + 2 + 3 + 4 (17)\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 (19)\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 + 3 (22)\n#else\n    lz::for_each(scan, [](int i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n    // prints 0 3 8 10 13 17 19 22\n\n    // essentially it's:\n    // 0 (0)\n    // 0 + 3 (3)\n    // 0 + 3 + 5 (8)\n    // 0 + 3 + 5 + 2 (10)\n    // 0 + 3 + 5 + 2 + 3 (13)\n    // 0 + 3 + 5 + 2 + 3 + 4 (17)\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 (19)\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 + 3 (22)\n\n    std::cout << '\\n';\n    // The 0 here is important. Cannot be left empty otherwise it will lead to compilation errors because the initial value cannot\n    // be deduced.\n    // operator+ is required or add a custom operator+ in its second parameter\n    lz::for_each(array | lz::exclusive_scan(0), [](int i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n// prints 0 3 8 10 13 17 19 22\n\n// essentially it's:\n// 0 (0)\n// 0 + 3 (3)\n// 0 + 3 + 5 (8)\n// 0 + 3 + 5 + 2 (10)\n// 0 + 3 + 5 + 2 + 3 (13)\n// 0 + 3 + 5 + 2 + 3 + 4 (17)\n// 0 + 3 + 5 + 2 + 3 + 4 + 2 (19)\n// 0 + 3 + 5 + 2 + 3 + 4 + 2 + 3 (22)\n#endif\n}\n"
  },
  {
    "path": "examples/filter.cpp",
    "content": "#include <Lz/filter.hpp>\n#include <vector>\n#include <iostream>\n\n\nint main() {\n    std::vector<int> to_filter = {1, 2, 3, 4, 5, 6};\n    const auto filter = lz::filter(to_filter, [](const int i) { return i % 2 == 0; });\n\tfor (int& i : filter) {\n\t\tstd::cout << i << ' ';\n\t\t// Or use fmt::print(\"{} \", i);\n\t}\n    // Output: 2 4 6\n\n\tstd::cout << '\\n';\n\tfor (int& i : to_filter | lz::filter([](const int i) { return i % 2 == 0; })) {\n\t\tstd::cout << i << ' ';\n\t\t// Or use fmt::print(\"{} \", i);\n\t}\n\t// Output: 2 4 6\n}"
  },
  {
    "path": "examples/flatten.cpp",
    "content": "#include <Lz/flatten.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<std::vector<std::vector<int>>> vectors = {\n        { { 1,2, 3}, {}, {4} },\n        {},\n        {{ 5, 6 }, {7} , {}}\n    };\n    auto flattened = lz::flatten(vectors);\n\n    for (int& i : flattened) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // prints 1 2 3 4 5 6 7\n\n    std::cout << '\\n';\n    for (int& i : vectors | lz::flatten) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // prints 1 2 3 4 5 6 7\n}"
  },
  {
    "path": "examples/generate.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/generate.hpp>\n#include <iostream>\n\nint main() {\n\tstd::size_t counter = 0;\n\tstatic constexpr std::size_t amount = 4;\n\n    auto gen = lz::generate(\n        [&counter]() {\n            auto tmp{ counter++ };\n            return tmp;\n        },\n        amount);\n\n#ifdef LZ_HAS_CXX_17\n    for (std::size_t i : gen) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n// Output: 0 1 2 3\n#else\n    lz::for_each(gen, [](std::size_t i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n// Output: 0 1 2 3\n#endif\n}\n"
  },
  {
    "path": "examples/generate_while.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/generate_while.hpp>\n#include <iostream>\n\nint main() {\n    int i = 0;\n    auto generator = lz::generate_while([&i]() {\n        auto copy = i++;\n        // If `copy` == 4, stop generating values.\n        // This function must return a pair like object (e.g. std::pair) where pair::first must\n        // be a type that is convertible to bool and where pair::second can be any type\n        return std::make_pair(copy, copy != 4);\n    });\n\n#ifdef LZ_HAS_CXX_17\n    // Iterator returns the same type as the function pair second type. In this case, int.\n    for (int i : generator) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 0 1 2 3\n#else\n    lz::for_each(generator, [](int idx) {\n        std::cout << idx << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n    // Output: 0 1 2 3\n#endif\n}\n"
  },
  {
    "path": "examples/group_by.cpp",
    "content": "#include <Lz/group_by.hpp>\n#include <iostream>\n#include <string>\n#include <vector>\n\nint main() {\n\tstd::vector<std::string> vec = {\n\t\t\"hello\", \"hellp\", \"i'm\", \"done\"\n\t};\n\n\tstd::sort(vec.begin(), vec.end(), [](const std::string& a, const std::string& b) { return a.length() < b.length(); });\n\tauto grouper = lz::group_by(vec, [](const std::string& a, const std::string& b) { return a.length() == b.length(); });\n\n\tfor (auto&& pair : grouper) {\n        std::cout << \"String length group: \" << pair.first.length() << '\\n';\n        // Or use fmt::print(\"String length group: {}\\n\", pair.first.length());\n        for (auto& str : pair.second) {\n            std::cout << \"value: \" << str << '\\n';\n            // Or use fmt::print(\"value: {}\\n\", str);\n        }\n    }\n\n    /* Output:\n    String length group: 3\n    value: i'm\n    String length group: 4\n    value: done\n    String length group: 5\n    value: hello\n    value: hellp\n    */\n\n    std::cout << '\\n';\n\n    for (auto&& pair : vec | lz::group_by([](const std::string& a, const std::string& b) { return a.length() == b.length(); })) {\n        std::cout << \"String length group: \" << pair.first.length() << '\\n';\n        // Or use fmt::print(\"String length group: {}\\n\", pair.first.length());\n        for (auto& str : pair.second) {\n            std::cout << \"value: \" << str << '\\n';\n            // Or use fmt::print(\"value: {}\\n\", str);\n        }\n    }\n    /* Output:\n    String length group: 3\n    value: i'm\n    String length group: 4\n    value: done\n    String length group: 5\n    value: hello\n    value: hellp\n    */\n}\n"
  },
  {
    "path": "examples/inclusive_scan.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/inclusive_scan.hpp>\n#include <iostream>\n\nint main() {\n    int array[] = {3, 5, 2, 3, 4, 2, 3};\n    // start the scan from arr[0] (3), with init 0\n    // operator+ is required or add a custom operator+ in its third parameter\n\n    auto scan = lz::inclusive_scan(array);\n\n#ifdef LZ_HAS_CXX_17\n    for (int& i : scan) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // prints 3 8 10 13 17 19 22\n\n    // essentially it's:\n    // 0 + 3 = 3\n    // 0 + 3 + 5 = 8\n    // 0 + 3 + 5 + 2 = 10\n    // 0 + 3 + 5 + 2 + 3 = 13\n    // 0 + 3 + 5 + 2 + 3 + 4 = 17\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 = 19\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 + 3 = 22\n\n    std::cout << '\\n';\n    // The 0 here is important. Cannot be left empty otherwise it will lead to compilation errors because the initial value cannot\n    // be deduced.\n    // operator+ is required or add a custom operator+ in its second parameter\n    for (int& i : array | lz::inclusive_scan(0)) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // essentially it's:\n    // 0 + 3 = 3\n    // 0 + 3 + 5 = 8\n    // 0 + 3 + 5 + 2 = 10\n    // 0 + 3 + 5 + 2 + 3 = 13\n    // 0 + 3 + 5 + 2 + 3 + 4 = 17\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 = 19\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 + 3 = 22\n#else\n    lz::for_each(scan, [](int i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n    // prints 3 8 10 13 17 19 22\n\n    // essentially it's:\n    // 0 + 3 = 3\n    // 0 + 3 + 5 = 8\n    // 0 + 3 + 5 + 2 = 10\n    // 0 + 3 + 5 + 2 + 3 = 13\n    // 0 + 3 + 5 + 2 + 3 + 4 = 17\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 = 19\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 + 3 = 22\n\n    std::cout << '\\n';\n    // The 0 here is important. Cannot be left empty otherwise it will lead to compilation errors because the initial value cannot\n    // be deduced.\n    // operator+ is required or add a custom operator+ in its second parameter\n    lz::for_each(array | lz::inclusive_scan(0), [](int i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n    // essentially it's:\n    // 0 + 3 = 3\n    // 0 + 3 + 5 = 8\n    // 0 + 3 + 5 + 2 = 10\n    // 0 + 3 + 5 + 2 + 3 = 13\n    // 0 + 3 + 5 + 2 + 3 + 4 = 17\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 = 19\n    // 0 + 3 + 5 + 2 + 3 + 4 + 2 + 3 = 22\n#endif\n}\n"
  },
  {
    "path": "examples/interleave.cpp",
    "content": "#include <Lz/interleave.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> vec1 = { 1, 2, 3 }, vec2 = { 4, 5, 6, 7 }, vec3 = { 8, 9, 10, 11, 12 };\n    auto interleaved = lz::interleave(vec1, vec2, vec3); // interleaved = { 1, 4, 8, 2, 5, 9, 3, 6, 10 }\n    // or\n    auto interleaved2 = vec1 | lz::interleave(vec2, vec3); // interleaved = { 1, 4, 8, 2, 5, 9, 3, 6, 10 }\n\n    \n    for (const auto& i : interleaved) {\n        std::cout << i << \" \";\n    }\n    // output: 1 4 8 2 5 9 3 6 10\n    std::cout << std::endl;\n\n    for (const auto& i : interleaved2) {\n        std::cout << i << \" \";\n    }\n    // output: 1 4 8 2 5 9 3 6 10\n    std::cout << std::endl;\n}\n"
  },
  {
    "path": "examples/intersection.cpp",
    "content": "#include <Lz/intersection.hpp>\n#include <vector>\n#include <iostream>\n\nint main() {\n    std::vector<int> a = { 1, 2, 3, 4, 5, 6, 7 };\n    std::vector<int> b = { 2, 3, 7, 9 };\n\n    std::sort(a.begin(), a.end());\n    std::sort(b.begin(), b.end());\n\n    for (int& i : lz::intersection(a, b)) {\n        std::cout << i << ' ';\n        // Or fmt::print(\"{} \", i);\n    }\n    // Output: 2 3 7\n    std::cout << '\\n';\n\n    for (int& i : a | lz::intersection(b)) {\n        std::cout << i << ' ';\n        // Or fmt::print(\"{} \", i);\n    }\n    // Output: 2 3 7\n}\n"
  },
  {
    "path": "examples/iter_tools.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/iter_tools.hpp>\n#include <iostream>\n#include <map>\n#include <vector>\n\nint main() {\n#ifdef LZ_HAS_CXX_17\n    // -------------------------------------- Lines---------------------------------------------------------------\n    std::string text = \"Hello\\nWorld\\n!\";\n    auto lines = lz::lines(text); // or: `text | lz::lines;`\n    for (const auto& line : lines) {\n        std::cout.write(line.data(), static_cast<std::streamsize>(line.size()));\n        std::cout << '\\n';\n    }\n    // \"Hello\"\n    // \"World\"\n    // \"!\"\n    std::cout << '\\n';\n\n    lz::string_view text2 = \"Hello\\nWorld\\n!\";\n    auto lines2 =\n        lz::lines(text2); // or: `text | lz::lines;` Does not hold a reference to the original string, since it is a string_view\n    for (const auto& line : lines2) {\n        std::cout.write(line.data(), static_cast<std::streamsize>(line.size()));\n        std::cout << '\\n';\n    }\n    // \"Hello\"\n    // \"World\"\n    // \"!\"\n    std::cout << '\\n';\n\n#else\n\n    // ------------------------------------ Lines ---------------------------------------------------------------------\n    std::string text = \"Hello\\nWorld\\n!\";\n    auto lines = lz::lines(text); // or: `text | lz::lines;`\n    lz::for_each(lines, [](const lz::string_view& line) {\n        std::cout.write(line.data(), static_cast<std::streamsize>(line.size()));\n        std::cout << '\\n';\n    });\n    // \"Hello\"\n    // \"World\"\n    // \"!\"\n    std::cout << '\\n';\n\n    lz::string_view text2 = \"Hello\\nWorld\\n!\";\n    auto lines2 =\n        lz::lines(text2); // or: `text | lz::lines;` Does not hold a reference to the original string, since it is a string_view\n    lz::for_each(lines2, [](const lz::string_view& line) {\n        std::cout.write(line.data(), static_cast<std::streamsize>(line.size()));\n        std::cout << '\\n';\n    });\n    // \"Hello\"\n    // \"World\"\n    // \"!\"\n    std::cout << '\\n';\n\n#endif\n\n    // -------------------------------- As --------------------------------------------------\n    std::vector<int> numbers = { 1, 2, 3, 4, 5 };\n#ifdef LZ_HAS_CXX_11\n\n    auto floats = lz::as<float>{}(numbers); // or: `numbers | lz::as<float>{};`\n\n#else\n\n    auto floats = lz::as<float>(numbers); // or: `numbers | lz::as<float>;`\n\n#endif\n\n    for (const auto& f : floats) {\n        std::cout << f << ' ';\n    }\n    // 1.0 2.0 3.0 4.0 5.0\n    std::cout << '\\n';\n\n    // --------------------------------- Get_nth ----------------------------------------------------\n    std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n#ifdef LZ_HAS_CXX_11\n\n    auto nth_elements = lz::get_nth<2>{}(three_tuple_vec); // or: `three_tuple_vec | lz::get_nth<2>{};`\n\n#else\n\n    auto nth_elements = lz::get_nth<2>(three_tuple_vec); // or: `three_tuple_vec | lz::get_nth<2>;`\n\n#endif\n    for (const auto& elem : nth_elements) {\n        std::cout << elem << ' ';\n    }\n    // 3 6 9\n    std::cout << '\\n';\n\n    // ------------------------------------- Keys and Values ----------------------------------------------\n    std::map<int, std::string> m = { { 1, \"hello\" }, { 2, \"world\" }, { 3, \"!\" } };\n    auto keys = lz::keys(m);     // or: `m | lz::keys;`\n    auto values = lz::values(m); // or: `m | lz::values;`\n\n    for (const auto& key : keys) {\n        std::cout << key << ' ';\n    }\n    // 1 2 3\n    std::cout << '\\n';\n\n    for (const auto& value : values) {\n        std::cout << value << ' ';\n    }\n    // hello world !\n    std::cout << '\\n';\n\n    // ---------------------------------------- Get nths ------------------------------------------------\n    std::vector<std::tuple<int, int, int>> three_tuple_vec2 = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n#ifdef LZ_HAS_CXX_11\n    auto nths = lz::get_nths<0, 2>{}(three_tuple_vec2); // or: `three_tuple_vec2 | lz::get_nths<0, 2>{};`\n#else\n    auto nths = lz::get_nths<0, 2>(three_tuple_vec2); // or: `three_tuple_vec2 | lz::get_nths<0, 2>;`\n#endif\n    for (const auto& tup : nths) {\n        std::cout << '(' << std::get<0>(tup) << \", \" << std::get<1>(tup) << \") \";\n    }\n    // (1, 3) (4, 6) (7, 9)\n    std::cout << '\\n';\n\n    // ------------------------------------- Filter map --------------------------------------\n    std::vector<int> vec = { 1, 2, 3, 4, 5 };\n    auto fm = lz::filter_map(vec, [](int i) { return i % 2 == 0; }, [](int i) { return i * 2; });\n    // or: `vec | lz::filter_map([](int i) { return i % 2 == 0; }, [](int i) { return i * 2; });`\n    for (const auto& f : fm) {\n        std::cout << f << ' ';\n    }\n    // 4 8\n    std::cout << '\\n';\n\n    // ------------------------------------- Select -----------------------------------------\n    std::vector<int> select_numbers = { 1, 2, 3, 4, 5 };\n    std::vector<bool> selectors = { true, false, true, false, true };\n    auto selected = lz::select(select_numbers, selectors); // or: `select_numbers | lz::select(selectors);`\n    for (const auto& s : selected) {\n        std::cout << s << ' ';\n    }\n    // 1 3 5\n    std::cout << '\\n';\n\n    // ------------------------------------- Drop back while ---------------------------------------\n    std::vector<int> drop_back_numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n    auto drop_back_iterable = lz::drop_back_while(drop_back_numbers, [](int i) { return i >= 7; });\n    // or: `drop_back_numbers | lz::drop_back_while([](int i) { return i >= 7; });`\n    for (const auto& db : drop_back_iterable) {\n        std::cout << db << ' ';\n    }\n    // 1 2 3 4 5 6\n    std::cout << '\\n';\n\n    // ------------------------------------- Trim string ----------------------------------------------\n    std::string trim_text = \"   Hello World!   \";\n    auto trimmed = lz::trim(trim_text); // or: `trim_text | lz::trim;`\n    for (const auto& t : trimmed) {\n        std::cout << t << ' ';\n    }\n    // Hello World!\n    std::cout << '\\n';\n\n    // ------------------------------------- Trim ----------------------------------------\n    std::vector<int> trim_numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n    auto trimmed_numbers = lz::trim(trim_numbers, [](int i) { return i < 3; }, [](int i) { return i > 7; });\n    // or: `trim_numbers | lz::trim([](int i) { return i < 3; }, [](int i) { return i > 7; });`\n    for (const auto& tn : trimmed_numbers) {\n        std::cout << tn << ' ';\n    }\n    // 3 4 5 6 7\n    std::cout << '\\n';\n\n    // ------------------------------------- Unzip with ----------------------------------------------------------\n    std::vector<std::tuple<int, int>> zipped = { std::make_tuple(1, 6), std::make_tuple(2, 7), std::make_tuple(3, 8) };\n    auto unzipped = lz::unzip_with(zipped, [](int a, int b) { return a + b; });\n    for (const auto& u : unzipped) {\n        std::cout << u << ' ';\n    }\n    // 7 9 11\n    std::cout << '\\n';\n\n    // ------------------------------------- iter_decay ----------------------------------------------------------\n    std::vector<int> decay_numbers = { 1, 2, 3, 4, 5 };\n    // decay_iterable is now a forward iterator, also returns a sentinel. If it were bidirectional, it would not return a\n    // sentinel.\n    auto decay_iterable = decay_numbers | lz::iter_decay(std::forward_iterator_tag{});\n\n#ifdef LZ_HAS_CXX_17\n    for (const auto& d : decay_iterable) {\n        std::cout << d << ' ';\n    }\n    // 1 2 3 4 5\n    std::cout << '\\n';\n#else\n    lz::for_each(decay_iterable, [](int i) { std::cout << i << ' '; });\n    // 1 2 3 4 5\n    std::cout << '\\n';\n#endif\n\n    // ------------------------------------- pad ----------------------------------------------------------\n    std::vector<int> pad_numbers = { 1, 2, 3 };\n    auto padded = pad_numbers | lz::pad(0, 2); // {1, 2, 3, 0, 0} by value\n\n#ifndef LZ_HAS_CXX_17\n    lz::for_each(padded, [](int p) { std::cout << p << ' '; });\n    // 1 2 3 0 0\n#else\n    for (const auto d : padded) {\n        std::cout << d << ' ';\n    }\n    // 1 2 3 0 0\n#endif\n    std::cout << '\\n';\n\n    auto to_pad = 3;\n    auto padded_ref = pad_numbers | lz::pad(std::ref(to_pad), 2); // {1, 2, 3, 3, 3} by reference\n#ifndef LZ_HAS_CXX_17\n    lz::for_each(padded_ref, [](std::reference_wrapper<int> p) { std::cout << p << ' '; });\n    // 1 2 3 3 3\n#else\n    for (std::reference_wrapper<int> p : padded_ref) {\n        std::cout << p << ' ';\n    }\n#endif\n    std::cout << '\\n';\n\n    // 1 2 3 3 3\n}\n"
  },
  {
    "path": "examples/join_where.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/join_where.hpp>\n#include <algorithm>\n#include <iostream>\n#include <vector>\n\nstruct customer {\n    int id;\n};\n\nstruct payment_bill {\n    int customer_id;\n    int id;\n};\n\nint main() {\n#ifdef LZ_HAS_CXX_17\n    std::vector<customer> customers{\n        customer{ 25 }, customer{ 1 }, customer{ 39 }, customer{ 103 }, customer{ 99 },\n    };\n    std::vector<payment_bill> payment_bills{\n        payment_bill{ 25, 0 }, payment_bill{ 25, 2 },  payment_bill{ 25, 3 },\n        payment_bill{ 99, 1 }, payment_bill{ 252, 1 }, payment_bill{ 252, 1 },\n    };\n\n    if (customers.size() > payment_bills.size()) {\n        std::sort(payment_bills.begin(), payment_bills.end(),\n                  [](const payment_bill& a, const payment_bill& b) { return a.customer_id < b.customer_id; });\n        auto joined = lz::join_where(\n            customers, payment_bills, [](const customer& p) { return p.id; }, [](const payment_bill& c) { return c.customer_id; },\n            [](const customer& p, const payment_bill& c) { return std::make_tuple(p, c); });\n\n        for (std::tuple<customer, payment_bill> join : joined) {\n            std::cout << std::get<0>(join).id << \" and \" << std::get<1>(join).customer_id\n                      << \" are the same. The corresponding payment bill id is \" << std::get<1>(join).id << '\\n';\n            /* Or use fmt::print(\"{} and {} are the same. The corresponding payment bill id is {}\\n\", std::get<0>(join).id,\n                                 std::get<1>(join).customer_id, std::get<1>(join).id); */\n        }\n        /*\n        Output:\n         25 and 25 are the same. The corresponding payment bill id is 0\n         25 and 25 are the same. The corresponding payment bill id is 2\n         25 and 25 are the same. The corresponding payment bill id is 3\n         99 and 99 are the same. The corresponding payment bill id is 1\n         */\n    }\n    // payment_bills sequence is larger, sort customers instead\n    else {\n        std::sort(customers.begin(), customers.end(), [](const customer& a, const customer& b) { return a.id < b.id; });\n        auto joined = lz::join_where(\n            payment_bills, customers, [](const payment_bill& p) { return p.customer_id; }, [](const customer& c) { return c.id; },\n            [](const payment_bill& p, const customer& c) { return std::make_tuple(p, c); });\n\n        for (std::tuple<payment_bill, customer> join : joined) {\n            std::cout << std::get<0>(join).customer_id << \" and \" << std::get<1>(join).id\n                      << \" are the same. The corresponding payment bill id is \" << std::get<0>(join).id << '\\n';\n            /* Or use fmt::print(\"{} and {} are the same. The corresponding payment bill id is {}\\n\",\n                                 std::get<1>(join).id, std::get<0>(join).customer_id, std::get<0>(join).id); */\n        }\n        /*\n         Output:\n         25 and 25 are the same. The corresponding payment bill id is 0\n         25 and 25 are the same. The corresponding payment bill id is 2\n         25 and 25 are the same. The corresponding payment bill id is 3\n         99 and 99 are the same. The corresponding payment bill id is 1\n         */\n    }\n\n    std::cout << '\\n';\n\n    // Of course, also the pipe syntax is supported\n    auto joined = payment_bills |\n                  lz::join_where(\n                      customers, [](const payment_bill& p) { return p.customer_id; }, [](const customer& c) { return c.id; },\n                      [](const payment_bill& p, const customer& c) { return std::make_tuple(p, c); });\n    for (std::tuple<payment_bill, customer> join : joined) {\n        std::cout << std::get<0>(join).customer_id << \" and \" << std::get<1>(join).id\n                  << \" are the same. The corresponding payment bill id is \" << std::get<0>(join).id << '\\n';\n    }\n    /*\n    Output:\n     25 and 25 are the same. The corresponding payment bill id is 0\n     25 and 25 are the same. The corresponding payment bill id is 2\n     25 and 25 are the same. The corresponding payment bill id is 3\n     99 and 99 are the same. The corresponding payment bill id is 1\n    */\n#else\n    std::vector<customer> customers{\n        customer{ 25 }, customer{ 1 }, customer{ 39 }, customer{ 103 }, customer{ 99 },\n    };\n    std::vector<payment_bill> payment_bills{\n        payment_bill{ 25, 0 }, payment_bill{ 25, 2 },  payment_bill{ 25, 3 },\n        payment_bill{ 99, 1 }, payment_bill{ 252, 1 }, payment_bill{ 252, 1 },\n    };\n\n    if (customers.size() > payment_bills.size()) {\n        std::sort(payment_bills.begin(), payment_bills.end(),\n                  [](const payment_bill& a, const payment_bill& b) { return a.customer_id < b.customer_id; });\n        auto joined = lz::join_where(\n            customers, payment_bills, [](const customer& p) { return p.id; }, [](const payment_bill& c) { return c.customer_id; },\n            [](const customer& p, const payment_bill& c) { return std::make_tuple(p, c); });\n\n        lz::for_each(joined, [](std::tuple<customer, payment_bill> join) {\n            std::cout << std::get<0>(join).id << \" and \" << std::get<1>(join).customer_id\n                      << \" are the same. The corresponding payment bill id is \" << std::get<1>(join).id << '\\n';\n            /* Or use fmt::print(\"{} and {} are the same. The corresponding payment bill id is {}\\n\", std::get<0>(join).id,\n                                 std::get<1>(join).customer_id, std::get<1>(join).id); */\n        });\n        /*\n        Output:\n         25 and 25 are the same. The corresponding payment bill id is 0\n         25 and 25 are the same. The corresponding payment bill id is 2\n         25 and 25 are the same. The corresponding payment bill id is 3\n         99 and 99 are the same. The corresponding payment bill id is 1\n         */\n    }\n    // payment_bills sequence is larger, sort customers instead\n    else {\n        std::sort(customers.begin(), customers.end(), [](const customer& a, const customer& b) { return a.id < b.id; });\n        auto joined = lz::join_where(\n            payment_bills, customers, [](const payment_bill& p) { return p.customer_id; }, [](const customer& c) { return c.id; },\n            [](const payment_bill& p, const customer& c) { return std::make_tuple(p, c); });\n\n        lz::for_each(joined, [](std::tuple<payment_bill, customer> join) {\n            std::cout << std::get<0>(join).customer_id << \" and \" << std::get<1>(join).id\n                      << \" are the same. The corresponding payment bill id is \" << std::get<0>(join).id << '\\n';\n            /* Or use fmt::print(\"{} and {} are the same. The corresponding payment bill id is {}\\n\",\n       std::get<1>(join).id, std::get<0>(join).customer_id, std::get<0>(join).id); */\n        });\n    }\n    /*\n     Output:\n     25 and 25 are the same. The corresponding payment bill id is 0\n     25 and 25 are the same. The corresponding payment bill id is 2\n     25 and 25 are the same. The corresponding payment bill id is 3\n     99 and 99 are the same. The corresponding payment bill id is 1\n     */\n\n    std::cout << '\\n';\n\n    // Of course, also the pipe syntax is supported\n    auto joined = payment_bills |\n                  lz::join_where(\n                      customers, [](const payment_bill& p) { return p.customer_id; }, [](const customer& c) { return c.id; },\n                      [](const payment_bill& p, const customer& c) { return std::make_tuple(p, c); });\n    lz::for_each(joined, [](std::tuple<payment_bill, customer> join) {\n        std::cout << std::get<0>(join).customer_id << \" and \" << std::get<1>(join).id\n                  << \" are the same. The corresponding payment bill id is \" << std::get<0>(join).id << '\\n';\n    });\n\n/*\nOutput:\n 25 and 25 are the same. The corresponding payment bill id is 0\n 25 and 25 are the same. The corresponding payment bill id is 2\n 25 and 25 are the same. The corresponding payment bill id is 3\n 99 and 99 are the same. The corresponding payment bill id is 1\n*/\n#endif\n}\n"
  },
  {
    "path": "examples/loop.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/loop.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n#ifdef LZ_HAS_CXX_17\n    std::vector<int> vec = { 1, 2, 3 };\n    for (int& i : lz::loop(vec, 2)) {\n        std::cout << i << ' ';\n    }\n    // Output: 1 2 3 1 2 3\n\n    std::cout << '\\n';\n\n    for (int& i : vec | lz::loop(2)) {\n        std::cout << i << ' ';\n    }\n\n    std::cout << '\\n';\n\n    std::size_t count = 0;\n    for (int& i : lz::loop(vec)) {\n        std::cout << i << ' ';\n        // Without break this would loop forever\n        ++count;\n        if (count == 500) {\n            break;\n        }\n    }\n    // Output: 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 ...\n\n    std::cout << '\\n';\n\n    count = 0;\n    for (int& i : vec | lz::loop) {\n        std::cout << i << ' ';\n        // Without break this would loop forever\n        ++count;\n        if (count == 500) {\n            break;\n        }\n    }\n    // Output: 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 ...\n#else\n    std::vector<int> vec = { 1, 2, 3 };\n    lz::for_each(lz::loop(vec, 2), [](int i) {\n        std::cout << i << ' ';\n    });\n    // Output: 1 2 3 1 2 3\n\n    std::cout << '\\n';\n\n    lz::for_each(vec | lz::loop(2), [](int i) {\n        std::cout << i << ' ';\n    });\n    // Output: 1 2 3 1 2 3\n\n    std::cout << '\\n';\n\n    std::size_t count = 0;\n\n    lz::for_each(lz::loop(vec), [&count](int i) {\n        std::cout << i << ' ';\n        // Without break this would loop forever\n        ++count;\n        if (count == 500) {\n            return false;\n        }\n        return true;\n    });\n    // Output: 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 ...\n\n    std::cout << '\\n';\n\n    count = 0;\n    lz::for_each(vec | lz::loop, [&count](int i) {\n        std::cout << i << ' ';\n        // Without break this would loop forever\n        ++count;\n        if (count == 500) {\n            return false;\n        }\n        return true;\n    });\n    // Output: 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 ...\n#endif\n}\n"
  },
  {
    "path": "examples/map.cpp",
    "content": "#include <Lz/map.hpp>\n#include <vector>\n#include <iostream>\n\n\nstruct some_struct {\n    std::string s;\n    int a;\n};\n\nint main() {\n    std::vector<some_struct> s = {\n        some_struct{\"Hello\"},\n        some_struct{\"World\"}\n    };\n\n    const auto mapper = lz::map(s, [](const some_struct& s) -> const std::string& { return s.s; });\n\n    for (const std::string& i : mapper) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: Hello World\n\n    std::cout << '\\n';\n    for (const std::string& i : s | lz::map([](const some_struct& s) -> const std::string& { return s.s; })) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: Hello World\n}"
  },
  {
    "path": "examples/maybe_owned.cpp",
    "content": "#include <Lz/map.hpp>\n#include <iostream>\n#include <vector>\n\nstruct non_lz_iterable {\n    const int* _begin{};\n    const int* _end{};\n\n    non_lz_iterable(const int* begin, const int* end) : _begin{ begin }, _end{ end } {\n    }\n\n    const int* begin() {\n        return _begin;\n    }\n    const int* end() {\n        return _end;\n    }\n};\n\nint main() {\n    // maybe_owned is a helper class that can be used to store a reference or copy of an iterable, depending on the type of the\n    // iterable.\n    // If the iterable is inherited from lazy_view, it will store a copy of the iterable. In all other cases, it will store a\n    // reference to the iterable.\n\n    const std::vector<int> vec{ 1, 2, 3 };\n    // will store a reference to the vector because it is not inherited from lazy_view\n    lz::maybe_owned<const std::vector<int>> maybe_owned{ vec };\n    std::cout << \"vec.begin() == maybe_owned.begin()? \" << std::boolalpha << (&(*vec.begin()) == &(*maybe_owned.begin()))\n              << '\\n'; // true\n    static_assert(lz::maybe_owned<std::vector<int>>::holds_reference, \"Iterable should hold a reference\");\n\n    non_lz_iterable it{ vec.data(), vec.data() + vec.size() };\n    // will store a reference to the vector because it is not inherited from lazy_view\n    lz::maybe_owned<non_lz_iterable> ref{ it };\n    static_assert(lz::maybe_owned<non_lz_iterable>::holds_reference, \"Iterable should hold a reference\");\n\n    // Sometimes, you don't want this behaviour, for example for other cheap to copy, non lz iterables (like `non_lz_iterable`).\n    // In this case, you can use lz::copied/lz::as_copied to store a copy of the iterable, instead of a\n    // reference.\n    lz::copied<non_lz_iterable> copied{ it }; // will *not* store a reference to the non lz iterable!\n    static_assert(!lz::copied<non_lz_iterable>::holds_reference, \"Iterable should hold a reference\");\n\n    // You can also use the helper function\n    copied = lz::as_copied(it); // will *not* store a copy of the non lz iterable\n    static_assert(!lz::maybe_owned<decltype(copied)>::holds_reference, \"Iterable should hold a reference\");\n}\n"
  },
  {
    "path": "examples/pairwise.cpp",
    "content": "#include <Lz/pairwise.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> vec1{ 1, 2, 3, 4, 5 };\n\n    for (auto iterable : lz::pairwise(vec1, 2)) {\n        for (auto v : iterable) {\n            std::cout << v << ' ';\n        }\n        std::cout << '\\n';\n    }\n\n    // Output\n    // 1 2\n    // 2 3\n    // 3 4\n    // 4 5\n}\n"
  },
  {
    "path": "examples/pipe.cpp",
    "content": "#include <Lz/algorithm/all_of.hpp>\n#include <Lz/drop.hpp>\n#include <Lz/iter_tools.hpp>\n#include <Lz/map.hpp>\n#include <Lz/take.hpp>\n#include <fmt/core.h>\n\nint main() {\n    int arr[]{ 3, 2, 4, 5 };\n    // the example below doesn't do anything special in particular, but should give an example on how to use chaining\n\n#ifndef LZ_HAS_CXX_11\n\n    // clang-format off\n    const auto iterable = arr\n        // take all elements\n        | lz::take(lz::distance(std::begin(arr), std::end(arr)))\n        // drop the first 0 elements\n        | lz::drop(0)\n        // add 1 to each\n        | lz::map([](int i) { return i + 1; })\n        // cast it to int\n        | lz::as<int>;\n    // clang-format on\n\n    const auto all_greater_five = lz::all_of(iterable, [](int i) { return i >= 5; });\n    fmt::print(\"{}\\n\", all_greater_five); // prints false\n\n#else\n\n    // clang-format off\n    const auto iterable = arr\n        // take all elements\n        | lz::take(std::distance(std::begin(arr), std::end(arr)))\n        // drop the first 0 elements\n        | lz::drop(0)\n        // add 1 to each\n        | lz::map([](int i) { return i + 1; })\n        // cast it to int\n        | lz::as<int>{};\n    // clang-format on\n\n    const auto all_greater_five = lz::all_of(iterable, [](int i) { return i >= 5; });\n    fmt::print(\"{}\\n\", all_greater_five); // prints false\n\n#endif\n}\n"
  },
  {
    "path": "examples/print_and_format.cpp",
    "content": "#include <Lz/stream.hpp>\n#include <Lz/basic_iterable.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> v = { 1, 2, 3, 4, 5 };\n\n    std::string output = lz::format(v); // 1, 2, 3, 4, 5\n    std::cout << output << '\\n';\n\n    output = lz::format(v, \" \"); // 1 2 3 4 5\n    std::cout << output << '\\n';\n\n    output = v | lz::format; // 1, 2, 3, 4, 5\n    std::cout << output << '\\n';\n\n    output = v | lz::format(\" \"); // 1 2 3 4 5\n    std::cout << output << '\\n';\n\n    lz::format(v, std::cout); // 1, 2, 3, 4, 5\n    std::cout << '\\n';\n\n    lz::format(v, std::cout, \" \"); // 1 2 3 4 5\n    std::cout << '\\n';\n\n    v | lz::format(std::cout); // 1, 2, 3, 4, 5\n    std::cout << '\\n';\n\n    v | lz::format(std::cout, \" \"); // 1 2 3 4 5\n    std::cout << '\\n';\n\n    // If you're using fmt or have std::format\n#if !defined(LZ_STANDALONE) || defined(LZ_HAS_FORMAT)\n    output = lz::format(v, \", \", \"{:02d}\"); // 01, 02, 03, 04, 05\n    std::cout << output << '\\n';\n\n    output = v | lz::format(\", \", \"{:02d}\"); // 01, 02, 03, 04, 05\n    std::cout << output << '\\n';\n\n    lz::format(v, std::cout, \", \", \"{:02d}\"); // 01, 02, 03, 04, 05\n    std::cout << '\\n';\n\n    v | lz::format(std::cout, \", \", \"{:02d}\"); // 01, 02, 03, 04, 05\n    std::cout << '\\n';\n#endif\n\n    std::cout << lz::basic_iterable<decltype(v.begin())>{ v.begin(), v.end() } << '\\n'; // 1, 2, 3, 4, 5\n}\n"
  },
  {
    "path": "examples/random.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/random.hpp>\n#include <iostream>\n\nint main() {\n    const float min = 0;\n    const float max = 1;\n    const size_t amount = 4;\n    // Both use std::std::mt19937 as default random engine\n    const auto rng = lz::random(min, max, amount); // return sentinel pair\n    const auto rng_common = lz::common_random(min, max, amount); // return common iterator\n\n#ifdef LZ_HAS_CXX_17\n    for (float i : rng) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: random number between [0, 1] random number between [0, 1] random number between [0, 1] random number between [0, 1]\n\n    for (float i : rng_common) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: random number between [0, 1] random number between [0, 1] random number between [0, 1] random number between [0, 1]\n\n    std::cout << '\\n';\n    // Or create your own\n    static std::random_device rd;\n    std::mt19937_64 gen(rd());\n    std::poisson_distribution<> d(500000);\n    auto r = lz::random(d, gen, 3);\n\n    for (int i : r) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: [random number] [random number] [random number]\n#else\n    lz::for_each(rng, [](float i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n    // Output: random number between [0, 1] random number between [0, 1] random number between [0, 1] random number between [0, 1]\n\n    lz::for_each(rng_common, [](float i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n\n\n    std::cout << '\\n';\n\n    // Or create your own\n    static std::random_device rd;\n    std::mt19937_64 gen(rd());\n    std::poisson_distribution<> d(500000);\n    auto r = lz::random(d, gen, 3);\n\n    lz::for_each(r, [](int i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n// Output: [random number] [random number] [random number]\n#endif\n}\n"
  },
  {
    "path": "examples/range.cpp",
    "content": "#include <Lz/range.hpp>\n#include <iostream>\n\n\nint main() {\n    for (int i : lz::range(3)) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 0 1 2\n\n    std::cout << '\\n';\n\n    for (int i : lz::range(1, 4)) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3\n\n    std::cout << '\\n';\n\n    for (double i : lz::range(1.0, 4.0, 0.5)) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 1 1.5 2 2.5 3 3.5\n}"
  },
  {
    "path": "examples/regex_split.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/regex_split.hpp>\n#include <iostream>\n#include <string>\n\nint main() {\n    std::string input = \"Hello,World!This,is,a,test\";\n    std::regex r(\",\");\n    auto result = lz::regex_split(input, r);\n\n#ifdef LZ_HAS_CXX_17\n    for (const lz::string_view word : result) {\n        // Use .write if using fmt::string_view\n        std::cout.write(word.data(), word.size());\n        std::cout << ' ';\n        // Or use fmt::print(\"{}\\n\", word);\n    }\n    // output: Hello World!This is a test\n\n    std::cout << '\\n';\n\n    for (const lz::string_view word : input | lz::regex_split(r)) {\n        // Use .write if using fmt::string_view\n        std::cout.write(word.data(), word.size());\n        std::cout << ' ';\n        // Or use fmt::print(\"{}\\n\", word);\n    }\n    // output: Hello World!This is a test\n#else\n    lz::for_each(result, [](const lz::string_view word) {\n        // Use .write if using fmt::string_view\n        std::cout.write(word.data(), word.size());\n        std::cout << ' ';\n        // Or use fmt::print(\"{}\\n\", word);\n    });\n    // output: Hello World!This is a test\n\n    std::cout << '\\n';\n\n    lz::for_each(input | lz::regex_split(r), [](const lz::string_view word) {\n        // Use .write if using fmt::string_view\n        std::cout.write(word.data(), word.size());\n        std::cout << ' ';\n        // Or use fmt::print(\"{}\\n\", word);\n    });\n    // output: Hello World!This is a test\n#endif\n}\n"
  },
  {
    "path": "examples/repeat.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/repeat.hpp>\n#include <iostream>\n\nint main() {\n    // Example usage of lz::repeat, yields elements by value\n    const auto to_repeat = 155;\n    const auto amount = 4;\n    const auto repeater_n = lz::repeat(to_repeat, amount);\n    const auto repeater_inf = lz::repeat(to_repeat);\n\n#ifdef LZ_HAS_CXX_17\n    for (int i : repeater_n) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 155 155 155 155\n\n    for (const auto i : repeater_inf) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 155 155 155 155 ...\n#else\n    lz::for_each(repeater_n, [](int i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n    // Output: 155 155 155 155\n\n    lz::for_each(repeater_inf, [](int i) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    });\n    // Output: 155 155 155 155 ...\n#endif\n}\n"
  },
  {
    "path": "examples/rotate.cpp",
    "content": "#include <Lz/rotate.hpp>\n#include <vector>\n#include <iostream>\n\nint main() {\n    std::vector<int> v = { 1, 2, 3, 4, 5 };\n    for (int& i : lz::rotate(v, 2)) {\n        std::cout << i << \" \";\n        // Or fmt::print(\"{} \", i);\n    }\n    // Output: 3 4 5 1 2\n\n    std::cout << '\\n';\n\n    for (int& i : v | lz::rotate(2)) {\n        std::cout << i << \" \";\n        // Or fmt::print(\"{} \", i);\n    }\n    // Output: 3 4 5 1 2\n}\n"
  },
  {
    "path": "examples/slice.cpp",
    "content": "#include <Lz/slice.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> v = { 1, 2, 3, 4, 5 };\n\n    auto s = lz::slice(v, 1, 3);\n    for (int& i : s) {\n        std::cout << i << \" \";\n        // Or fmt::print(\"{} \", i);\n    }\n    // Output: 2 3\n\n    std::cout << '\\n';\n\n    for (int& i : v | lz::slice(1, 3)) {\n        std::cout << i << \" \";\n        // Or fmt::print(\"{} \", i);\n    }\n    // Output: 2 3\n}\n"
  },
  {
    "path": "examples/split.cpp",
    "content": "#include <Lz/algorithm/for_each.hpp>\n#include <Lz/split.hpp>\n#include <Lz/util/string_view.hpp>\n#include <array>\n#include <iostream>\n#include <vector>\n\nint main() {\n    // With split you can split an iterable on a delimiter or an iterable delimiter (multiple delimiters)\n    // The starting point is: if the delimiter is a single value (so no iterable) a string_view or a const char* (any char) type\n    // then the delimiter does not have to be by reference.\n    // If the delimiter is a non-lz iterable (like a std::string or a std::vector) then it has to be by reference.\n#ifdef LZ_HAS_CXX_17\n    std::string to_split = \"Hello world \";\n    std::string delim = \" \";\n    // sv_split returns a string_view splitter\n    // delim has to be by reference, this does not count for const char*/c_string iterator\n    const auto splitter = lz::sv_split(to_split, delim);\n\n    std::cout << \"Using string_views:\\n\";\n\n    for (lz::string_view substring : splitter) {\n        const auto substring_size = static_cast<std::streamsize>(substring.size());\n        std::cout.write(substring.data(), substring_size);\n        std::cout << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    }\n    // Output: Hello world\n\n    std::cout << '\\n';\n    // const char* doesn't have to be by reference\n    for (lz::string_view substring : to_split | lz::sv_split(\" \")) {\n        const auto substring_size = static_cast<std::streamsize>(substring.size());\n        std::cout.write(substring.data(), substring_size);\n        std::cout << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    }\n    // Output: Hello world\n\n    std::cout << \"\\n\\nUsing strings:\\n\";\n    // const char* doesn't have to be by reference\n    const auto splitter2 = lz::s_split(to_split, \" \");\n    for (std::string substring : splitter2) {\n        std::cout << substring << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    }\n    // Output: Hello world\n    std::cout << '\\n';\n    // const char* doesn't have to be by reference\n    for (std::string substring : to_split | lz::s_split(\" \")) {\n        std::cout << substring << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    }\n    // Output: Hello world\n\n    std::cout << \"\\n\\nUsing other container types:\\n\";\n    std::vector<int> to_split2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n    std::array<int, 1> to_split_on = { 5 };\n    // to_split_on must be by reference!\n    const auto splitter3 = lz::split(to_split2, to_split_on);\n    // Returns an iterable of iterables\n    for (auto splitted : splitter3) {\n        for (auto& i : splitted) {\n            std::cout << i << ' ';\n            // Or use fmt::print(\"{} \", i);\n        }\n        std::cout << '\\n';\n    }\n    // Output:\n    // 1 2 3 4\n    // 6 7 8 9\n\n    // With custom types\n    std::string to_split3 = \"Hello world \";\n    std::string delim2 = \" \";\n    // delim must be by reference\n    auto splitter4 = to_split3 | lz::t_split<std::vector<char>>(delim2);\n    for (std::vector<char> substring : splitter4) {\n        const auto substring_size = static_cast<std::streamsize>(substring.size());\n        std::cout.write(substring.data(), substring_size);\n        std::cout << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    }\n    // Output: Hello world\n    std::cout << '\\n';\n\n    // All of the same rules also apply for single arguments:\n\n    std::string to_split4 = \"hello world\";\n    // delim doesn't have to be by reference\n    auto splitter_single = lz::sv_split(to_split4, ' ');\n    for (lz::string_view substring : splitter_single) {\n        const auto substring_size = static_cast<std::streamsize>(substring.size());\n        std::cout.write(substring.data(), substring_size);\n        std::cout << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    }\n#else\n    std::string to_split = \"Hello world \";\n    std::string delim = \" \";\n    // sv_split returns a string_view splitter\n    // delim must be by reference\n    const auto splitter = lz::sv_split(to_split, delim);\n\n    std::cout << \"Using string_views:\\n\";\n\n    lz::for_each(splitter, [](lz::string_view substring) {\n        const auto substring_size = static_cast<std::streamsize>(substring.size());\n        std::cout.write(substring.data(), substring_size);\n        std::cout << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    });\n    // Output: Hello world\n\n    std::cout << '\\n';\n\n    lz::for_each(splitter, [](lz::string_view substring) {\n        const auto substring_size = static_cast<std::streamsize>(substring.size());\n        std::cout.write(substring.data(), substring_size);\n        std::cout << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    });\n    // Output: Hello world\n\n    std::cout << \"\\n\\nUsing strings:\\n\";\n    // const char* doesn't have to be by reference\n    const auto splitter2 = lz::s_split(to_split, \" \");\n    lz::for_each(splitter2, [](std::string substring) {\n        std::cout << substring << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    });\n    // Output: Hello world\n    std::cout << '\\n';\n\n    lz::for_each(splitter2, [](std::string substring) {\n        std::cout << substring << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    });\n    // Output: Hello world\n\n    std::cout << \"\\n\\nUsing other container types:\\n\";\n    std::vector<int> to_split2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n    std::array<int, 1> to_split_on = { 5 };\n    // to_split_on must be by reference!\n    const auto splitter3 = lz::split(to_split2, to_split_on);\n    // Returns an iterable of iterables\n    lz::for_each(splitter3, [](lz::basic_iterable<std::vector<int>::iterator> splitted) {\n        for (int i : splitted) {\n            std::cout << i << ' ';\n            // Or use fmt::print(\"{} \", i);\n        }\n        std::cout << '\\n';\n    });\n    // Output:\n    // 1 2 3 4\n    // 6 7 8 9\n\n    // With custom types\n    std::string to_split3 = \"Hello world \";\n    std::string delim2 = \" \";\n#ifdef LZ_HAS_CXX_11\n    // delim2 must be by reference\n    auto splitter4 = to_split3 | lz::t_split<std::vector<char>>{}(delim2);\n#else\n    // delim2 must be by reference\n    auto splitter4 = to_split3 | lz::t_split<std::vector<char>>(delim2);\n#endif\n    lz::for_each(splitter4, [](std::vector<char> substring) {\n        const auto substring_size = static_cast<std::streamsize>(substring.size());\n        std::cout.write(substring.data(), substring_size);\n        std::cout << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    });\n    // Output: Hello world\n    std::cout << '\\n';\n\n    std::string to_split4 = \"hello world\";\n    // delim doesn't have to be by reference\n    auto splitter_single = lz::sv_split(to_split4, ' ');\n    lz::for_each(splitter_single, [](lz::string_view substring) {\n        const auto substring_size = static_cast<std::streamsize>(substring.size());\n        std::cout.write(substring.data(), substring_size);\n        std::cout << ' ';\n        // Or use fmt::print(\"{} \", substring);\n    });\n#endif\n}\n"
  },
  {
    "path": "examples/take.cpp",
    "content": "#include <Lz/take.hpp>\n#include <vector>\n#include <iostream>\n\n\nint main() {\n    std::vector<int> seq = { 1, 2, 3, 4, 5, 6 };\n\n    auto taken = lz::take(seq, 3);\n    for (int& i : taken) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3\n\n    std::cout << '\\n';\n\n    for (int& i : seq | lz::take(3)) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n\n    // Output: 1 2 3\n\n    auto taken_iter = lz::take(seq.begin(), 3);\n    for (int& i : taken_iter) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3\n    std::cout << '\\n';\n}"
  },
  {
    "path": "examples/take_every.cpp",
    "content": "#include <Lz/take_every.hpp>\r\n#include <iostream>\r\n#include <vector>\r\n\r\nint main() {\r\n    std::vector<int> sequence = {1, 2, 3, 4, 5};\r\n    auto take_every = lz::take_every(sequence, 2);\r\n\r\n    for (int& i : take_every) {\r\n        std::cout << i << ' ';\r\n        // Or use fmt::print(\"{} \", i);\r\n    }\r\n    // Output: 1 3 5\r\n\r\n    std::cout << '\\n';\r\n\r\n    take_every = lz::take_every(sequence, 2, 1);\r\n\r\n    for (int& i : take_every) {\r\n        std::cout << i << ' ';\r\n        // Or use fmt::print(\"{} \", i);\r\n    }\r\n    // Output: 2 4 6\r\n\r\n    std::cout << '\\n';\r\n\r\n    for (int& i : sequence | lz::take_every(2)) {\r\n        std::cout << i << ' ';\r\n        // Or use fmt::print(\"{} \", i);\r\n    }\r\n    // Output: 1 3 5\r\n}"
  },
  {
    "path": "examples/take_while.cpp",
    "content": "#include <Lz/take_while.hpp>\n#include <iostream>\n#include <vector>\n\nint main() {\n    std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n\n    auto taken = lz::take_while(vec, [](int i) { return i < 4; });\n\n    for (int& i : taken) {\n        std::cout << i << ' ';\n        // or use fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3\n\n    std::cout << '\\n';\n\n    for (int& i : vec | lz::take_while([](int i) { return i < 4; })) {\n        std::cout << i << ' ';\n        // or use fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3\n}"
  },
  {
    "path": "examples/to_container.cpp",
    "content": "#include <Lz/algorithm/transform.hpp>\r\n#include <Lz/generate.hpp>\r\n#include <Lz/map.hpp>\r\n#include <Lz/procs/to.hpp>\r\n#include <iostream>\r\n#include <list>\r\n#include <map>\r\n#include <set>\r\n#include <vector>\r\n\r\n// In case you have a custom container, you can specialize `lz::custom_copier_for` to copy the elements to your container.\r\n// This is useful if you have a custom container that requires a specific way of copying elements. You will need to specialize\r\n// `lz::custom_copier_for` for your custom container if all of the following are true:\r\n// - Your custom container does not have a `push_back` method\r\n// - Your custom container does not have an `insert` method\r\n// - Your custom container does not have an `insert_after` method (implicitly also requires `before_begin`)\r\n// - Your custom container does not have a `push` method\r\n// - Your custom container is not std::array\r\ntemplate<class T>\r\nclass custom_container {\r\n    std::vector<T> _vec;\r\n\r\npublic:\r\n    void reserve(std::size_t size) {\r\n        _vec.reserve(size);\r\n    }\r\n\r\n    std::vector<T>& vec() {\r\n        return _vec;\r\n    }\r\n};\r\n\r\n// Specialize `lz::custom_copier_for` for your custom container\r\ntemplate<class T>\r\nstruct lz::custom_copier_for<custom_container<T>> {\r\n    template<class Iterable>\r\n    void copy(Iterable&& iterable, custom_container<T>& container) const {\r\n        container.reserve(iterable.size());\r\n        // Copy the contents of the iterable to the container. Container is not yet reserved\r\n        lz::copy(std::forward<Iterable>(iterable), std::back_inserter(container.vec()));\r\n    }\r\n};\r\n\r\nint main() {\r\n    char c = 'a';\r\n    auto generator = lz::generate(\r\n        [&c]() {\r\n            auto tmp = c;\r\n            ++c;\r\n            return tmp;\r\n        },\r\n        4);\r\n\r\n    // To vector:\r\n    auto vec = generator | lz::to<std::vector>();\r\n    for (char& val : vec) {\r\n        std::cout << val << ' ';\r\n    }\r\n    c = 'a';\r\n    // Output: a b c d\r\n    std::cout << '\\n';\r\n\r\n    // To set\r\n    auto set = generator | lz::to<std::set>();\r\n    for (char val : set) {\r\n        std::cout << val << ' ';\r\n    }\r\n    c = 'a';\r\n    // Output: a b c d\r\n    std::cout << '\\n';\r\n\r\n    // To list\r\n    auto list = generator | lz::to<std::list<char>>();\r\n    for (char val : set) {\r\n        std::cout << val << ' ';\r\n    }\r\n    c = 'a';\r\n    // Output: a b c d\r\n    std::cout << \"\\n\\n\";\r\n\r\n    // To map\r\n    auto map = generator | lz::map([](const char c) { return std::make_pair(static_cast<char>(c + 1), c); }) |\r\n               lz::to<std::map<char, char>>();\r\n    for (std::pair<char, char> pair : map) {\r\n        std::cout << pair.first << ' ' << pair.second << '\\n';\r\n    }\r\n    c = 'a';\r\n    // Output:\r\n    // b a\r\n    // c b\r\n    // d c\r\n    // e d\r\n\r\n    std::cout << '\\n';\r\n\r\n    std::vector<char> copy_to_vec;\r\n    lz::copy(generator, std::back_inserter(copy_to_vec));\r\n    for (char val : copy_to_vec) {\r\n        std::cout << val << ' ';\r\n    }\r\n    c = 'a';\r\n    // Output: a b c d\r\n\r\n    std::cout << '\\n';\r\n\r\n    std::vector<int> transform_to_vec;\r\n    lz::transform(generator, std::back_inserter(transform_to_vec), [](const char i) -> char { return i + 1; });\r\n    for (int val : transform_to_vec) {\r\n        std::cout << val << ' ';\r\n    }\r\n    c = 'a';\r\n    // Output: 98 99 100 101\r\n\r\n    std::cout << '\\n';\r\n\r\n    auto out = generator | lz::map([](char c) -> char { return c + 1; }) | lz::to<std::vector>();\r\n    for (char val : out) {\r\n        std::cout << val << ' ';\r\n    }\r\n    // Output: b c d e\r\n\r\n    auto custom = generator | lz::to<custom_container<char>>();\r\n    for (char val : custom.vec()) {\r\n        std::cout << val << ' ';\r\n    }\r\n    // Output: a b c d\r\n}\r\n"
  },
  {
    "path": "examples/unique.cpp",
    "content": "#include <Lz/unique.hpp>\n#include <iostream>\n#include <vector>\n#include <algorithm>\n\nint main() {\n    std::vector<int> vector = {5, 3, 2, 5, 6, 42, 2, 3, 56, 3, 1, 12, 3};\n    std::sort(vector.begin(), vector.end());\n\n    // operator< is required or add a custom operator < in its second parameter\n    const auto unique = lz::unique(vector);\n\n    for (int& i : unique) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3 4 5 6 12 42 56\n\n    std::cout << '\\n';\n\n    // operator< is required or add a custom operator < in its first parameter\n    for (int& i : vector | lz::unique) {\n        std::cout << i << ' ';\n        // Or use fmt::print(\"{} \", i);\n    }\n    // Output: 1 2 3 4 5 6 12 42 56\n}"
  },
  {
    "path": "examples/zip.cpp",
    "content": "#include <Lz/zip.hpp>\n#include <vector>\n#include <iostream>\n\n\nint main() {\n    std::vector<int> a = {1, 2, 3, 4};\n    std::vector<int> b = {1, 2, 3};\n\t\n    for (std::tuple<int&, int&> tup : lz::zip(a, b)) {\n        std::cout << std::get<0>(tup) << ' ' << std::get<1>(tup) << '\\n';\n    }\n    // Yields:\n    // 1 1\n    // 2 2\n    // 3 3\n\n    std::cout << '\\n';\n\n    for (std::tuple<int&, int&> tup : a | lz::zip(b)) {\n        std::cout << std::get<0>(tup) << ' ' << std::get<1>(tup) << '\\n';\n    }\n    // Yields:\n    // 1 1\n    // 2 2\n    // 3 3\n}"
  },
  {
    "path": "examples/zip_longest.cpp",
    "content": "#include <Lz/zip_longest.hpp>\n#include <iostream>\n#include <list>\n#include <vector>\n\nint main() {\n    std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };\n    std::list<char> v2 = { 'h', 'e', 'l', 'l' };\n    std::vector<int> v3 = { 1, 2, 3 };\n\n    // iterator is now lowest iterator category of the three iterators. In this case, it is std::bidi_iterator_tag because v2 is a\n    // list<char> which has a bidirectional iterator.\n    for (auto&& tup : lz::zip_longest(v1, v2, v3)) { // Elements are accessed by reference (std::reference_wrapper)\n        auto&& first = std::get<0>(tup);\n        auto&& second = std::get<1>(tup);\n        auto&& third = std::get<2>(tup);\n\n        if (first) {\n            std::cout << *first << ' ';\n        }\n        if (second) {\n            std::cout << *second << ' ';\n        }\n        if (third) {\n            std::cout << *third << ' ';\n        }\n        std::cout << '\\n';\n    }\n    // Output:\n    // 1 h 1\n    // 2 e 2\n    // 3 l 3\n    // 4 l\n    // 5\n    // 6\n\n    std::cout << '\\n';\n\n    // iterator is now lowest iterator category of the three iterators. In this case, it is std::bidi_iterator_tag because v2 is a\n    // list<char> which has a bidirectional iterator.\n    for (auto&& tup : v1 | lz::zip_longest(v2, v3)) { // Elements are accessed by reference (std::reference_wrapper)\n        auto&& first = std::get<0>(tup);\n        auto&& second = std::get<1>(tup);\n        auto&& third = std::get<2>(tup);\n\n        if (first) {\n            std::cout << *first << ' ';\n        }\n        if (second) {\n            std::cout << *second << ' ';\n        }\n        if (third) {\n            std::cout << *third << ' ';\n        }\n        std::cout << '\\n';\n    }\n    // Output:\n    // 1 h 1\n    // 2 e 2\n    // 3 l 3\n    // 4 l\n    // 5\n    // 6\n}"
  },
  {
    "path": "include/Lz/algorithm/accumulate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_ACCUMULATE_HPP\n#define LZ_ALGORITHM_ACCUMULATE_HPP\n\n#include <Lz/detail/algorithm/accumulate.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\n#ifdef LZ_HAS_CXX_20\n#include <numeric>\n#else\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Accumulates the values in the range [begin, end) using the binary operator @p unary_predicate\n *\n * @param iterable The iterable to accumulate\n * @param init The initial value to start accumulating with\n * @param unary_predicate The binary operator to accumulate the values with\n * @return The accumulated value\n */\ntemplate<class Iterable, class T, class UnaryPredicate = LZ_BIN_OP(plus, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 T accumulate(Iterable&& iterable, T init, UnaryPredicate unary_predicate = {}) {\n    return detail::accumulate(detail::begin(iterable), detail::end(iterable), std::move(init), std::move(unary_predicate));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/adjacent_find.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_ADJACENT_FIND_HPP\n#define LZ_ALGORITHM_ADJACENT_FIND_HPP\n\n#include <Lz/detail/algorithm/adjacent_find.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/traits/iter_type.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the first occurrence of two adjacent elements in the range [begin(iterable), end(iterable)) that satisfy the\n * binary binary_predicate @p binary_predicate\n *\n * @param iterable The iterable to search in\n * @param binary_predicate The binary binary_predicate to search with\n * @return Iterator to the first occurrence of two adjacent elements that satisfy the binary binary_predicate or\n * `end(iterable)` if the elements are not found\n */\ntemplate<class Iterable, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> adjacent_find(Iterable&& iterable, BinaryPredicate binary_predicate = {}) {\n    return detail::adjacent_find(detail::begin(iterable), detail::end(iterable), std::move(binary_predicate));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/algorithm.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_UNDERSCORE_HPP\n#define LZ_ALGORITHM_UNDERSCORE_HPP\n\n#include <Lz/algorithm/accumulate.hpp>\n#include <Lz/algorithm/adjacent_find.hpp>\n#include <Lz/algorithm/all_of.hpp>\n#include <Lz/algorithm/any_of.hpp>\n#include <Lz/algorithm/back.hpp>\n#include <Lz/algorithm/back_or.hpp>\n#include <Lz/algorithm/binary_search.hpp>\n#include <Lz/algorithm/contains.hpp>\n#include <Lz/algorithm/count.hpp>\n#include <Lz/algorithm/copy.hpp>\n#include <Lz/algorithm/count_if.hpp>\n#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/ends_with.hpp>\n#include <Lz/algorithm/is_sorted.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/nth.hpp>\n#include <Lz/algorithm/find.hpp>\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/algorithm/find_if_not.hpp>\n#include <Lz/algorithm/find_last.hpp>\n#include <Lz/algorithm/find_last_if.hpp>\n#include <Lz/algorithm/find_last_if_not.hpp>\n#include <Lz/algorithm/find_last_or_default.hpp>\n#include <Lz/algorithm/find_last_or_default_if.hpp>\n#include <Lz/algorithm/find_last_or_default_if_not.hpp>\n#include <Lz/algorithm/find_last_or_default_not.hpp>\n#include <Lz/algorithm/find_or_default.hpp>\n#include <Lz/algorithm/find_or_default_if.hpp>\n#include <Lz/algorithm/for_each.hpp>\n#include <Lz/algorithm/for_each_while.hpp>\n#include <Lz/algorithm/for_each_while_n.hpp>\n#include <Lz/algorithm/front.hpp>\n#include <Lz/algorithm/front_or.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/algorithm/index_of.hpp>\n#include <Lz/algorithm/index_of_if.hpp>\n#include <Lz/algorithm/lower_bound.hpp>\n#include <Lz/algorithm/max_element.hpp>\n#include <Lz/algorithm/mean.hpp>\n#include <Lz/algorithm/min_element.hpp>\n#include <Lz/algorithm/none_of.hpp>\n#include <Lz/algorithm/partition.hpp>\n#include <Lz/algorithm/peek.hpp>\n#include <Lz/algorithm/search.hpp>\n#include <Lz/algorithm/starts_with.hpp>\n#include <Lz/algorithm/transform.hpp>\n#include <Lz/algorithm/upper_bound.hpp>\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/all_of.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_ALL_OF_HPP\n#define LZ_ALGORITHM_ALL_OF_HPP\n\n#include <Lz/algorithm/find_if_not.hpp>\n#include <Lz/detail/compiler_config.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Checks if all elements in the range [begin(iterable), end(iterable)) satisfy the unary_predicate @p\n unary_predicate\n *\n * @param iterable The iterable to check\n * @param unary_predicate The unary_predicate to call whether all elements satisfy this condition\n * @return true if all elements satisfy the unary_predicate, false otherwise\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 bool all_of(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return lz::find_if_not(std::forward<Iterable>(iterable), std::move(unary_predicate)) == detail::end(iterable);\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/any_of.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_ANY_OF_HPP\n#define LZ_ALGORITHM_ANY_OF_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Checks whether any of the elements in the range [begin(iterable), end(iterable)) satisfy the unary_predicate @p\n * unary_predicate\n *\n * @param iterable The iterable to check\n * @param unary_predicate The unary_predicate to call whether any of the elements satisfy this condition\n * @return true if any of the elements satisfy the unary_predicate, false otherwise\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 bool any_of(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return lz::find_if(std::forward<Iterable>(iterable), std::move(unary_predicate)) != detail::end(iterable);\n}\n\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/back.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_BACK_HPP\n#define LZ_ALGORITHM_BACK_HPP\n\n#include <Lz/detail/algorithm/back.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Returns a reference to the last element in the given iterable. If the iterable is not a random access or\n * bidirectional iterable, this function will iterate through the entire iterable to find the last element. If the iterable is\n * empty, the behavior is undefined.\n * @param iterable The iterable to get the last element from.\n * @return A reference to the last element in the iterable.\n */\ntemplate<class Iterable>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::ref_iterable_t<Iterable> back(Iterable&& iterable) {\n    return detail::back(detail::begin(iterable), detail::end(iterable));\n}\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/back_or.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_BACK_OR_HPP\n#define LZ_ALGORITHM_BACK_OR_HPP\n\n#include <Lz/algorithm/back.hpp>\n#include <Lz/algorithm/empty.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * This function returns the end element. If the sequence is empty, it returns `value`.\n * @param iterable The iterable to get the end value of, or `value` in case it is empty.\n * @param default_value The value to return if `iterable` is empty.\n * @return Either the end element of `iterable` or `value` if the sequence is empty.\n */\ntemplate<class Iterable, class T>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::val_iterable_t<Iterable> back_or(Iterable&& iterable, T&& default_value) {\n    return lz::empty(iterable) ? std::forward<T>(default_value) : lz::back(iterable);\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/binary_search.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_BINARY_SEARCH_HPP\n#define LZ_ALGORITHM_BINARY_SEARCH_HPP\n\n#include <Lz/algorithm/lower_bound.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Searches for the first occurrence of the value @p value in the range [begin(iterable), end(iterable)) using the\n * binary search algorithm. @p iterable must be sorted beforehand.\n * @param iterable The iterable to search in\n * @param value The value to search for\n * @param binary_predicate Predicate to search the value with\n * @return true if the value is found, false otherwise\n */\ntemplate<class Iterable, class T, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 bool binary_search(Iterable&& iterable, const T& value, BinaryPredicate binary_predicate = {}) {\n    auto it = lz::lower_bound(iterable, value, binary_predicate);\n    return it != detail::end(iterable) && !binary_predicate(value, *it);\n}\n} // namespace lz\n\n#endif // LZ_ALGORITHM_BINARY_SEARCH_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/contains.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORTHM_CONTAINS_HPP\n#define LZ_ALGORTHM_CONTAINS_HPP\n\n#include <Lz/algorithm/find.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Checks if the range [begin(iterable), end(iterable)) contains the value @p value\n *\n * @param iterable The iterable to check\n * @param value The value to check for\n * @return `true` if the value is found, `false` otherwise\n */\ntemplate<class Iterable, class T>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 bool contains(Iterable&& iterable, const T& value) {\n    return lz::find(std::forward<Iterable>(iterable), value) != detail::end(iterable);\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/copy.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_COPY_HPP\n#define LZ_ALGORITHM_COPY_HPP\n\n#include <Lz/detail/algorithm/copy.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Copies elements from an iterable to the output iterator.\n *\n * @param iterable The iterable to copy from.\n * @param out The output iterator to copy to.\n */\ntemplate<class Iterable, class OutputIterator>\nvoid copy(Iterable&& iterable, OutputIterator out) {\n    detail::copy(detail::begin(iterable), detail::end(iterable), std::move(out));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/count.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_COUNT_HPP\n#define LZ_ALGORITHM_COUNT_HPP\n\n#include <Lz/detail/algorithm/count.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Counts the amount of elements in the range [begin(iterable), end(iterable)) that are equal to the value @p value\n *\n * @param iterable The iterable to count in\n * @param value The value to count\n * @return The amount of elements that are equal to @p value\n */\ntemplate<class Iterable, class T>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::diff_iterable_t<Iterable> count(Iterable&& iterable, const T& value) {\n    return detail::count(detail::begin(iterable), detail::end(iterable), value);\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_COUNT_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/count_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_COUNT_IF_HPP\n#define LZ_ALGORITHM_COUNT_IF_HPP\n\n#include <Lz/detail/algorithm/count_if.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Counts the amount of elements in the range [begin(iterable), end(iterable)) that satisfy the unary_predicate @p\n * unary_predicate\n *\n * @param iterable The iterable to count\n * @param unary_predicate The unary_predicate to count the elements with\n * @return The amount of elements that satisfy the unary_predicate\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::diff_iterable_t<Iterable>\ncount_if(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return detail::count_if(detail::begin(iterable), detail::end(iterable), std::move(unary_predicate));\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_COUNT_IF_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/empty.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALOGORITHM_EMPTY_HPP\n#define LZ_ALOGORITHM_EMPTY_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * Checks whether [begin, end) is empty.\n * @param iterable The iterable to check whether it is empty.\n * @return True if it is empty, false otherwise.\n */\ntemplate<class Iterable>\nLZ_NODISCARD constexpr bool empty(Iterable&& iterable) {\n    return detail::begin(iterable) == detail::end(iterable);\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/ends_with.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_ENDS_WITH_HPP\n#define LZ_ALGORITHM_ENDS_WITH_HPP\n\n#include <Lz/detail/algorithm/ends_with.hpp>\n#include <Lz/procs/eager_size.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n#ifdef LZ_HAS_CXX_17\n\n/**\n * @brief Checks if the range [begin(iterable), end(iterable)) ends with the bidirectional range [begin(iterable2),\n * end(iterable2)). Needs to know the size of the range if its input iterables aren't bidirectional\n * so it may be worth your while to use `lz::cache_size` if the input iterable @p iterable and @p iterable2 aren't sized and\n * going to use this function multiple times. Does not use `lz::cached_reverse` to reverse the elements (if bidirectional).\n * @param iterable The iterable to check\n * @param iterable2 The bidirectional iterable to check for\n * @param binary_predicate The binary binary_predicate to check the values with\n * @return `true` if the value is found, `false` otherwise\n */\ntemplate<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<Iterable>)>\n[[nodiscard]] constexpr bool ends_with(Iterable&& iterable, Iterable2&& iterable2, BinaryPredicate binary_predicate = {}) {\n    if constexpr (detail::is_bidi_v<iter_t<Iterable>>) {\n        return detail::ends_with(detail::begin(iterable), detail::end(iterable), detail::begin(iterable2), detail::end(iterable2),\n                                 std::move(binary_predicate));\n    }\n    else {\n        return detail::ends_with(detail::begin(iterable), detail::end(iterable), detail::begin(iterable2), detail::end(iterable2),\n                                 std::move(binary_predicate), lz::eager_size(iterable), lz::eager_size(iterable2));\n    }\n}\n\n#else\n\n/**\n * @brief Checks if the range [begin(iterable), end(iterable)) ends with the bidirectional range [begin(iterable2),\n * end(iterable2))\n *\n * @param iterable The iterable to check\n * @param iterable2 The bidirectional iterable to check for\n * @param binary_predicate The binary binary_predicate to check the values with\n * @return `true` if the value is found, `false` otherwise\n */\ntemplate<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::enable_if_t<detail::is_bidi<iter_t<Iterable>>::value, bool>\nends_with(Iterable&& iterable, Iterable2&& iterable2, BinaryPredicate binary_predicate = {}) {\n    return detail::ends_with(detail::begin(iterable), detail::end(iterable), detail::begin(iterable2), detail::end(iterable2),\n                             std::move(binary_predicate));\n}\n\n/**\n * @brief Checks if the range [begin(iterable), end(iterable)) ends with the forward iterable range [begin(iterable2),\n * end(iterable2)). Needs to know the size of the range if its input iterables aren't bidirectional so it may be worth your\n * while to use `lz::cache_size` if the input iterable @p iterable and @p iterable2 aren't sized and going to use this\n * function multiple times.\n *\n * @param iterable The iterable to check\n * @param iterable2 The forward iterable to check for\n * @param binary_predicate The binary binary_predicate to check the values with\n * @return `true` if the value is found, `false` otherwise\n */\ntemplate<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::enable_if_t<!detail::is_bidi<iter_t<Iterable>>::value, bool>\nends_with(Iterable&& iterable, Iterable2&& iterable2, BinaryPredicate binary_predicate = {}) {\n    return detail::ends_with(detail::begin(iterable), detail::end(iterable), detail::begin(iterable2), detail::end(iterable2),\n                             std::move(binary_predicate), lz::eager_size(iterable), lz::eager_size(iterable2));\n}\n\n#endif // LZ_HAS_CXX_17\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/equal.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_EQUAL_HPP\n#define LZ_ALGORITHM_EQUAL_HPP\n\n#include <Lz/detail/algorithm/equal.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/procs/size.hpp>\n#include <Lz/traits/is_sized.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n#ifndef LZ_HAS_CXX_17\n\n/**\n * Use this function to check if two lz iterators are the same.\n * @param a An iterable, its underlying value type should have an operator== with `b`\n * @param b An iterable, its underlying value type should have an operator== with `a`\n * @param binary_predicate The predicate to check if two elements are equal\n * @return true if both are equal, false otherwise.\n */\ntemplate<class IterableA, class IterableB, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<IterableA>)>\nLZ_NODISCARD\n    LZ_CONSTEXPR_CXX_14 detail::enable_if_t<detail::is_sized<IterableA>::value && detail::is_sized<IterableB>::value, bool>\n    equal(IterableA&& a, IterableB&& b, BinaryPredicate binary_predicate = {}) {\n    if (lz::size(a) != lz::size(b)) {\n        return false;\n    }\n\n    return detail::equal(detail::begin(a), detail::end(a), detail::begin(b), detail::end(b), std::move(binary_predicate));\n}\n\n/**\n * Use this function to check if two lz iterators are the same.\n * @param a An iterable, its underlying value type should have an operator== with `b`\n * @param b An iterable, its underlying value type should have an operator== with `a`\n * @param binary_predicate The predicate to check if two elements are equal\n * @return true if both are equal, false otherwise.\n */\ntemplate<class IterableA, class IterableB, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<IterableA>)>\nLZ_NODISCARD\n    LZ_CONSTEXPR_CXX_14 detail::enable_if_t<!detail::is_sized<IterableA>::value || !detail::is_sized<IterableB>::value, bool>\n    equal(IterableA&& a, IterableB&& b, BinaryPredicate binary_predicate = {}) {\n    return detail::equal(detail::begin(a), detail::end(a), detail::begin(b), detail::end(b), std::move(binary_predicate));\n}\n\n#else\n\n/**\n * Use this function to check if two lz iterators are the same.\n * @param a An iterable, its underlying value type should have an operator== with `b`\n * @param b An iterable, its underlying value type should have an operator== with `a`\n * @param binary_predicate The predicate to check if two elements are equal\n * @return true if both are equal, false otherwise.\n */\ntemplate<class IterableA, class IterableB, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<IterableA>)>\n[[nodiscard]] constexpr bool equal(IterableA&& a, IterableB&& b, BinaryPredicate binary_predicate = {}) {\n    if constexpr (detail::is_sized_v<IterableA> && detail::is_sized_v<IterableB>) {\n        if (lz::size(a) != lz::size(b)) {\n            return false;\n        }\n    }\n\n    return detail::equal(detail::begin(a), detail::end(a), detail::begin(b), detail::end(b), std::move(binary_predicate));\n}\n\n#endif\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/find.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_FIND_HPP\n#define LZ_DETAIL_ALGORITHM_FIND_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the first element in the range [begin(iterable), end(iterable)) that satisfies the value @p value\n *\n * @param iterable The iterable to find the element in\n * @param value The value to find\n * @return The iterator to the first element that satisfies the value or `end(iterable)` if the element is not found\n */\ntemplate<class Iterable, class T>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> find(Iterable&& iterable, const T& value) {\n    return lz::find_if(std::forward<Iterable>(iterable), [&value](detail::ref_t<iter_t<Iterable>> val) { return val == value; });\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/find_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_IF_HPP\n#define LZ_ALGORITHM_FIND_IF_HPP\n\n#include <Lz/detail/algorithm/find_if.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/traits/iter_type.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\n#include <algorithm>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the first element in the range [begin, end) that satisfies the binary_predicate @p binary_predicate\n *\n * @param iterable The iterable to find the element in\n * @param unary_predicate The binary_predicate to find the element with\n * @return Iterator to the first element that satisfies the binary_predicate or `end(iterable)` if the element is not found\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> find_if(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return detail::find_if(detail::begin(iterable), detail::end(iterable), std::move(unary_predicate));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/find_if_not.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_IF_NOT_HPP\n#define LZ_ALGORITHM_FIND_IF_NOT_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the first element in the range [begin(iterable), end(iterable)) that does not satisfy the unary_predicate @p\n * unary_predicate\n *\n * @param iterable The iterable to find the element in\n * @param unary_predicate The unary_predicate to find the element with\n * @return Iterator to the first element that does not satisfy the unary_predicate or `end(iterable)` if the element is not\n * found\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> find_if_not(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return lz::find_if(std::forward<Iterable>(iterable),\n                       [&unary_predicate](detail::ref_t<iter_t<Iterable>> value) { return !unary_predicate(value); });\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_FIND_IF_NOT_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/find_last.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_LAST_HPP\n#define LZ_ALGORITHM_FIND_LAST_HPP\n\n#include <Lz/algorithm/find_last_if.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the first element in the range [begin(iterable), end(iterable)) that satisfies the value @p value.\n * Does not use `lz::cached_reverse` to reverse the elements (if bidirectional).\n * @param iterable The iterable to find the element in\n * @param value The value to find\n * @return An iterator to the first element that satisfies the unary_predicate or `end(iterable)` if the element is not found\n */\ntemplate<class Iterable, class T>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> find_last(Iterable&& iterable, const T& value) {\n    return lz::find_last_if(std::forward<Iterable>(iterable),\n                            [&value](detail::ref_t<iter_t<Iterable>> val) { return val == value; });\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/find_last_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_LAST_IF_HPP\n#define LZ_ALGORITHM_FIND_LAST_IF_HPP\n\n#include <Lz/basic_iterable.hpp>\n#include <Lz/detail/algorithm/find_last_if.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the last element in the range [begin(iterable), end(iterable)) that satisfies the unary_predicate @p\n * unary_predicate. Does not use `lz::cached_reverse` to reverse the elements (if bidirectional).\n * @param iterable The iterable to find the element in\n * @param unary_predicate The unary_predicate to find the element with\n * @return An iterator to the first element that satisfies the unary_predicate or `end(iterable)` if the element is not found\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> find_last_if(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return detail::find_last_if(std::forward<Iterable>(iterable), std::move(unary_predicate));\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_FIND_LAST_IF_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/find_last_if_not.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_LAST_IF_NOT_HPP\n#define LZ_ALGORITHM_FIND_LAST_IF_NOT_HPP\n\n#include <Lz/algorithm/find_last_if.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the last element in the range [begin(iterable), end(iterable)) that does not satisfy the unary_predicate @p\n * unary_predicate. Does not use `lz::cached_reverse` to reverse the elements (if bidirectional).\n *\n * @param iterable The iterable to find the element in\n * @param unary_predicate The unary_predicate to find the element with\n * @return Iterator to the last element that does not satisfy the unary_predicate or `end(iterable)` if the element is not\n * found\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 iter_t<Iterable>\nfind_last_if_not(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return lz::find_last_if(std::forward<Iterable>(iterable),\n                            [&unary_predicate](detail::ref_t<iter_t<Iterable>> value) { return !unary_predicate(value); });\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_FIND_LAST_IF_NOT_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/find_last_or_default.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_LAST_OR_DEFAULT_HPP\n#define LZ_ALGORITHM_FIND_LAST_OR_DEFAULT_HPP\n\n#include <Lz/algorithm/find_last_or_default_if.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the first element in the range [begin(iterable), end(iterable)) that satisfies the value @p to_find. If the\n * element is found, it returns the value, otherwise it returns @p default_value\n * @param iterable The iterable to find the element in\n * @param to_find The value to find\n * @param default_value The value to return if the element is not found, defaults to `T()`\n * @return The value @p to_find if it is found, otherwise @p default_value\n */\ntemplate<class Iterable, class T, class U>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::val_iterable_t<Iterable>\nfind_last_or_default(Iterable&& iterable, T&& to_find, U&& default_value) {\n    return lz::find_last_or_default_if(\n        std::forward<Iterable>(iterable), [&to_find](detail::ref_t<iter_t<Iterable>> value) { return value == to_find; },\n        std::forward<U>(default_value));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/find_last_or_default_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_LAST_OR_DEFAULT_IF_HPP\n#define LZ_ALGORITHM_FIND_LAST_OR_DEFAULT_IF_HPP\n\n#include <Lz/algorithm/find_last_if.hpp>\n\nnamespace lz {\n\n/**\n * @brief Finds the last element in the range [begin(iterable), end(iterable)) that satisfies the unary_predicate @p\n * unary_predicate. If the element is found, it returns the value, otherwise it returns @p default_value\n * @param iterable The iterable to search.\n * @param unary_predicate The search unary_predicate in [begin(iterable), end(iterable)). Must return bool.\n * @param default_value The value to return when no element matches unary_predicate @p unary_predicate in [begin(iterable),\n * end(iterable)). Defaults to `Iterable::value_type()`\n * @return The value if it is found, otherwise @p default_value\n */\ntemplate<class Iterable, class UnaryPredicate, class U>\nLZ_CONSTEXPR_CXX_14 detail::val_iterable_t<Iterable>\nfind_last_or_default_if(Iterable&& iterable, UnaryPredicate unary_predicate, U&& default_value) {\n    auto pos = lz::find_last_if(iterable, std::move(unary_predicate));\n    return static_cast<detail::val_t<iter_t<Iterable>>>(pos == detail::end(iterable) ? std::forward<U>(default_value) : *pos);\n}\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/find_last_or_default_if_not.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_LAST_OR_DEFAULT_IF_NOT_HPP\n#define LZ_ALGORITHM_FIND_LAST_OR_DEFAULT_IF_NOT_HPP\n\n#include <Lz/algorithm/find_last_or_default_if.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Find the last element in the iterable that does not satisfy the predicate. If no such element is found, return the\n * default value.\n *\n * @param iterable The iterable to search.\n * @param unary_predicate The predicate to test each element.\n * @param default_value The value to return if no element is found.\n * @return The found element or the default value.\n */\ntemplate<class Iterable, class UnaryPredicate, class U>\nLZ_CONSTEXPR_CXX_14 detail::val_iterable_t<Iterable>\nfind_last_or_default_if_not(Iterable&& iterable, UnaryPredicate unary_predicate, U&& default_value) {\n    return lz::find_last_or_default_if(\n        std::forward<Iterable>(iterable),\n        [&unary_predicate](detail::ref_t<iter_t<Iterable>> val) { return !unary_predicate(val); },\n        std::forward<U>(default_value));\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_FIND_LAST_OR_DEFAULT_IF_NOT_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/find_last_or_default_not.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_LAST_OR_DEFAULT_NOT_HPP\n#define LZ_ALGORITHM_FIND_LAST_OR_DEFAULT_NOT_HPP\n\n#include <Lz/algorithm/find_last_or_default_if.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Find the last element in the iterable that is not equal to the given value. If no such element is found, return the\n * default value.\n *\n * @param iterable The iterable to search.\n * @param value The value to compare each element against.\n * @param default_value The value to return if no element is found.\n * @return The found element or the default value.\n */\ntemplate<class Iterable, class T, class U>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::val_iterable_t<Iterable>\nfind_last_or_default_not(Iterable&& iterable, T&& value, U&& default_value) {\n    return lz::find_last_or_default_if(\n        std::forward<Iterable>(iterable), [&value](detail::ref_t<iter_t<Iterable>> val) { return !(val == value); },\n        std::forward<U>(default_value));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/find_or_default.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_OR_DEFAULT_HPP\n#define LZ_ALGORITHM_FIND_OR_DEFAULT_HPP\n\n#include <Lz/algorithm/find_or_default_if.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the first element in the range [begin(iterable), end(iterable)) that satisfies the value @p to_find. If the\n * element is found, it returns the value, otherwise it returns @p default_value\n * @param iterable The iterable to find the element in\n * @param to_find The value to find\n * @param default_value The value to return if the element is not found, defaults to `T()`\n * @return The value @p to_find if it is found, otherwise @p default_value\n */\ntemplate<class Iterable, class T, class U>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::val_iterable_t<Iterable>\nfind_or_default(Iterable&& iterable, T&& to_find, U&& default_value) {\n    return lz::find_or_default_if(\n        std::forward<Iterable>(iterable), [&to_find](detail::ref_t<iter_t<Iterable>> value) { return value == to_find; },\n        std::forward<U>(default_value));\n}\n\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/find_or_default_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FIND_OR_DEFAULT_IF_HPP\n#define LZ_ALGORITHM_FIND_OR_DEFAULT_IF_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the first element in the range [begin(iterable), end(iterable)) that satisfies the unary_predicate @p\n * unary_predicate. If the element is found, it returns the value, otherwise it returns @p default_value\n * @param iterable The iterable to search.\n * @param unary_predicate The search unary_predicate in [begin(iterable), end(iterable)). Must return bool.\n * @param default_value The value to return when no element matches unary_predicate @p unary_predicate in [begin(iterable),\n * end(iterable)). Defaults to `Iterable::value_type()`\n * @return The value if it is found, otherwise @p default_value\n */\ntemplate<class Iterable, class UnaryPredicate, class T>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::val_iterable_t<Iterable>\nfind_or_default_if(Iterable&& iterable, UnaryPredicate unary_predicate, T&& default_value) {\n    auto pos = lz::find_if(iterable, std::move(unary_predicate));\n    return static_cast<detail::val_t<iter_t<Iterable>>>(pos == detail::end(iterable) ? std::forward<T>(default_value) : *pos);\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/for_each.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FOR_EACH_HPP\n#define LZ_ALGORITHM_FOR_EACH_HPP\n\n#include <Lz/detail/algorithm/for_each.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\ntemplate<class Iterable, class Func>\nLZ_CONSTEXPR_CXX_14 void for_each(Iterable&& iterable, Func func) {\n    detail::for_each(detail::begin(iterable), detail::end(iterable), std::move(func));\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_FOR_EACH_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/for_each_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FOR_EACH_WHILE_HPP\n#define LZ_ALGORITHM_FOR_EACH_WHILE_HPP\n\n#include <Lz/detail/algorithm/for_each_while.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Applies the given function to each element in the iterable until the function returns false. @p unary_predicate\n * should return a boolean. If `true` is returned, the next element will be processed. If `false` is returned, the iteration\n * stops.\n *\n * @param iterable The iterable to process.\n * @param unary_predicate The function to apply to each element. Should return a boolean.\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 void for_each_while(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return detail::for_each_while(detail::begin(iterable), detail::end(iterable), std::move(unary_predicate));\n}\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/for_each_while_n.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FOR_EACH_WHILE_N_HPP\n#define LZ_ALGORITHM_FOR_EACH_WHILE_N_HPP\n\n#include <Lz/detail/algorithm/for_each_while_n.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Keeps iterating over @p iterable while @p binary_predicate returns true and @p n < distance(begin(iterable),\n * end(iterable)).\n *\n * @param iterable A range\n * @param n The amount of elements to iterate over\n * @param unary_op Predicate that must either return true or false\n */\ntemplate<class Iterable, class UnaryOp>\nLZ_CONSTEXPR_CXX_14 void for_each_while_n(Iterable&& iterable, size_t n, UnaryOp unary_op) {\n    detail::for_each_while_n(std::forward<Iterable>(iterable), n, std::move(unary_op));\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_FOR_EACH_WHILE_N_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/front.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FRONT_HPP\n#define LZ_ALGORITHM_FRONT_HPP\n\n#include <Lz/algorithm/empty.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Returns the first element of the given iterable.\n *\n * @param iterable The iterable to get the first element from.\n * @return The first element of the iterable.\n * @throws std::out_of_range if the iterable is empty.\n */\ntemplate<class Iterable>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::ref_iterable_t<Iterable> front(Iterable&& iterable) {\n    LZ_ASSERT(!lz::empty(iterable), \"Called lz::front on an empty iterable\");\n    return *detail::begin(iterable);\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/front_or.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_FRONT_OR_HPP\n#define LZ_ALGORITHM_FRONT_OR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * Gets the first element of the sequence, or `default_value` if the sequence is empty.\n * @param iterable The iterable to get the first value of, or `value` in case it is empty.\n * @param default_value The value to return if `iterable` is empty.\n * @return Either the first element of `iterable` or `value` if the sequence is empty.\n */\ntemplate<class Iterable, class T>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::val_iterable_t<Iterable> front_or(Iterable&& iterable, T&& default_value) {\n    auto begin = detail::begin(iterable);\n    auto end = detail::end(iterable);\n    return begin == end ? std::forward<T>(default_value) : *begin;\n}\n\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/has_many.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_HAS_MANY_HPP\n#define LZ_ALGORITHM_HAS_MANY_HPP\n\n#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/has_one.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * Returns whether the sequence holds >= 2 elements.\n * @param iterable The sequence to check whether it has many (>= 2) elements.\n * @return True if the amount of values are >= 2, false otherwise.\n */\ntemplate<class Iterable>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 bool has_many(Iterable&& iterable) {\n    return !lz::empty(iterable) && !lz::has_one(iterable);\n}\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/has_one.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_HAS_ONE_HPP\n#define LZ_ALGORITHM_HAS_ONE_HPP\n\n#include <Lz/algorithm/empty.hpp>\n#include <Lz/basic_iterable.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * Returns whether the sequence holds exactly one element.\n * @param iterable The sequence to check whether is has exactly one element.\n * @return True if it has one element exactly, false otherwise.\n */\ntemplate<class Iterable>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 bool has_one(Iterable&& iterable) {\n    auto begin = detail::begin(iterable);\n    auto end = detail::end(iterable);\n    return !lz::empty(iterable) && lz::empty(make_basic_iterable(++begin, end));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/index_of.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_INDEX_OF_HPP\n#define LZ_ALGORITHM_INDEX_OF_HPP\n\n#include <Lz/algorithm/index_of_if.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Returns the index of the first occurrence of the specified value in the iterable. If the value is not found, it\n * returns lz::npos.\n * @param iterable The iterable to search.\n * @param value The value to search for.\n * @return The index of the first occurrence of the specified value in the iterable, or lz::npos if the value is not found.\n */\ntemplate<class Iterable, class T>\nLZ_CONSTEXPR_CXX_14 size_t index_of(Iterable&& iterable, const T& value) {\n    return detail::index_of_if(detail::begin(iterable), detail::end(iterable),\n                               [&value](detail::ref_t<iter_t<Iterable>> val) { return val == value; });\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_INDEX_OF_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/index_of_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_INDEX_OF_IF_HPP\n#define LZ_ALGORITHM_INDEX_OF_IF_HPP\n\n#include <Lz/detail/algorithm/index_of_if.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Returns the index of the first element in the iterable that satisfies the unary_predicate @p unary_predicate.\n * If no such element is found, it returns lz::npos.\n * @param iterable The iterable to search.\n * @param unary_predicate The search unary_predicate in [begin(iterable), end(iterable)). Must return bool.\n * @return The index of the first element that satisfies the unary_predicate @p unary_predicate, or lz::npos if no such\n * element is found.\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 size_t index_of_if(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return detail::index_of_if(detail::begin(iterable), detail::end(iterable), std::move(unary_predicate));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/is_sorted.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_IS_SORTED_HPP\n#define LZ_ALGORITHM_IS_SORTED_HPP\n\n#include <Lz/detail/algorithm/is_sorted.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Checks if the range [begin(iterable), end(iterable)) is sorted using the binary binary_predicate @p\n * binary_predicate\n *\n * @param iterable The iterable to check\n * @param binary_predicate The binary binary_predicate to check the range with\n * @return true if the range is sorted, false otherwise\n */\ntemplate<class Iterable, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 bool is_sorted(Iterable && iterable, BinaryPredicate binary_predicate = {}) {\n    return detail::is_sorted(detail::begin(iterable), detail::end(iterable), std::move(binary_predicate));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/lower_bound.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_LOWER_BOUND_HPP\n#define LZ_ALGORITHM_LOWER_BOUND_HPP\n\n#include <Lz/detail/algorithm/lower_bound.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n#include <Lz/procs/distance.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Searches for the first element in the partitioned range [begin(iterable), end(iterable)) which is not ordered before\n * @p value.\n *\n * @param iterable The iterable to check\n * @param value The value to check for\n * @param binary_predicate The to use when comparing the values\n * @return Iteartor to the first element that satisfies the value or `end(iterable)` if the element is not found\n */\ntemplate<class Iterable, class T, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable>)>\nLZ_CONSTEXPR_CXX_14 detail::enable_if_t<detail::is_iterable<Iterable>::value, iter_t<Iterable>>\nlower_bound(Iterable&& iterable, const T& value, BinaryPredicate binary_predicate = {}) {\n    return detail::lower_bound(std::forward<Iterable>(iterable), value, std::move(binary_predicate));\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_LOWER_BOUND_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/max_element.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_MAX_ELEMENT_HPP\n#define LZ_ALGORITHM_MAX_ELEMENT_HPP\n\n#include <Lz/detail/algorithm/max_element.hpp>\n#include <Lz/detail/procs/operators.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the maximum element in the range [begin, end) using the binary binary_predicate @p binary_predicate\n * @param iterable The iterable to find the maximum element in\n * @param binary_predicate The binary operator to find the maximum element with\n * @return The maximum element in the range, if the range is empty, the return value is `end`\n */\ntemplate<class Iterable, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> max_element(Iterable&& iterable, BinaryPredicate binary_predicate = {}) {\n    return detail::max_element(detail::begin(iterable), detail::end(iterable), std::move(binary_predicate));\n}\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/mean.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_MEAN_HPP\n#define LZ_ALGORITHM_MEAN_HPP\n\n#include <Lz/detail/algorithm/mean.hpp>\n#include <Lz/detail/procs/operators.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * Gets the mean of a sequence.\n * @param iterable The iterable to calculate the mean of.\n * @param binary_op The binary operator to calculate the mean with.\n * @return The mean of the container.\n */\ntemplate<class Iterable, class BinaryOp = LZ_BIN_OP(plus, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 double mean(Iterable && iterable, BinaryOp binary_op = {}) {\n    return detail::mean(detail::begin(iterable), detail::end(iterable), std::move(binary_op));\n}\n\n} // namespace lz\n#endif // LZ_ALGORITHM_MEAN_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/min_element.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_MIN_ELEMENT_HPP\n#define LZ_ALGORITHM_MIN_ELEMENT_HPP\n\n#include <Lz/algorithm/max_element.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Finds the minimum element in the range [begin, end) using the binary binary_predicate @p binary_predicate\n *\n * @param iterable The iterable to find the minimum element in\n * @param binary_predicate The binary operator to find the minimum element with\n * @return The minimum element in the range or `end` if the range is empty\n */\ntemplate<class Iterable, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> min_element(Iterable&& iterable, BinaryPredicate binary_predicate = {}) {\n    return lz::max_element(std::forward<Iterable>(iterable),\n                           [&binary_predicate](detail::ref_t<iter_t<Iterable>> a, detail::ref_t<iter_t<Iterable>> b) {\n                               return !binary_predicate(a, b);\n                           });\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/none_of.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_NONE_OF_HPP\n#define LZ_ALGORITHM_NONE_OF_HPP\n\n#include <Lz/algorithm/any_of.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Checks whether none of the elements in the range [begin(iterable), end(iterable)) satisfy the unary_predicate @p\n * unary_predicate\n *\n * @param iterable The iterable to check\n * @param unary_predicate The unary_predicate to call whether none of the elements satisfy this condition\n * @return true if none of the elements satisfy the unary_predicate, false otherwise\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 bool none_of(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return !lz::any_of(std::forward<Iterable>(iterable), std::move(unary_predicate));\n}\n\n} // namespace lz\n#endif // LZ_ALGORITHM_NONE_OF_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/npos.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_NPOS_HPP\n#define LZ_ALGORITHM_NPOS_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * This value is returned when index_of(_if) does not find the value specified.\n */\nLZ_INLINE_VAR constexpr size_t npos = static_cast<size_t>(-1);\n\n}\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/nth.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_NTH_HPP\n#define LZ_ALGORITHM_NTH_HPP\n\n#include <Lz/detail/procs/next_fast.hpp>\n#include <Lz/traits/iter_type.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Returns an iterator to the n-th element of the iterable.\n *\n * @param iterable The iterable to get the n-th element from\n * @param n The index of the element to get, starting from 0\n * @return An iterator to the n-th element of the iterable\n */\ntemplate<class Iterable>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> nth(Iterable&& iterable, detail::diff_iterable_t<Iterable> n) {\n    return detail::next_fast(std::forward<Iterable>(iterable), n);\n}\n\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/partition.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_PARTITION_HPP\n#define LZ_ALGORITHM_PARTITION_HPP\n\n#include <Lz/detail/algorithm/partition.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Partitions the iterable in such a way that all elements that satisfy the unary_predicate come before all elements\n * that do not satisfy the unary_predicate. The relative order of the elements is not preserved.\n *\n * @param iterable The iterable to partition\n * @param unary_predicate The unary_predicate to partition the iterable with\n * @return Iterator to the first element in the second group (the elements that do not satisfy the unary_predicate)\n */\ntemplate<class Iterable, class UnaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> partition(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    return detail::partition(detail::begin(iterable), detail::end(iterable), std::move(unary_predicate));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/peek.hpp",
    "content": "#ifndef LZ_ALGORITHM_PEEK_HPP\n#define LZ_ALGORITHM_PEEK_HPP\n\n#include <Lz/detail/algorithm/peek.hpp>\n#include <Lz/detail/compiler_config.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Returns an optional containing the second element of the given iterable, or nullopt if the iterable has less than\n * two elements.\n *\n * @param iterable The iterable to peek into.\n * @return An optional containing the next element of the iterable, or nullopt if the iterable has less than two elements.\n */\ntemplate<class Iterable>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 auto\npeek(Iterable&& iterable) -> decltype(detail::peek(detail::begin(iterable), detail::end(iterable))) {\n    return detail::peek(detail::begin(iterable), detail::end(iterable));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/search.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_SEARCH_HPP\n#define LZ_ALGORITHM_SEARCH_HPP\n\n#include <Lz/detail/algorithm/search.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Searches for the first occurrence of the range [begin(iterable2), end(iterable2)) in the range [begin(iterable),\n * end(iterable))\n *\n * @param iterable The iterable to search in\n * @param iterable2 The iterable to search for\n * @param binary_predicate The binary predicate to search with\n * @return The iterator to the first occurrence of the range [begin(iterable2), end(iterable2)) in the range [begin(iterable),\n * end(iterable)) or `end(iterable)` if the range is not found\n */\ntemplate<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 std::pair<iter_t<Iterable>, iter_t<Iterable>> search(\n    Iterable && iterable, Iterable2 && iterable2, BinaryPredicate binary_predicate = {}) {\n    return detail::search(detail::begin(iterable), detail::end(iterable), detail::begin(iterable2), detail::end(iterable2),\n                          std::move(binary_predicate));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/starts_with.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_STARTS_WITH_HPP\n#define LZ_ALGORITHM_STARTS_WITH_HPP\n\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/detail/algorithm/starts_with.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n#ifdef LZ_HAS_CXX_17\n\n/**\n * @brief Checks if the beginning of the first iterable matches the second iterable, using an optional binary predicate for\n * comparison.\n *\n * @param iterable The first iterable to check.\n * @param iterable2 The second iterable to compare against.\n * @param binary_predicate A binary predicate that takes two elements (one from each iterable) and returns true if they are\n * considered equal. Defaults to `std::equal_to<>`.\n * @return true if the beginning of the first iterable matches the second iterable, false otherwise.\n */\ntemplate<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(equal_to, val_iterable_t<Iterable>)>\n[[nodiscard]] constexpr bool starts_with(Iterable && iterable, Iterable2 && iterable2, BinaryPredicate binary_predicate = {}) {\n    if constexpr (is_sized_v<Iterable> && is_sized_v<Iterable2>) {\n        if (lz::size(iterable2) > lz::size(iterable)) {\n            return false;\n        }\n    }\n    return detail::starts_with(detail::begin(iterable), detail::end(iterable), detail::begin(iterable2), detail::end(iterable2),\n                               std::move(binary_predicate));\n}\n\n#else\n\n/**\n * @brief Checks if the beginning of the first iterable matches the second iterable, using an optional binary predicate for\n * comparison.\n *\n * @param iterable The first iterable to check.\n * @param iterable2 The second iterable to compare against.\n * @param binary_predicate A binary predicate that takes two elements (one from each iterable) and returns true if they are\n * considered equal. Defaults to `std::equal_to<>`.\n * @return true if the beginning of the first iterable matches the second iterable, false otherwise.\n */\ntemplate<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::enable_if_t<is_sized<Iterable>::value && is_sized<Iterable2>::value, bool> starts_with(\n    Iterable && iterable, Iterable2 && iterable2, BinaryPredicate binary_predicate = {}) {\n    if (lz::size(iterable2) > lz::size(iterable)) {\n        return false;\n    }\n    return detail::starts_with(detail::begin(iterable), detail::end(iterable), detail::begin(iterable2), detail::end(iterable2),\n                               std::move(binary_predicate));\n}\n\n/**\n * @brief Checks if the beginning of the first iterable matches the second iterable, using an optional binary predicate for\n * comparison.\n *\n * @param iterable The first iterable to check.\n * @param iterable2 The second iterable to compare against.\n * @param binary_predicate A binary predicate that takes two elements (one from each iterable) and returns true if they are\n * considered equal. Defaults to `std::equal_to<>`.\n * @return true if the beginning of the first iterable matches the second iterable, false otherwise.\n */\ntemplate<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(equal_to, detail::val_iterable_t<Iterable>)>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::enable_if_t<!is_sized<Iterable>::value || !is_sized<Iterable2>::value, bool> starts_with(\n    Iterable && iterable, Iterable2 && iterable2, BinaryPredicate binary_predicate = {}) {\n    return detail::starts_with(detail::begin(iterable), detail::end(iterable), detail::begin(iterable2), detail::end(iterable2),\n                               std::move(binary_predicate));\n}\n\n#endif\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/algorithm/transform.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_TRANSFORM_HPP\n#define LZ_ALGORITHM_TRANSFORM_HPP\n\n#include <Lz/detail/algorithm/transform.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Applies the given unary operation to the elements in the range [begin(iterable), end(iterable)) and writes the\n * results in the range starting at @p output.\n * @param iterable The iterable to transform\n * @param output The output iterator to write the transformed elements to\n * @param unary_op The unary operation to apply to each element\n */\ntemplate<class Iterable, class OutputIterator, class UnaryOp>\nLZ_CONSTEXPR_CXX_14 void transform(Iterable&& iterable, OutputIterator output, UnaryOp unary_op) {\n    detail::transform(detail::begin(iterable), detail::end(iterable), std::move(output), std::move(unary_op));\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_TRANSFORM_HPP\n"
  },
  {
    "path": "include/Lz/algorithm/upper_bound.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ALGORITHM_UPPER_BOUND_HPP\n#define LZ_ALGORITHM_UPPER_BOUND_HPP\n\n#include <Lz/algorithm/lower_bound.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Searches for the first element in the partitioned range [begin(iterable), end(iterable)) which is not ordered before\n * @p value.\n *\n * @param iterable The iterable to check\n * @param value The value to check for\n * @param binary_predicate The to use when comparing the values\n * @return Iteartor to the first element that satisfies the value or `end(iterable)` if the element is not found\n */\ntemplate<class Iterable, class T, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable>)>\nLZ_CONSTEXPR_CXX_14 detail::enable_if_t<detail::is_iterable<Iterable>::value, iter_t<Iterable>>\nupper_bound(Iterable&& iterable, const T& value, BinaryPredicate binary_predicate = {}) {\n    return lz::lower_bound(\n        std::forward<Iterable>(iterable), value,\n        [&binary_predicate](detail::ref_t<iter_t<Iterable>> v1, const T& v2) { return !binary_predicate(v2, v1); });\n}\n\n} // namespace lz\n\n#endif // LZ_ALGORITHM_UPPER_BOUND_HPP\n"
  },
  {
    "path": "include/Lz/any_iterable.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ANY_VIEW_HPP\n#define LZ_ANY_VIEW_HPP\n\n#include <Lz/detail/iterators/any_iterable/any_iterator_impl.hpp>\n#include <Lz/detail/iterators/iterator_wrapper.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/procs/chain.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n/**\n * @brief Class that can contain any type of view. For example: a container or another view. Use this when you cannot use\n * `auto`. Please be aware that this implementation uses type erasure and therefore is a little bit slower than using\n * `auto`/specifying the \"actual\" type. For e.g.\n * ```cpp\n * // Preferred:\n * lz::filter_iterable<std::vector<int>::iterator, lambdafunction> f = lz::filter( // stuff...\n * // Or\n * auto f = lz::filter( // stuff\n * // Versus (slower):\n * lz::any_iterable<int> f = lz::filter( // stuff...\n * // or\n * auto f = lz::make_any_iterable(lz::filter( // stuff...\n * ```\n *\n * @tparam T The value_type of the iterator. For example: std::vector<int>::value_type = int, which requires a\n * lz::any_iterable<int>\n * @tparam Reference The reference of the iterator. In most cases T&, but with generative iterators it's oftentimes just T.\n * @tparam IterCat The iterator category. `std::forward_iterator_tag` by default.\n * @tparam DiffType The difference_type. It is used for the return type of iterator - iterator\n */\ntemplate<class T, class Reference = T&, class IterCat = std::forward_iterator_tag, class DiffType = std::ptrdiff_t>\nclass any_iterable : public lazy_view {\nprivate:\n    using it = detail::iterator_wrapper<T, Reference, IterCat, DiffType>;\n\n    template<class Iterable>\n    using any_iter_impl = detail::any_iterator_impl<iter_t<Iterable>, sentinel_t<Iterable>, T, Reference, IterCat, DiffType>;\n\n    it _begin;\n    it _end;\n\n#ifndef LZ_HAS_CONCEPTS\n\n    // One of them fits, the other does not\n        template<class Iterable>\n        any_iterable(Iterable&& iterable,\n                     detail::enable_if_t<\n                         (sizeof(iter_t<Iterable>) > it::SBO_SIZE) || (sizeof(sentinel_t<Iterable>) > it::SBO_SIZE), int>) :\n            _begin{ detail::make_unique<any_iter_impl<Iterable>>(detail::begin(iterable)) },\n            _end{ detail::make_unique<any_iter_impl<Iterable>>(detail::end(iterable)) } {\n        }\n\n    // Both fit\n    template<class Iterable>\n    any_iterable(\n        Iterable&& iterable,\n        detail::enable_if_t<(sizeof(iter_t<Iterable>) <= it::SBO_SIZE) && (sizeof(sentinel_t<Iterable>) <= it::SBO_SIZE), int>) :\n        _begin{ detail::in_place_type_t<any_iter_impl<Iterable>>{}, detail::begin(iterable) },\n        _end{ detail::in_place_type_t<any_iter_impl<Iterable>>{}, detail::end(iterable) } {\n    }\n\n#endif\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr any_iterable()\n        requires(std::default_initializable<it>)\n    = default;\n\n    // One of them fits, the other does not\n\n    /**\n     * @brief Construct a new any_iterable object\n     *\n     * @param iterable Any iterable, like a vector, list, etc. Can also be another lz range/view\n     */\n    template<class Iterable>\n        requires((sizeof(iter_t<Iterable>) > it::SBO_SIZE) || (sizeof(sentinel_t<Iterable>) > it::SBO_SIZE))\n    any_iterable(Iterable&& iterable) :\n        _begin{ detail::make_unique<any_iter_impl<Iterable>>(detail::begin(iterable)) },\n        _end{ detail::make_unique<any_iter_impl<Iterable>>(detail::end(iterable)) } {\n    }\n\n    // Both fit\n\n    /**\n     * @brief Construct a new any_iterable object\n     *\n     * @param iterable Any iterable, like a vector, list, etc. Can also be another lz range/view\n     */\n    template<class Iterable>\n        requires((sizeof(iter_t<Iterable>) <= it::SBO_SIZE) && (sizeof(sentinel_t<Iterable>) <= it::SBO_SIZE))\n    any_iterable(Iterable&& iterable) :\n        _begin{ detail::in_place_type_t<any_iter_impl<Iterable>>{}, detail::begin(iterable) },\n        _end{ detail::in_place_type_t<any_iter_impl<Iterable>>{}, detail::end(iterable) } {\n    }\n\n#else\n\n    template<class I = it, class = detail::enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr any_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n    /**\n     * @brief Construct a new any_iterable object\n     *\n     * @param iterable Any iterable, like a vector, list, etc. Can also be another lz range/view\n     */\n    template<class Iterable>\n    any_iterable(Iterable&& iterable) : any_iterable(std::forward<Iterable>(iterable), 0) {\n    }\n\n#endif\n\n    LZ_NODISCARD it begin() const {\n        return _begin;\n    }\n\n    LZ_NODISCARD it end() const {\n        return _end;\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(detail::is_ra_tag_v<IterCat>)\n    {\n        return static_cast<size_t>(std::distance(_begin, _end));\n    }\n\n#else\n\n    template<class I = IterCat>\n    LZ_NODISCARD detail::enable_if_t<detail::is_ra_tag<I>::value, size_t> size() const {\n        return static_cast<size_t>(std::distance(_begin, _end));\n    }\n\n#endif\n};\n\n/**\n * Creates an any_iterable object from an iterable. This is useful when you cannot use `auto` or when you want to store\n * different types of views in a container.\n *\n * @param iterable The iterable to create an any_iterable from.\n * @return The any_iterable object.\n */\ntemplate<class Iterable>\nany_iterable<detail::val_iterable_t<Iterable>, detail::ref_iterable_t<Iterable>, detail::iter_cat_iterable_t<Iterable>,\n             detail::diff_iterable_t<Iterable>>\nmake_any_iterable(Iterable && iterable) {\n    return any_iterable<detail::val_iterable_t<Iterable>, detail::ref_iterable_t<Iterable>, detail::iter_cat_iterable_t<Iterable>,\n                        detail::diff_iterable_t<Iterable>>{ std::forward<Iterable>(iterable) };\n}\n\n} // namespace lz\n\n#endif // LZ_ANY_VIEW_HPP\n"
  },
  {
    "path": "include/Lz/as_iterator.hpp",
    "content": "#pragma once\n\n#ifndef LZ_AS_ITERATOR_HPP\n#define LZ_AS_ITERATOR_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/as_iterator.hpp>\n\nnamespace lz {\n\n/**\n * @brief Creates an `as_iterator_iterable` from an iterable. This means it will return its underling iterators instead of\n * using the `operator*`. This can be useful for when you need the iterator and the deref operator. The iterable has a size\n * method if the underlying iterable is sized. Returns the same sentinel as its underlying iterable. Example:\n * ```cpp\n * std::vector<int> vec = {1, 2, 3, 4, 5};\n * auto iter = lz::as_iterator(vec);\n * for (const auto vector_iterator : iter) {\n *     std::cout << *vector_iterator << \" \"; // prints: 1 2 3 4 5\n * }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::as_iterator_adaptor as_iterator{};\n\n/**\n * @brief Helper alias for as_iterator_iterable.\n *\n * @tparam Iterable The iterable\n * @tparam IterCat (Optional) The iterator category to decay the iterable to. If not provided, it will be deduced from the\n * iterable.\n */\ntemplate<class Iterable, class IterCat = detail::iter_cat_iterable_t<Iterable>>\nusing as_iterator_iterable = detail::as_iterator_iterable<Iterable, IterCat>;\n\n} // namespace lz\n\n#endif // LZ_AS_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/basic_iterable.hpp",
    "content": "#pragma once\n\n#ifndef LZ_BASIC_ITERABLE_HPP\n#define LZ_BASIC_ITERABLE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/procs/size.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class I, class S = I>\nclass basic_iterable_impl : public lazy_view {\n    // we decay I to decay it to a pointer, if it is an array\n    using iterator_type = typename std::decay<I>::type;\n    using sentinel_type = typename std::decay<S>::type;\n\n    iterator_type _begin{};\n    sentinel_type _end{};\n\n    using traits = std::iterator_traits<iterator_type>;\n\npublic:\n    using iterator = iterator_type;\n    using const_iterator = iterator_type;\n    using value_type = val_t<iterator_type>;\n    using sentinel = sentinel_type;\n\n    constexpr basic_iterable_impl(const basic_iterable_impl&) = default;\n    LZ_CONSTEXPR_CXX_14 basic_iterable_impl& operator=(const basic_iterable_impl&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr basic_iterable_impl()\n        requires(std::default_initializable<iterator_type> && std::default_initializable<sentinel_type>)\n    = default;\n\n#else\n\n    template<class B = decltype(_begin),\n             class = enable_if_t<std::is_default_constructible<B>::value && std::is_default_constructible<sentinel_type>::value>>\n    constexpr basic_iterable_impl() noexcept(std::is_nothrow_default_constructible<B>::value &&\n                                             std::is_nothrow_default_constructible<sentinel_type>::value) {\n    }\n\n#endif\n\n    template<class Iterable>\n    explicit constexpr basic_iterable_impl(Iterable&& iterable) :\n        basic_iterable_impl{ detail::begin(iterable), detail::end(iterable) } {\n    }\n\n    constexpr basic_iterable_impl(iterator_type begin, sentinel_type end) : _begin{ std::move(begin) }, _end{ std::move(end) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(is_ra_tag_v<typename traits::iterator_category>)\n    {\n        LZ_ASSERT(_end - _begin >= 0, \"Incompatible iterator\");\n        return static_cast<size_t>(_end - _begin);\n    }\n\n#else\n\n    template<class Tag = typename traits::iterator_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_ra_tag<Tag>::value, size_t> size() const {\n        LZ_ASSERT(_end - _begin >= 0, \"Incompatible iterator\");\n        return static_cast<size_t>(_end - _begin);\n    }\n\n#endif\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return _begin;\n    }\n\n    LZ_NODISCARD constexpr sentinel end() const {\n        return _end;\n    }\n};\n\ntemplate<class I, class S = I>\nclass sized_iterable_impl : public lazy_view {\n    // we decay I to decay it to a pointer, if it is an array\n    using iterator_type = typename std::decay<I>::type;\n    using sentinel_type = typename std::decay<S>::type;\n\n    I _begin;\n    S _end;\n    size_t _size{};\n\n    friend struct common_adaptor;\n\n    constexpr sized_iterable_impl(iterator_type begin, sentinel_type end, size_t size) :\n        _begin{ std::move(begin) },\n        _end{ std::move(end) },\n        _size{ size } {\n    }\n\npublic:\n    using iterator = I;\n    using const_iterator = iterator_type;\n    using value_type = val_t<iterator_type>;\n    using sentinel = S;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr sized_iterable_impl()\n        requires(std::default_initializable<iterator_type> && std::default_initializable<sentinel_type>)\n    = default;\n\n#else\n\n    template<class B = decltype(_begin),\n             class = enable_if_t<std::is_default_constructible<B>::value && std::is_default_constructible<sentinel_type>::value>>\n    constexpr sized_iterable_impl() noexcept(std::is_nothrow_default_constructible<B>::value &&\n                                             std::is_nothrow_default_constructible<sentinel_type>::value) {\n    }\n\n#endif\n\n    constexpr sized_iterable_impl(iterator_type begin, sentinel_type end) :\n        _begin{ std::move(begin) },\n        _end{ std::move(end) },\n        _size{ static_cast<size_t>(_end - _begin) } {\n    }\n\n    template<class Iterable>\n    explicit constexpr sized_iterable_impl(Iterable&& iterable) :\n        _begin{ detail::begin(iterable) },\n        _end{ detail::end(iterable) },\n        _size{ lz::size(iterable) } {\n    }\n\n    LZ_NODISCARD constexpr size_t size() const noexcept {\n        return _size;\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return _begin;\n    }\n\n    LZ_NODISCARD constexpr sentinel end() const {\n        return _end;\n    }\n};\n} // namespace detail\n} // namespace lz\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief A class that can be converted to any container. It only contains the iterators and\n * can be used in pipe expressions, converted to a container with `to<Container>()`, used in algorithms, for-each loops,\n * etc... It contains the size of the iterable.\n * @tparam It The iterator type.\n * @tparam S The sentinel type.\n */\ntemplate<class It, class S = It>\nusing sized_iterable = detail::sized_iterable_impl<It, S>;\n\n/**\n * @brief A class that can be converted to any container. It only contains the iterators and\n * can be used in pipe expressions, converted to a container with `to<Container>()`, used in algorithms, for-each loops,\n * etc... It *may* contain the size of the iterable, depending on the iterator category (needs to be random access).\n * @tparam It The iterator type.\n * @tparam S The sentinel type.\n */\ntemplate<class It, class S = It>\nusing basic_iterable = detail::basic_iterable_impl<It, S>;\n\n/**\n * @brief Creates a basic_iterable from an iterator and a sentinel. It can be used to create an iterable from a pair of\n * iterators.\n *\n * @param begin The begin iterator.\n * @param end The end sentinel.\n * @return A basic_iterable object\n */\ntemplate<class I, class S>\nLZ_NODISCARD constexpr basic_iterable<I, S> make_basic_iterable(I begin, S end) {\n    return { std::move(begin), std::move(end) };\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/c_string.hpp",
    "content": "#pragma once\n\n#ifndef LZ_C_STRING_HPP\n#define LZ_C_STRING_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/c_string.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n/**\n * @brief This adaptor is used to create a c forward iterable cstring iterable object. Its end() function will return a sentinel,\n * rather than an actual iterator. This iterable does not contain a .size() method. Example:\n * ```cpp\n * const char* str = \"Hello, World!\";\n * auto cstr = lz::c_string(str);\n * // or:\n * const char str[] = \"Hello, World!\";\n * auto cstr = str | lz::c_string;\n * ```\n */\nLZ_INLINE_VAR constexpr detail::c_string_adaptor c_string{};\n\n/**\n * @brief Helper alias for the c_string_iterable.\n * @tparam CharT The character type of the string.\n * Example:\n * ```cpp\n * lz::c_string_iterable<const char> cstr = lz::c_string(\"Hello, World!\");\n * ```\n*/\ntemplate<class CharT>\nusing c_string_iterable = detail::c_string_iterable<CharT>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/cached_size.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CACHED_SIZE_HPP\n#define LZ_CACHED_SIZE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/cached_size.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Creates an iterable with a size method. Gets the size eagerly (using `lz::eager_size`). Some iterables will traverse\n * its entire input iterable to get to its end, or to get to know its size. For instance `z = zip(filter(x))` will traverse\n * the entire sequence of `filter` in `filter::end()` if `filter` is not sentinelled and if `filter` is bidirectional or\n * stronger. Now if you were to call `z.end()` it would traverse the entire `filter` iterable each time you\n * call `z.end()`. This is not very efficient, so you can use `lz::cache_size` to cache the size of the iterable. This will\n * traverse the iterable once and cache the size, so that subsequent calls to `z.end()` will not traverse the iterable again,\n * but will return the cached size instead. Another solution would be to use `lz::iter_decay`, defined in\n * `<Lz/iter_tools.hpp>`, to decay the iterable to a forward one.\n *\n * The following iterables require a(n) (eagerly)sized iterable:\n * - `lz::chunks`\n * - `lz::enumerate`\n * - `lz::exclude`\n * - `lz::interleave`\n * - `lz::rotate`\n * - `lz::take`\n * - `lz::take_every`\n * - `lz::zip_longest`\n * - `lz::zip`\n *\n * Example:\n * ```cpp\n * auto to_filter = lz::range(10); // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n * // f isn't sized and is bidirectional\n * auto f = to_filter | lz::filter([](int i) { return i % 2 == 0; }); // {0, 2, 4, 6, 8}\n *\n * // Get the size first, then chunks and enumerate will use the cached size\n * auto iterable = f | lz::cache_size | lz::chunks(3) | lz::enumerate;\n *\n * // iterable = { {0, {0, 2, 4}}, {1, {6, 8}} }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::cached_size_adaptor cache_size{};\n\n/**\n * @brief Helper alias for the cached_size_iterable.\n * Example:\n * ```cpp\n * std::vector<int> vec = {1, 2, 3, 4, 5};\n * lz::cached_size_iterable<std::vector<int>> iterable = lz::cache_size(vec);\n * ```\n */\ntemplate<class Iterable>\nusing cached_size_iterable = detail::cached_size_iterable<Iterable>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/cartesian_product.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CARTESIAN_PRODUCT_HPP\n#define LZ_CARTESIAN_PRODUCT_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/cartesian_product.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Performs a cartesian product on the given iterables. This means that it will return all possible combinations\n * of the elements of the given iterables. Contains a .size() function if all of the iterables have a .size() function.\n * Returns a sentinel if one of the iterables has a forward iterator or a sentinel. Returns the same iterator category of the\n * 'weakest' input iterables. Example:\n * ```cpp\n * std::vector<int> a = {1, 2};\n * std::vector<int> b = {3, 4};\n * auto product = lz::cartesian_product(a, b); // product = {{1, 3}, {1, 4}, {2, 3}, {2, 4}}\n * // or\n * auto product = a | lz::cartesian_product(b); // product = {{1, 3}, {1, 4}, {2, 3}, {2, 4}}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::cartesian_product_adaptor cartesian_product{};\n\n/**\n * @brief Helper alias for the cartesian_product_iterable.\n * @tparam Iterables The types of the iterables.\n * Example:\n * ```cpp\n * std::vector<int> a = {1, 2};\n * std::vector<int> b = {3, 4};\n * lz::cartesian_product_iterable<std::vector<int>, std::vector<int>> product = lz::cartesian_product(a, b);\n * ```\n */\ntemplate<class... Iterables>\nusing cartesian_product_iterable = detail::cartesian_product_iterable<Iterables...>;\n\n} // namespace lz\n\n#endif // LZ_CARTESIAN_PRODUCT_HPP\n"
  },
  {
    "path": "include/Lz/chunk_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CHUNK_IF_HPP\n#define LZ_CHUNK_IF_HPP\n\n#include <Lz/detail/adaptors/chunk_if.hpp>\n#include <Lz/procs/chain.hpp>\n#include <Lz/util/string_view.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n#ifdef LZ_HAS_CXX_11\n\n/**\n * @brief This adaptor is used to make chunks of the iterable, based on a condition returned by the function passed. The iterator\n * category is forward, and returns a sentinel. It returns an iterable of iterables, with its value_type being\n * the template parameter passed. If `std::string` or `[lz|std]::string_view` is prefered as its `value_type`, please see\n * `sv_chunk_if`. This iterable does not contain a .size() method. Iterable is forward only.\n *\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * // Both return an iterable of std::vector<int>\n * auto chunked = lz::t_chunk_if<std::vector<int>>{}(vec, [](int i) { return i % 2 == 0; }); // chunked = { {1, 2}, {3, 4}, {5} } }\n * // or\n * auto chunked = vec | lz::t_chunk_if<std::vector<int>>{}([](int i) { return i % 2 == 0; }); // chunked = { {1, 2}, {3, 4}, {5} } }\n * ```\n * @tparam ValueType The value type of the chunked iterable.\n */\ntemplate<class ValueType>\nusing t_chunk_if = detail::chunk_if_adaptor<ValueType>;\n\n#else\n\n/**\n * @brief This adaptor is used to make chunks of the iterable, based on a condition returned by the function passed. The iterator\n * category is forward, and returns a sentinel. It returns an iterable of iterables, with its value_type being\n * the template parameter passed. If `std::string` or `[lz|std]::string_view` is prefered as its `value_type`, please see\n * `sv_chunk_if`. This iterable does not contain a .size() method. Iterable is forward only.\n *\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * // Both return an iterable of std::vector<int>\n * auto chunked = lz::t_chunk_if<std::vector<int>>(vec, [](int i) { return i % 2 == 0; }); // chunked = { {1, 2}, {3, 4}, {5} } }\n * // or\n * auto chunked = vec | lz::t_chunk_if<std::vector<int>>([](int i) { return i % 2 == 0; }); // chunked = { {1, 2}, {3, 4}, {5} } }\n * ```\n * @tparam ValueType The value type of the chunked iterable.\n */\ntemplate<class ValueType>\nLZ_INLINE_VAR constexpr detail::chunk_if_adaptor<ValueType> t_chunk_if;\n\n#endif\n\n/**\n * @brief This adaptor is used to make chunks of the iterable, based on a condition returned by the function passed. The iterator\n * category is forward, and returns a sentinel. It returns an iterable of iterables, with its value_type being\n * `lz::basic_iterable`. If `std::string` or `[lz|std]::string_view` is prefered as its `value_type`, please see\n * `sv_chunk_if`. This iterable does not contain a .size() method. Iterable is forward only.\n *\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto chunked = lz::chunk_if(vec, [](int i) { return i % 2 == 0; }); // chunked = { {1, 2}, {3, 4}, {5} }\n * // or\n * auto chunked = vec | lz::chunk_if([](int i) { return i % 2 == 0; }); // chunked = { {1, 2}, {3, 4}, {5} }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::chunk_if_adaptor<void> chunk_if{};\n\n/**\n * @brief This adaptor is used to make chunks of the iterable, based on a condition returned by the function passed. The iterator\n * category is forward, and returns a sentinel. It returns an iterable of `[std|lz]::string_view`. The input iterable must\n * therefore be random_access. This iterable does not contain a .size() method. Iterable is forward only.\n *\n * Example:\n * ```cpp\n * std::string s = \"hello;world;;\";\n * auto chunked = lz::sv_chunk_if(s, [](char c) { return c == ';'; });\n * // chunked = { string_view{\"hello\"}, string_view{\"world\"}, string_view{\"\"}, string_view{\"\"} }\n * // or\n * auto chunked = s | lz::sv_chunk_if([](char c) { return c == ';'; });\n * // chunked = { string_view{\"hello\"}, string_view{\"world\"}, string_view{\"\"}, string_view{\"\"} }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::chunk_if_adaptor<lz::string_view> sv_chunk_if{};\n\n/**\n * @brief Helper alias for the chunk_if_iterable.\n * @tparam Iterable The type of the input iterable.\n * @tparam UnaryPredicate The type of the predicate function.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * using chunked_t = lz::chunk_if_iterable<std::vector<int>, std::vector<int>, std::function<bool(int)>>;\n * chunked_t chunked = lz::chunk_if(vec, [](int i) { return i % 2 == 0; });\n * ```\n */\ntemplate<class Iterable, class UnaryPredicate>\nusing chunk_if_iterable = detail::chunk_if_iterable<basic_iterable<iter_t<Iterable>, iter_t<Iterable>>, Iterable, UnaryPredicate>;\n\n/**\n * @brief Helper alias for the chunk_if_iterable.\n * @tparam ValueType The value type of the chunked iterable which is returned by this iterator operator*.\n * @tparam Iterable The type of the input iterable.\n * @tparam UnaryPredicate The type of the predicate function.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * using chunked_t = lz::chunk_if_iterable<std::vector<int>, std::vector<int>, std::function<bool(int)>>;\n * chunked_t chunked = lz::chunk_if(vec, [](int i) { return i % 2 == 0; });\n * ```\n */\ntemplate<class ValueType, class Iterable, class UnaryPredicate>\nusing t_chunk_if_iterable = detail::chunk_if_iterable<ValueType, Iterable, UnaryPredicate>;\n\n/**\n * @brief Helper alias for the sv_chunk_if_iterable.\n * @tparam Iterable The type of the input iterable.\n * @tparam UnaryPredicate The type of the predicate function.\n * Example:\n * ```cpp\n * std::string s = \"hello;world;;\";\n * using chunked_t = lz::sv_chunk_if_iterable<std::string, std::function<bool(char)>>;\n * chunked_t chunked = lz::sv_chunk_if(vec, [](char i) { return i == ';'; });\n * ```\n */\ntemplate<class Iterable, class UnaryPredicate>\nusing sv_chunk_if_iterable =\n    detail::chunk_if_iterable<lz::basic_iterable<lz::string_view, lz::string_view>, Iterable, UnaryPredicate>;\n\n} // namespace lz\n\n#endif // LZ_CHUNK_IF_HPP\n"
  },
  {
    "path": "include/Lz/chunks.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CHUNKS_HPP\n#define LZ_CHUNKS_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/chunks.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief This adaptor is used to make chunks of the iterable, based on chunk size. The iterator\n * category is the same as its input iterable. It returns an iterable of iterables. Its end() function will return a sentinel,\n * if the input iterable has a forward iterator or has a sentinel. If its input iterable has a .size() method, then this iterable\n * will also have a .size() method.\n * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example),\n * the entire sequence is traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this\n * traversal alltogether, you can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache\n * the size of the iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go\n * backward, its entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however\n * will traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n * but will return the cached size instead.\n * The following iterables require a(n) (eagerly)sized iterable:\n * - `lz::chunks`\n * - `lz::enumerate`\n * - `lz::exclude`\n * - `lz::interleave`\n * - `lz::rotate`\n * - `lz::take`\n * - `lz::take_every`\n * - `lz::zip_longest`\n * - `lz::zip`\n *\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto chunked = lz::chunks(vec, 3); // chunked = { {1, 2, 3}, {4, 5} }\n * // or\n * auto chunked = vec | lz::chunks(3); // chunked = { {1, 2, 3}, {4, 5} }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::chunks_adaptor chunks{};\n\n/**\n * @brief This is the type of the iterable returned by `lz::chunks`.\n * @tparam Iterable The type of the input iterable.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * lz::chunks_iterable<std::vector<int>> chunked = lz::chunks(vec, 3);\n * ```\n */\ntemplate<class Iterable>\nusing chunks_iterable = detail::chunks_iterable<Iterable>;\n\n} // namespace lz\n\n#endif // LZ_CHUNKS_HPP\n"
  },
  {
    "path": "include/Lz/common.hpp",
    "content": "#pragma once\n\n#ifndef LZ_COMMON_HPP\n#define LZ_COMMON_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/common.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * Creates a common view from an iterator and a sentinel. If the iterable does not have a sentinel (i.e. its begin() function\n * must return a different type than its end() function), the iput iterable is returned. . Example:\n * ```cpp\n * auto c_str = lz::c_string(\"Hello, World!\"); // begin() and end() return different types\n * auto common_view = lz::common(c_str);\n * // or\n * auto common_view = lz::c_string(\"Hello, World!\") | lz::common;\n * // now you can use common_view in <algorithm> functions\n *\n *  // random access:\n * auto repeated = lz::repeat(20, 5); // begin() and end() return different types\n * auto common_view = lz::common(repeated); // common_view is a basic_iterable\n * // or\n * auto common_view = lz::repeat(20, 5) | lz::common;\n * ```\n * @attention Always try to use lz::common as late as possible. cpp-lazy is implemented in such a way that it will return\n * sentinels if possible to avoid duplicate data. So this for example:\n * ```cpp\n *  auto common = lz::c_string(\"Hello, World!\") | lz::common | lz::take(5);\n * ```\n * Will not result in an iterable where its end() and begin() functions return the same type. This is because\n * the common view is created first and then the take view is created. The take view will return another sentinel.\n */\nLZ_INLINE_VAR constexpr detail::common_adaptor common{};\n\n/**\n * @brief Helper type alias for the common iterable.\n * @tparam Iterable The iterable type.\n * ```cpp\n * lz::c_string_iterable<char> c_str = lz::c_string(\"Hello, World!\"); // begin() and end() return different types\n * lz::common_iterable<lz::c_string_iterable<char>> common_view = lz::common(c_str);\n * ```\n */\ntemplate<class Iterable>\nusing common_iterable = decltype(lz::detail::common_adaptor{}(std::declval<Iterable>()));\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/concatenate.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_CONCATENATE_HPP\r\n#define LZ_CONCATENATE_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/concatenate.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Concatenates multiple iterables into one iterable. The reference type returned by operator* will:\r\n * - be by value if one of the iterables yields by value\r\n * - be by const reference if one of the iterables yield by const reference.\r\n * - be by mutable reference if all iterables yield by mutable reference.\r\n * Contains a .size() function if all iterables have a .size()function. The size is the sum of all the sizes of the iterables.\r\n * Contains a sentinel if one of the iterables contains a sentinel. Its iterator category is the 'weakest' of the input\r\n * iterables. Example:\r\n * ```cpp\r\n * std::vector<int> a = {1, 2};\r\n * std::vector<int> b = {3, 4};\r\n * auto concatenated = lz::concat(a, b); // concatenated = {1, 2, 3, 4}\r\n * // or\r\n * auto concatenated = a | lz::concat(b); // concatenated = {1, 2, 3, 4}\r\n * auto range = lz::range(5, 10);\r\n * auto c = lz::concat(a, range); // yields by value, not by reference\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::concatenate_adaptor concat{};\r\n\r\n/**\r\n * @brief Helper alias for the concatenate iterable.\r\n * @tparam Iterables The iterables to concatenate.\r\n * ```cpp\r\n * std::vector<int> a = {1, 2};\r\n * std::vector<int> b = {3, 4};\r\n * lz::concatenate_iterable<std::vector<int>, std::vector<int>> concatenated = lz::concat(a, b);\r\n * ```\r\n */\r\ntemplate<class... Iterables>\r\nusing concatenate_iterable = detail::concatenate_iterable<Iterables...>;\r\n\r\n} // namespace lz\r\n\r\n#endif // LZ_CONCATENATE_HPP\r\n"
  },
  {
    "path": "include/Lz/detail/adaptors/as_iterator.hpp",
    "content": "#pragma once\n\n#ifndef LZ_AS_ITERATOR_ADAPTOR_HPP\n#define LZ_AS_ITERATOR_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/as_iterator.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class IterTag>\nusing is_input_tag = std::is_convertible<IterTag, std::input_iterator_tag>;\n\ntemplate<class IterTag>\nusing is_output_tag = std::is_convertible<IterTag, std::output_iterator_tag>;\n\nstruct as_iterator_adaptor {\n    using adaptor = as_iterator_adaptor;\n\n    /**\n     * @brief Creates an `as_iterator_iterable` from an iterable. This means it\n     * will return its underling iterators instead of using the `operator*`.\n     * This can be useful for when you need the iterator and the deref operator.\n     * The iterable has a size method if the underlying iterable is sized.\n     * Returns the same sentinel as its underlying iterable. Example:\n     * ```cpp\n     * std::vector<int> vec = {1, 2, 3, 4, 5};\n     * auto iter = lz::as_iterator(vec);\n     * for (const auto vector_iterator : iter) {\n     *     std::cout << *vector_iterator << \" \"; // prints: 1 2 3 4 5\n     * }\n     * ```\n     * @param iterable The iterable to get its iterators from.\n     * @return An `as_iterator_iterable` that contains the iterators of the\n     * iterable.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr as_iterator_iterable<remove_ref_t<Iterable>, detail::iter_cat_iterable_t<Iterable>>\n    operator()(Iterable&& iterable) const {\n        return as_iterator_iterable<remove_ref_t<Iterable>, detail::iter_cat_iterable_t<Iterable>>{ std::forward<Iterable>(\n            iterable) };\n    }\n\n    /**\n     * @brief Creates an `as_iterator_iterable` from an iterable and decays into\n     * the iterator category `IterCat`. This means it will return its underling\n     * iterators instead of using the `operator*` and only contains the\n     * operations associated with that iterator category. This can be useful for\n     * when you need the iterator and the deref operator and want to decay it to\n     * a lower iterator type. The iterable has a size method if the underlying\n     * iterable is sized. Returns the same sentinel as its underlying iterable.\n     * An example for random access iterators:\n     * ```cpp\n     * std::vector<int> vec = {1, 2, 3, 4, 5};\n     * auto iter = lz::as_iterator(vec, std::forward_iterator_tag{}); // decays\n     * to forward iterator for (const auto vector_iterator : iter) { std::cout\n     * << *vector_iterator << \" \"; // prints: 1 2 3 4 5\n     * }\n     * ```\n     * @param iterable The iterable to get its iterators from and decay to the\n     * iterator category `IterCat`.\n     * @param ic The iterator category to decay the iterable to\n     * @return An `as_iterator_iterable` that contains the iterators of the\n     * iterable decayed to the iterator category `IterCat`.\n     */\n    template<class Iterable, class IterCat>\n    LZ_NODISCARD constexpr as_iterator_iterable<remove_ref_t<Iterable>, IterCat>\n    operator()(Iterable&& iterable, IterCat ic) const {\n        return static_cast<void>(ic), as_iterator_iterable<remove_ref_t<Iterable>, IterCat>{ std::forward<Iterable>(iterable) };\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief Creates an `as_iterator_iterable` from an iterable and decays into\n     * the iterator category `IterCat`. This means it will return its underling\n     * iterators instead of using the `operator*` and only contains the\n     * operations associated with that iterator category. This can be useful for\n     * when you need the iterator and the deref operator and want to decay it to\n     * a lower iterator type. The iterable has a size method if the underlying\n     * iterable is sized. Returns the same sentinel as its underlying iterable.\n     * An example for random access iterators:\n     * ```cpp\n     * std::vector<int> vec = {1, 2, 3, 4, 5};\n     * auto iter = vec | lz::as_iterator(std::forward_iterator_tag{}); // decays\n     * to forward iterator for (const auto vector_iterator : iter) { std::cout\n     * << *vector_iterator << \" \"; // prints: 1 2 3 4 5\n     * }\n     * ```\n     * @param ic The iterator category to decay the iterable to\n     * @return An an adaptor that can be used in a pipe expression\n     */\n    template<class IterCat>\n    [[nodiscard]] constexpr fn_args_holder<adaptor, IterCat> operator()(IterCat ic) const\n        requires(is_input_tag<IterCat>::value || is_output_tag<IterCat>::value)\n    {\n        static_cast<void>(ic);\n        return {};\n    }\n\n#else\n    // clang-format off\n\n    /**\n     * @brief Creates an `as_iterator_iterable` from an iterable and decays into the iterator category `IterCat`. This means it\n     * will return its underling iterators instead of using the `operator*` and only contains the operations associated with that\n     * iterator category. This can be useful for when you need the iterator and the deref operator and want to decay it to a lower\n     * iterator type. The iterable has a size method if the underlying iterable is sized. Returns the same sentinel as its\n     * underlying iterable. An example for random access iterators:\n     * ```cpp\n     * std::vector<int> vec = {1, 2, 3, 4, 5};\n     * auto iter = vec | lz::as_iterator(std::forward_iterator_tag{}); // decays to forward iterator\n     * for (const auto vector_iterator : iter) {\n     *     std::cout << *vector_iterator << \" \"; // prints: 1 2 3 4 5\n     * }\n     * ```\n     * @param ic The iterator category to decay the iterable to\n     * @return An an adaptor that can be used in a pipe expression\n     */\n    template<class IterCat>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 \n    enable_if_t<is_input_tag<IterCat>::value || is_output_tag<IterCat>::value, fn_args_holder<adaptor, IterCat>>\n    operator()(IterCat ic) const {\n        static_cast<void>(ic);\n        return {};\n    }\n\n#endif\n\n    // clang-format on\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_AS_ITERATOR_ADAPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/c_string.hpp",
    "content": "#pragma once\n\n#ifndef LZ_C_STRING_ADAPTOR_HPP\n#define LZ_C_STRING_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/c_string.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct c_string_adaptor {\n    using adaptor = c_string_adaptor;\n\n    /**\n     * @brief This adaptor is used to create a forward iterable cstring iterable object. Its end() function will return a\n     * sentinel, rather than an actual iterator. This iterable does not contain a .size() method. Example:\n     * ```cpp\n     * const char* str = \"Hello, World!\";\n     * auto cstr = lz::c_string(str);\n     * // or:\n     * const char str[] = \"Hello, World!\";\n     * auto cstr = str | lz::c_string; // Only works for arrays\n     * ```\n     * @param str The string to create a cstring iterable from.\n     * @return A c_string_iterable object that can be used to iterate over the characters in the string.\n     **/\n    template<class C>\n    LZ_NODISCARD constexpr c_string_iterable<C> operator()(C* str) const noexcept {\n        return c_string_iterable<C>{ str };\n    }\n\n    /**\n     * @brief This adaptor is used to create a forward iterable cstring iterable object. Its end() function will return a\n     * sentinel, rather than an actual iterator. This iterable does not contain a .size() method. Example:\n     * ```cpp\n     * const char* str = \"Hello, World!\";\n     * auto cstr = lz::c_string(str);\n     * // or:\n     * const char str[] = \"Hello, World!\";\n     * auto cstr = str | lz::c_string; // Only works for arrays\n     * ```\n     * @param str The string to create a cstring iterable from.\n     * @return A c_string_iterable object that can be used to iterate over the characters in the string.\n     **/\n    template<class C>\n    LZ_NODISCARD constexpr c_string_iterable<const C> operator()(const C* str) const noexcept {\n        return c_string_iterable<const C>{ str };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/cached_size.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CACHED_SIZE_ADAPTOR_HPP\n#define LZ_CACHED_SIZE_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/cached_size.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct cached_size_adaptor {\n    using adaptor = cached_size_adaptor;\n\n    /**\n     * @brief Creates an iterable with a size method. Gets the size eagerly (using `lz::eager_size`). Some iterables will traverse\n     * its entire input iterable to get to its end, or to get to know its size. For instance `z = zip(filter(x))` will traverse\n     * the entire sequence of `filter` in `filter::end()` if `filter` is not sentinelled and if `filter` is bidirectional or\n     * stronger. Now if you were to call `z.end()` it would traverse the entire `filter` iterable each time you\n     * call `z.end()`. This is not very efficient, so you can use `lz::cache_size` to cache the size of the iterable. This will\n     * traverse the iterable once and cache the size, so that subsequent calls to `z.end()` will not traverse the iterable again,\n     * but will return the cached size instead. Another solution would be to use `lz::iter_decay`, defined in\n     * `<Lz/iter_tools.hpp>`, to decay the iterable to a forward one.\n     *\n     * The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * auto to_filter = lz::range(10); // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n     * // f isn't sized and is bidirectional\n     * auto f = to_filter | lz::filter([](int i) { return i % 2 == 0; }); // {0, 2, 4, 6, 8}\n     *\n     * // Get the size first, then chunks and enumerate will use the cached size\n     * auto iterable = f | lz::cache_size | lz::chunks(3) | lz::enumerate;\n     *\n     * // iterable = { {0, {0, 2, 4}}, {1, {6, 8}} }\n     * ```\n     * @param iterable The iterable to cache the size of\n     * @return A cached_size_iterable object that can be used to iterate over the elements in the iterable with a cached size.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr cached_size_iterable<remove_ref_t<Iterable>> operator()(Iterable&& iterable) const {\n        return cached_size_iterable<remove_ref_t<Iterable>>{ std::forward<Iterable>(iterable) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/cartesian_product.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CARTESIAN_PRODUCT_ADAPTOR_HPP\n#define LZ_CARTESIAN_PRODUCT_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/cartesian_product.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct cartesian_product_adaptor {\n    using adaptor = cartesian_product_adaptor;\n\n    /**\n     * @brief Performs a cartesian product on the given iterables. This means that it will return all possible combinations\n     * of the elements of the given iterables. Contains a .size() function if all of the iterables have a .size() function.\n     * Returns a sentinel if one of the iterables has a forward iterator or a sentinel. Example:\n     * ```cpp\n     * std::vector<int> a = {1, 2};\n     * std::vector<int> b = {3, 4};\n     * auto product = lz::cartesian_product(a, b); // product = {{1, 3}, {1, 4}, {2, 3}, {2, 4}}\n     * // or\n     * auto product = a | lz::cartesian_product(b); // product = {{1, 3}, {1, 4}, {2, 3}, {2, 4}}\n     * ```\n     * @param iterables The iterables to perform the cartesian product on.\n     * @return A cartesian_product_iterable containing the cartesian product of the given iterables.\n     */\n    template<class... Iterables>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 cartesian_product_iterable<remove_ref_t<Iterables>...>\n    operator()(Iterables&&... iterables) const {\n        return { std::forward<Iterables>(iterables)... };\n    }\n};\n\n} // namespace detail\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/chunk_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CHUNK_IF_ADAPTOR_HPP\n#define LZ_CHUNK_IF_ADAPTOR_HPP\n\n#include <Lz/basic_iterable.hpp>\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/chunk_if.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class ValueType>\nstruct chunk_if_adaptor {\n    using adaptor = chunk_if_adaptor<ValueType>;\n\n    /**\n     * @brief This adaptor is used to make chunks of the iterable, based on a condition returned by the function passed. The\n     * iterator category is forward and returns a sentinel. It returns an iterable of some type T of which T must be\n     * constructible from its input begin() and end(). So essentially: `T(input_iterable.begin(), input_iterable.end())`. Example:\n     * ```cpp\n     * template<class T>\n     * struct my_container {\n     *   template<class I, class S>\n     *   my_container(I begin, S end) {\n     *      // stuff\n     *   }\n     * };\n     * std::string s = \"hello;world;;\";\n     * lz::chunk_if_adaptor<my_container<char>> my_chunk_if_fn;\n     * auto chunked = my_chunk_if_fn(s, [](char c) { return c == ';'; });\n     * // chunked = { my_container<char>{\"hello\"}, my_container<char>{\"world\"}, my_container<char>{\"\"}, my_container<char>{\"\"} }\n     * ```\n     * @param iterable The iterable to chunk.\n     * @param predicate The predicate to chunk on.\n     * @return An iterable of type T, where T is constructible from the begin and end iterators of the input iterable.\n     */\n    template<class Iterable, class UnaryPredicate>\n    constexpr chunk_if_iterable<ValueType, remove_ref_t<Iterable>, UnaryPredicate>\n    operator()(Iterable&& iterable, UnaryPredicate predicate) const {\n        return { std::forward<Iterable>(iterable), std::move(predicate) };\n    }\n\n    /**\n     * @brief This adaptor is used to make chunks of the iterable, based on a condition returned by the function passed. The\n     * iterator category is forward and returns a sentinel. It returns an iterable of some type T of which T must be\n     * constructible from its input begin() and end(). So essentially: `T(input_iterable.begin(), input_iterable.end())`. Example:\n     * ```cpp\n     * template<class T>\n     * struct my_container {\n     *   template<class I, class S>\n     *   my_container(I begin, S end) {\n     *      // stuff\n     *   }\n     * };\n     * std::string s = \"hello;world;;\";\n     * lz::chunk_if_adaptor<my_container<char>> my_chunk_if_fn;\n     * auto chunked = s | my_chunk_if_fn([](char c) { return c == ';'; });\n     * // chunked = { my_container<char>{\"hello\"}, my_container<char>{\"world\"}, my_container<char>{\"\"}, my_container<char>{\"\"} }\n     * ```\n     * @param predicate The predicate to chunk on.\n     * @return An adaptor that can be used with the pipe operator to chunk the iterable.\n     */\n    template<class UnaryPredicate>\n    LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, UnaryPredicate> operator()(UnaryPredicate predicate) const {\n        return { std::move(predicate) };\n    }\n};\n\ntemplate<>\nstruct chunk_if_adaptor<void> {\n    using adaptor = chunk_if_adaptor<void>;\n\n    /**\n     * @brief This adaptor is used to make chunks of the iterable, based on a condition returned by the function passed. The\n     * iterator category is forward and returns a sentinel. It returns an iterable of iterables. This iterable does not contain\n     * a .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto chunked = lz::chunk_if(vec, [](int i) { return i % 2 == 0; }); // chunked = { {1, 2}, {3, 4}, {5} } }\n     * ```\n     * @param iterable The iterable to chunk.\n     * @param predicate The predicate to chunk on.\n     * @return An iterable of iterables, where each inner iterable is a chunk based on the predicate.\n     */\n    template<class Iterable, class UnaryPredicate>\n    constexpr chunk_if_iterable<basic_iterable<iter_t<Iterable>, iter_t<Iterable>>, remove_ref_t<Iterable>, UnaryPredicate>\n    operator()(Iterable&& iterable, UnaryPredicate predicate) const {\n        return { std::move(iterable), std::move(predicate) };\n    }\n\n    /**\n     * @brief This adaptor is used to make chunks of the iterable, based on a condition returned by the function passed. The\n     * iterator category is forward and returns a sentinel. It returns an iterable of iterables. This iterable does not contain a\n     * .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto chunked = vec | lz::chunk_if([](int i) { return i % 2 == 0; }); // chunked = { {1, 2}, {3, 4}, {5} } }\n     * ```\n     * @param predicate The predicate to chunk on.\n     * @return An adaptor that can be used with the pipe operator to chunk the iterable.\n     */\n    template<class UnaryPredicate>\n    constexpr fn_args_holder<adaptor, UnaryPredicate> operator()(UnaryPredicate predicate) const {\n        return { std::move(predicate) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/chunks.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CHUNKS_ADAPTOR_HPP\n#define LZ_CHUNKS_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/chunks.hpp>\n\nnamespace lz {\nnamespace detail {\n\nstruct chunks_adaptor {\n    using adaptor = chunks_adaptor;\n\n    /**\n     * @brief This adaptor is used to make chunks of the iterable, based on chunk size. The iterator\n     * category is the same as its input iterable. It returns an iterable of iterables. Its end() function will return a sentinel,\n     * if the input iterable has a forward iterator or has a sentinel.\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     *iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     *entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     *traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but\n     *will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto chunked = lz::chunks(vec, 3); // chunked = { {1, 2, 3}, {4, 5} }\n     * // or\n     * auto chunked = vec | lz::chunks(3); // chunked = { {1, 2, 3}, {4, 5} }\n     * ```\n     * @param iterable The iterable to chunk\n     * @param chunk_size The size of the chunks\n     * @return An iterable of iterables, where each inner iterable is a chunk of the original iterable\n     **/\n    template<class Iterable>\n    constexpr chunks_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, diff_iterable_t<Iterable> chunk_size) const {\n        return { std::forward<Iterable>(iterable), chunk_size };\n    }\n\n    /**\n     * @brief This adaptor is used to make chunks of the iterable, based on chunk size. The iterator\n     * category is the same as its input iterable. It returns an iterable of iterables. Its end() function will return a sentinel,\n     * if the input iterable has a forward iterator or has a sentinel. If its input iterable has a .size() method, then this\n     * iterable will also have a .size() method.\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     *iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     *entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     *traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but\n     *will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto chunked = vec | lz::chunks(3); // chunked = { {1, 2, 3}, {4, 5} }\n     * ```\n     * @param chunk_size The size of the chunks\n     * @return An adaptor that can be used to chunk the iterable\n     **/\n    LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, ptrdiff_t> operator()(const ptrdiff_t chunk_size) const {\n        return { chunk_size };\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/common.hpp",
    "content": "#pragma once\n\n#ifndef LZ_COMMON_ADAPTOR_HPP\n#define LZ_COMMON_ADAPTOR_HPP\n\n#include <Lz/basic_iterable.hpp>\n#include <Lz/detail/iterables/common.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct common_adaptor {\n    using adaptor = common_adaptor;\n\n#ifdef LZ_HAS_CXX_17\n\n    /**\n     * Creates a common view from an iterator and a sentinel. If the iterable does not have a sentinel (i.e. its begin() function\n     * must return a different type than its end() function), the input iterable is returned. Example:\n     * ```cpp\n     * auto c_str = lz::c_string(\"Hello, World!\"); // begin() and end() return different types\n     * auto common_view = lz::common(c_str);\n     * // or\n     * auto common_view = lz::c_string(\"Hello, World!\") | lz::common;\n     * // now you can use common_view in <algorithm> functions\n     *\n     * // random access:\n     * auto repeated = lz::repeat(20, 5); // begin() and end() return different types\n     * auto common_view = lz::common(repeated); // common_view is a basic_iterable\n     * // or\n     * auto common_view = lz::repeat(20, 5) | lz::common;\n     * ```\n     * @attention Always try to use lz::common as late as possible. cpp-lazy is implemented in such a way that it will return\n     * sentinels if possible to avoid duplicate data. So this for example:\n     * ```cpp\n     * auto common = lz::c_string(\"Hello, World!\") | lz::common | lz::take(5);\n     * ```\n     * Will not result in an iterable where its end() and begin() functions return the same type. This is because\n     * the common view is created first and then the take view is created. The take view will return another sentinel.\n     * @param iterable The iterable to create a common view from.\n     * @return A common iterable that can be used with algorithms that require a common iterator.\n     */\n    template<class Iterable>\n    [[nodiscard]] constexpr decltype(auto) operator()(Iterable&& iterable) const {\n        using it = iter_t<Iterable>;\n\n        if constexpr (!has_sentinel_v<Iterable>) {\n            return std::forward<Iterable>(iterable);\n        }\n        else if constexpr (is_ra_v<it>) {\n            const auto size = detail::end(iterable) - iterable.begin();\n            return basic_iterable<it>{ iterable.begin(), iterable.begin() + size };\n        }\n        else if constexpr (std::is_assignable_v<it, sentinel_t<Iterable>>) {\n            auto end = detail::begin(iterable);\n            end = detail::end(iterable);\n            if constexpr (is_sized_v<Iterable>) {\n                return sized_iterable<it>{ detail::begin(iterable), end, lz::size(iterable) };\n            }\n            else {\n                return make_basic_iterable(detail::begin(iterable), end);\n            }\n        }\n        else {\n            return common_iterable<remove_ref_t<Iterable>>{ std::forward<Iterable>(iterable) };\n        }\n    }\n\n#else\n\n    /**\n     * Creates a common view from an iterator and a sentinel. If the iterable does not have a sentinel (i.e. its begin() function\n     * must return a different type than its end() function), the input iterable is returned. Example:\n     * ```cpp\n     * auto c_str = lz::c_string(\"Hello, World!\"); // begin() and end() return different types\n     * auto common_view = lz::common(c_str);\n     * // or\n     * auto common_view = lz::c_string(\"Hello, World!\") | lz::common;\n     * // now you can use common_view in <algorithm> functions\n     *\n     * // random access:\n     * auto repeated = lz::repeat(20, 5); // begin() and end() return different types\n     * auto common_view = lz::common(repeated); // common_view is a basic_iterable\n     * // or\n     * auto common_view = lz::repeat(20, 5) | lz::common;\n     * ```\n     * @attention Always try to use lz::common as late as possible. cpp-lazy is implemented in such a way that it will return\n     * sentinels if possible to avoid duplicate data. So this for example:\n     * ```cpp\n     * auto common = lz::c_string(\"Hello, World!\") | lz::common | lz::take(5);\n     * ```\n     * Will not result in an iterable where its end() and begin() functions return the same type. This is because\n     * the common view is created first and then the take view is created. The take view will return another sentinel.\n     * @param iterable The iterable to create a common view from.\n     * @return A common iterable that can be used with algorithms that require a common iterator.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr enable_if_t<!has_sentinel<Iterable>::value, Iterable> operator()(Iterable&& iterable) const {\n        return std::forward<Iterable>(iterable);\n    }\n\n    /**\n     * Creates a common view from an iterator and a sentinel. If the iterable does not have a sentinel (i.e. its begin() function\n     * must return a different type than its end() function), the input iterable is returned. Example:\n     * ```cpp\n     * auto c_str = lz::c_string(\"Hello, World!\"); // begin() and end() return different types\n     * auto common_view = lz::common(c_str);\n     * // or\n     * auto common_view = lz::c_string(\"Hello, World!\") | lz::common;\n     * // now you can use common_view in <algorithm> functions\n     *\n     * // random access:\n     * auto repeated = lz::repeat(20, 5); // begin() and end() return different types\n     * auto common_view = lz::common(repeated); // common_view is a basic_iterable\n     * // or\n     * auto common_view = lz::repeat(20, 5) | lz::common;\n     * ```\n     * @attention Always try to use lz::common as late as possible. cpp-lazy is implemented in such a way that it will return\n     * sentinels if possible to avoid duplicate data. So this for example:\n     * ```cpp\n     * auto common = lz::c_string(\"Hello, World!\") | lz::common | lz::take(5);\n     * ```\n     * Will not result in an iterable where its end() and begin() functions return the same type. This is because\n     * the common view is created first and then the take view is created. The take view will return another sentinel.\n     * @param iterable The iterable to create a common view from.\n     * @return A common iterable that can be used with algorithms that require a common iterator.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr enable_if_t<has_sentinel<Iterable>::value &&\n                                           !std::is_assignable<iter_t<Iterable>, sentinel_t<Iterable>>::value &&\n                                           !is_ra<iter_t<Iterable>>::value,\n                                       common_iterable<remove_ref_t<Iterable>>>\n    operator()(Iterable&& iterable) const {\n        return common_iterable<remove_ref_t<Iterable>>{ std::forward<Iterable>(iterable) };\n    }\n\n    /**\n     * Creates a common view from an iterator and a sentinel. If the iterable does not have a sentinel (i.e. its begin() function\n     * must return a different type than its end() function), the input iterable is returned. Example:\n     * ```cpp\n     * auto c_str = lz::c_string(\"Hello, World!\"); // begin() and end() return different types\n     * auto common_view = lz::common(c_str);\n     * // or\n     * auto common_view = lz::c_string(\"Hello, World!\") | lz::common;\n     * // now you can use common_view in <algorithm> functions\n     *\n     * // random access:\n     * auto repeated = lz::repeat(20, 5); // begin() and end() return different types\n     * auto common_view = lz::common(repeated); // common_view is a basic_iterable\n     * // or\n     * auto common_view = lz::repeat(20, 5) | lz::common;\n     * ```\n     * @attention Always try to use lz::common as late as possible. cpp-lazy is implemented in such a way that it will return\n     * sentinels if possible to avoid duplicate data. So this for example:\n     * ```cpp\n     * auto common = lz::c_string(\"Hello, World!\") | lz::common | lz::take(5);\n     * ```\n     * Will not result in an iterable where its end() and begin() functions return the same type. This is because\n     * the common view is created first and then the take view is created. The take view will return another sentinel.\n     * @param iterable The iterable to create a common view from.\n     * @return A common iterable that can be used with algorithms that require a common iterator.\n     */\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14\n        enable_if_t<!is_ra<iter_t<Iterable>>::value && has_sentinel<Iterable>::value &&\n                        std::is_assignable<iter_t<Iterable>, sentinel_t<Iterable>>::value && !is_sized<Iterable>::value,\n                    basic_iterable<iter_t<Iterable>>>\n        operator()(Iterable&& iterable) const {\n        auto end = detail::begin(iterable);\n        end = detail::end(iterable);\n        return make_basic_iterable(detail::begin(iterable), end);\n    }\n\n    /**\n     * Creates a common view from an iterator and a sentinel. If the iterable does not have a sentinel (i.e. its begin() function\n     * must return a different type than its end() function), the input iterable is returned. Example:\n     * ```cpp\n     * auto c_str = lz::c_string(\"Hello, World!\"); // begin() and end() return different types\n     * auto common_view = lz::common(c_str);\n     * // or\n     * auto common_view = lz::c_string(\"Hello, World!\") | lz::common;\n     * // now you can use common_view in <algorithm> functions\n     *\n     * // random access:\n     * auto repeated = lz::repeat(20, 5); // begin() and end() return different types\n     * auto common_view = lz::common(repeated); // common_view is a basic_iterable\n     * // or\n     * auto common_view = lz::repeat(20, 5) | lz::common;\n     * ```\n     * @attention Always try to use lz::common as late as possible. cpp-lazy is implemented in such a way that it will return\n     * sentinels if possible to avoid duplicate data. So this for example:\n     * ```cpp\n     * auto common = lz::c_string(\"Hello, World!\") | lz::common | lz::take(5);\n     * ```\n     * Will not result in an iterable where its end() and begin() functions return the same type. This is because\n     * the common view is created first and then the take view is created. The take view will return another sentinel.\n     * @param iterable The iterable to create a common view from.\n     * @return A common iterable that can be used with algorithms that require a common iterator.\n     */\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14\n        enable_if_t<!is_ra<iter_t<Iterable>>::value && has_sentinel<Iterable>::value &&\n                        std::is_assignable<iter_t<Iterable>, sentinel_t<Iterable>>::value && is_sized<Iterable>::value,\n                    sized_iterable<iter_t<Iterable>>>\n        operator()(Iterable&& iterable) const {\n        auto end = detail::begin(iterable);\n        end = detail::end(iterable);\n        return { detail::begin(iterable), end, lz::size(iterable) };\n    }\n\n    /**\n     * Creates a common view from an iterator and a sentinel. If the iterable does not have a sentinel (i.e. its begin() function\n     * must return a different type than its end() function), the input iterable is returned. Example:\n     * ```cpp\n     * auto c_str = lz::c_string(\"Hello, World!\"); // begin() and end() return different types\n     * auto common_view = lz::common(c_str);\n     * // or\n     * auto common_view = lz::c_string(\"Hello, World!\") | lz::common;\n     * // now you can use common_view in <algorithm> functions\n     *\n     * // random access:\n     * auto repeated = lz::repeat(20, 5); // begin() and end() return different types\n     * auto common_view = lz::common(repeated); // common_view is a basic_iterable\n     * // or\n     * auto common_view = lz::repeat(20, 5) | lz::common;\n     * ```\n     * @attention Always try to use lz::common as late as possible. cpp-lazy is implemented in such a way that it will return\n     * sentinels if possible to avoid duplicate data. So this for example:\n     * ```cpp\n     * auto common = lz::c_string(\"Hello, World!\") | lz::common | lz::take(5);\n     * ```\n     * Will not result in an iterable where its end() and begin() functions return the same type. This is because\n     * the common view is created first and then the take view is created. The take view will return another sentinel.\n     * @param iterable The iterable to create a common view from.\n     * @return A common iterable that can be used with algorithms that require a common iterator.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_ra<iter_t<Iterable>>::value && has_sentinel<Iterable>::value,\n                                       basic_iterable<iter_t<Iterable>>>\n    operator()(Iterable&& iterable) const {\n        return basic_iterable<iter_t<Iterable>>{ iterable.begin(), iterable.begin() + (iterable.end() - iterable.begin()) };\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/concatenate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CONCATENATE_ADAPTOR_HPP\n#define LZ_CONCATENATE_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/concatenate.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct concatenate_adaptor {\n    using adaptor = concatenate_adaptor;\n\n    /**\n     * @brief Concatenates multiple iterables into one iterable. The reference type returned by operator* will:\n     * - be by value if one of the iterables yields by value\n     * - be by const reference if one of the iterables yield by const reference.\n     * - be by mutable reference if all iterables yield by mutable reference.\n     * Contains a .size() function if all iterables have a .size() function. The size is the sum of all the sizes of the\n     * iterables. Contains a sentinel if one of the iterables contains a sentinel or is forward. Its iterator category is the\n     * 'weakest' of the input iterables. Example:\n     * ```cpp\n     * std::vector<int> a = {1, 2};\n     * std::vector<int> b = {3, 4};\n     * auto concatenated = lz::concat(a, b); // concatenated = {1, 2, 3, 4}\n     * // or\n     * auto concatenated = a | lz::concat(b); // concatenated = {1, 2, 3, 4}\n     * auto range = lz::range(5, 10);\n     * auto c = lz::concat(a, range); // yields by value, not by reference\n     * ```\n     * @param iterables The iterables to concatenate\n     * @return An iterable that yields the concatenated elements of the input iterables.\n     */\n    template<class... Iterables>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 concatenate_iterable<remove_ref_t<Iterables>...> operator()(Iterables&&... iterables) const {\n        return { std::forward<Iterables>(iterables)... };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/drop.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DROP_ADAPTOR_HPP\n#define LZ_DROP_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/drop.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct drop_adaptor {\n    using adaptor = drop_adaptor;\n\n    /**\n     * @brief This adaptor is used to drop the first n elements of an iterable. The iterator category is the same as the input\n     * iterator category. Its end() function will return the same type as its input iterable. If its input iterable has a\n     * .size() method, then this iterable will also have a .size() method. If the amount to drop is larger than the size, size\n     * will be 0. Example:\n     * ```cpp\n     * auto vec = std::vector<int>{1, 2, 3, 4, 5};\n     * auto res = lz::drop(vec, 2); // res = {3, 4, 5}\n     * auto res = lz::drop(vec, 50); // res = {}, size = 0\n     * ```\n     * @param iterable The iterable to drop elements from\n     * @param n The amount of elements to drop\n     * @return A drop_iterable that will drop the first n elements of the input iterable\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr drop_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, const diff_iterable_t<Iterable> n) const {\n        return { iterable, n };\n    }\n\n    /**\n     * @brief This adaptor is used to drop the first n elements of an iterable. The iterator category is the same as the input\n     * iterator category. Its end() function will return the same type as its input iterable. If its input iterable has a\n     * .size() method, then this iterable will also have a .size() method. If the amount to drop is larger than the size, size\n     * will be 0. Example:\n     * ```cpp\n     * auto vec = std::vector<int>{1, 2, 3, 4, 5};\n     * auto res = vec | lz::drop(2); // res = {3, 4, 5}\n     * auto res = vec | lz::drop(50); // res = {}, size = 0\n     * ```\n     * @param n The amount of elements to drop\n     * @return An adaptor that can be used with pipe expressions\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, ptrdiff_t> operator()(const ptrdiff_t n) const {\n        return { n };\n    }\n};\n} // namespace lz\n} // namespace detail\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/drop_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DROP_WHILE_ADAPTOR_HPP\n#define LZ_DROP_WHILE_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/drop_while.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct drop_while_adaptor {\n    using adaptor = drop_while_adaptor;\n\n    /**\n     * @brief This adaptor is used to make an iterable where the iterator keeps dropping elements as long as the predicate returns\n     * `true`. Once it has returned `false`, it will return the elements as usual. The iterator category is the same as its input\n     * iterable. Its end() function will return a sentinel if its input iterable is forward or less or has a sentinel. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto dropped = lz::drop_while(vec, [](int i) { return i < 3; }); // dropped = { 3, 4, 5 }\n     * ```\n     * @param iterable The iterable to drop elements from\n     * @param unary_predicate The predicate to drop elements with\n     * @return An iterable that drops elements while the predicate returns true\n     */\n    template<class Iterable, class UnaryPredicate>\n    LZ_NODISCARD constexpr drop_while_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, UnaryPredicate unary_predicate) const {\n        return { std::forward<Iterable>(iterable), std::move(unary_predicate) };\n    }\n\n    /**\n     * @brief This adaptor is used to make an iterable where the iterator keeps dropping elements as long as the predicate returns\n     * `true`. Once it has returned `false`, it will return the elements as usual. The iterator category is the same as its input\n     * iterable. Its end() function will return a sentinel if its input iterable is forward or less or has a sentinel. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto dropped = vec | lz::drop_while([](int i) { return i < 3; }); // dropped = { 3, 4, 5 }\n     * ```\n     * @param unary_predicate The predicate to drop elements with\n     * @return An adaptor that can be used in a pipe expression\n     */\n    template<class UnaryPredicate>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, UnaryPredicate> operator()(UnaryPredicate unary_predicate) const {\n        return { std::move(unary_predicate) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/duplicates.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DUPLICATES_ADAPTOR_HPP\n#define LZ_DUPLICATES_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/duplicates.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct duplicates_adaptor {\n    using adaptor = duplicates_adaptor;\n\n    /**\n     * @brief This iterable returns a pair of each element in the iterable and the number of times it appears in the iterable.\n     * `pair::first` is the element and `pair::second` is the count. The count is `size_t` and starts from 1. The input\n     * iterable needs to be sorted first in order to count the duplicates. The iterator category is min(bidirectional, <input\n     * iterator category>). Its end() function will return a sentinel if the input iterable is forward or has a sentinel. This\n     * iterable does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> input{ 1, 2, 2, 3, 4, 4, 5 };\n     * auto dupes = lz::duplicates(input); // { { 1, 1 }, { 2, 2 }, { 3, 1 }, { 4, 2 }, { 5, 1 } }\n     * ```\n     * @param iterable The iterable to check the duplicates of.\n     * @param compare The binary predicate to compare the elements of the iterable. Defaults to `std::less`.\n     * @return An iterable that returns a pair of each element in the iterable and the number of times it appears in the iterable.\n     */\n    template<class Iterable, class BinaryPredicate = LZ_BIN_OP(less, val_iterable_t<Iterable>)>\n    LZ_NODISCARD constexpr duplicates_iterable<remove_ref_t<Iterable>, BinaryPredicate>\n    operator()(Iterable&& iterable, BinaryPredicate compare = {}) const {\n        return { std::forward<Iterable>(iterable), std::move(compare) };\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief This iterable returns a pair of each element in the iterable and the number of times it appears in the iterable.\n     * `pair::first` is the element and `pair::second` is the count. The count is `size_t` and starts from 1. The input\n     * iterable needs to be sorted first in order to count the duplicates. The iterator category is min(bidirectional, <input\n     * iterator category>). Its end() function will return a sentinel if the input iterable is forward or has a sentinel. This\n     * iterable does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> input{ 1, 2, 2, 3, 4, 4, 5 };\n     * auto dupes = input | lz::duplicates; // { { 1, 1 }, { 2, 2 }, { 3, 1 }, { 4, 2 }, { 5, 1 } }\n     * ```\n     * @param compare The binary predicate to compare the elements of the iterable. Defaults to `std::less`.\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class BinaryPredicate = std::less<>>\n    [[nodiscard]] constexpr fn_args_holder<adaptor, BinaryPredicate> operator()(BinaryPredicate compare = {}) const\n        requires(!iterable<BinaryPredicate>)\n    {\n        return { std::move(compare) };\n    }\n\n#else\n\n    /**\n     * @brief This iterable returns a pair of each element in the iterable and the number of times it appears in the iterable.\n     * `pair::first` is the element and `pair::second` is the count. The count is `size_t` and starts from 1. The input\n     * iterable needs to be sorted first in order to count the duplicates. The iterator category is min(bidirectional, <input\n     * iterator category>). Its end() function will return a sentinel if the input iterable is forward or has a sentinel. This\n     * iterable does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> input{ 1, 2, 2, 3, 4, 4, 5 };\n     * auto dupes = input | lz::duplicates; // { { 1, 1 }, { 2, 2 }, { 3, 1 }, { 4, 2 }, { 5, 1 } }\n     * ```\n     * @param compare The binary predicate to compare the elements of the iterable. Defaults to `std::less`.\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class BinaryPredicate>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_iterable<BinaryPredicate>::value, fn_args_holder<adaptor, BinaryPredicate>>\n    operator()(BinaryPredicate compare) const {\n        return { std::move(compare) };\n    }\n\n    /**\n     * @brief This iterable returns a pair of each element in the iterable and the number of times it appears in the iterable.\n     * `pair::first` is the element and `pair::second` is the count. The count is `size_t` and starts from 1. The input\n     * iterable needs to be sorted first in order to count the duplicates. The iterator category is min(bidirectional, <input\n     * iterator category>). Its end() function will return a sentinel if the input iterable is forward or has a sentinel. This\n     * iterable does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> input{ 1, 2, 2, 3, 4, 4, 5 };\n     * auto dupes = input | lz::duplicates; // { { 1, 1 }, { 2, 2 }, { 3, 1 }, { 4, 2 }, { 5, 1 } }\n     * ```\n     * @return An adaptor that can be used in pipe expressions\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor> operator()() const {\n        return {};\n    }\n\n#endif\n};\n\n#ifdef LZ_HAS_CONCEPTS\n\nLZ_MODULE_EXPORT template<class Iterable, class Adaptor>\n    requires(lz::iterable<Iterable>)\n[[nodiscard]] constexpr auto operator|(Iterable&& iterable, lz::detail::duplicates_adaptor) {\n    return lz::detail::duplicates_adaptor{}(std::forward<Iterable>(iterable), lz::detail::val_iterable_t<Iterable>{},\n                                            LZ_BIN_OP(less, lz::detail::val_iterable_t<Iterable>){});\n}\n\n#else\n\nLZ_MODULE_EXPORT template<class Iterable, class Adaptor>\nLZ_NODISCARD constexpr auto operator|(Iterable&& iterable, lz::detail::duplicates_adaptor) -> lz::detail::enable_if_t<\n    lz::detail::is_iterable<Iterable>::value,\n    decltype(lz::detail::duplicates_adaptor{}(std::forward<Iterable>(iterable), lz::detail::val_iterable_t<Iterable>{},\n                                              LZ_BIN_OP(less, lz::detail::val_iterable_t<Iterable>){}))> {\n    return lz::detail::duplicates_adaptor{}(std::forward<Iterable>(iterable), lz::detail::val_iterable_t<Iterable>{},\n                                            LZ_BIN_OP(less, lz::detail::val_iterable_t<Iterable>){});\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/enumerate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ENUMERATE_ADAPTOR_HPP\n#define LZ_ENUMERATE_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/enumerate.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct enumerate_adaptor {\n    using adaptor = enumerate_adaptor;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief Returns an iterable that enumerates the elements of the input iterable, meaning it returns a std::pair<IntType,\n     * ValueType>, where std::pair::first_type is an integer (corresponding to the current index) and std::pair::second_type is\n     * the value of the input iterable (by reference). The index starts at 0 by default, but can be changed by passing a start\n     * value to the function.\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::forward_list<int> list = {1, 2, 3, 4, 5};\n     * auto enumerated = lz::enumerate(list); // enumerated = { {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5} }\n     * auto enumerated = lz::enumerate(list, 5); // enumerated = { {5, 1}, {6, 2}, {7, 3}, {8, 4}, {9, 5} }\n     * ```\n     * Use lz::cache_size if:\n     * - Your iterable is exactly bidirectional (so forward/random access excluded) and\n     * - Your iterable is not sized and\n     * - Your iterable is not sentinelled\n     * - You either use multiple/a combination of the following iterables OR (see last point):\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     * - Are planning call end() multiple times on the same instance (with one or more of the above iterable\n     * combinations)\n     * @param iterable The iterable to enumerate\n     * @param start The start index of the enumeration\n     * @return An iterable that enumerates the elements of the input iterable\n     */\n    template<class Iterable, class IntType = int>\n    LZ_NODISCARD constexpr enumerate_iterable<remove_ref_t<Iterable>, IntType>\n    operator()(Iterable&& iterable, const IntType start = 0) const\n        requires lz::iterable<Iterable>\n    {\n\n        return { std::forward<Iterable>(iterable), start };\n    }\n\n    /**\n     * @brief Returns an iterable that enumerates the elements of the input iterable, meaning it returns a std::pair<IntType,\n     * ValueType>, where std::pair::first_type is an integer (corresponding to the current index) and std::pair::second_type is\n     * the value of the input iterable (by reference). The index starts at 0 by default, but can be changed by passing a start\n     * value to the function.\n     *\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::forward_list<int> list = {1, 2, 3, 4, 5};\n     * auto enumerated = list | lz::enumerate; // enumerated = { {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5} }\n     * auto enumerated = list | lz::enumerate(5); // enumerated = { {5, 1}, {6, 2}, {7, 3}, {8, 4}, {9, 5} }\n     * ```\n     * Use lz::cache_size if:\n     * - Your iterable is exactly bidirectional (so forward/random access excluded) and\n     * - Your iterable is not sized and\n     * - Your iterable is not sentinelled\n     * - You either use multiple/a combination of the following iterables OR (see last point):\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     * - Are planning call end() multiple times on the same instance (with one or more of the above iterable\n     * combinations)\n     * @param start The start index of the enumeration\n     * @return An adaptor that can be used with a pipe operator\n     */\n    template<class IntType = int>\n    [[nodiscard]] constexpr fn_args_holder<adaptor, IntType> operator()(const IntType start = 0) const\n        requires(!lz::iterable<IntType>)\n    {\n        return { start };\n    }\n\n#else\n\n    /**\n     * @brief Returns an iterable that enumerates the elements of the input iterable, meaning it returns a std::pair<IntType,\n     * ValueType>, where std::pair::first_type is an integer (corresponding to the current index) and std::pair::second_type is\n     * the value of the input iterable (by reference). The index starts at 0 by default, but can be changed by passing a start\n     * value to the function.\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::forward_list<int> list = {1, 2, 3, 4, 5};\n     * auto enumerated = lz::enumerate(list); // enumerated = { {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5} }\n     * auto enumerated = lz::enumerate(list, 5); // enumerated = { {5, 1}, {6, 2}, {7, 3}, {8, 4}, {9, 5} }\n     * ```\n     * Use lz::cache_size if:\n     * - Your iterable is exactly bidirectional (so forward/random access excluded) and\n     * - Your iterable is not sized and\n     * - Your iterable is not sentinelled\n     * - You either use multiple/a combination of the following iterables OR (see last point):\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     * - Are planning call end() multiple times on the same instance (with one or more of the above iterable\n     * combinations)\n     * @param iterable The iterable to enumerate\n     * @param start The start index of the enumeration\n     * @return An iterable that enumerates the elements of the input iterable\n     */\n    template<class Iterable, class IntType = int>\n    LZ_NODISCARD constexpr enable_if_t<is_iterable<Iterable>::value, enumerate_iterable<remove_ref_t<Iterable>, IntType>>\n    operator()(Iterable&& iterable, const IntType start = 0) const {\n        return { std::forward<Iterable>(iterable), start };\n    }\n\n    /**\n     * @brief Returns an iterable that enumerates the elements of the input iterable, meaning it returns a std::pair<IntType,\n     * ValueType>, where std::pair::first_type is an integer (corresponding to the current index) and std::pair::second_type is\n     * the value of the input iterable (by reference). The index starts at 0 by default, but can be changed by passing a start\n     * value to the function.\n     *\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::forward_list<int> list = {1, 2, 3, 4, 5};\n     * auto enumerated = list | lz::enumerate; // enumerated = { {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5} }\n     * auto enumerated = list | lz::enumerate(5); // enumerated = { {5, 1}, {6, 2}, {7, 3}, {8, 4}, {9, 5} }\n     * ```\n     * Use lz::cache_size if:\n     * - Your iterable is exactly bidirectional (so forward/random access excluded) and\n     * - Your iterable is not sized and\n     * - Your iterable is not sentinelled\n     * - You either use multiple/a combination of the following iterables OR (see last point):\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     * - Are planning call end() multiple times on the same instance (with one or more of the above iterable\n     * combinations)\n     * @param start The start index of the enumeration\n     * @return An adaptor that can be used with a pipe operator\n     */\n    template<class IntType = int>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_iterable<IntType>::value, fn_args_holder<adaptor, IntType>>\n    operator()(const IntType start = 0) const {\n        return { start };\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_ENUMERATE_ADAPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/except.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCEPT_ADAPTOR_HPP\n#define LZ_EXCEPT_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/except.hpp>\n#include <Lz/detail/procs/operators.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct except_adaptor {\n    using adaptor = except_adaptor;\n\n    /**\n     * @brief Excepts an iterable with another iterable. This means that it returns every item that is not in the second iterable\n     * argument. Can be used with a custom comparer. Does not contain a .size() method, it's a bidirectional iterator if the first\n     * iterable is at least bidirectional and returns a sentinel if its first iterable has a sentinel or is forward only. The\n     * second iterable must be sorted in order for it to work. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * std::vector<int> to_except = { 5, 3 };\n     * std::sort(to_except.begin(), to_except.end());\n     * auto excepted = lz::except(vec, to_except); // excepted = { 1, 2, 4 }\n     * // or\n     * auto excepted = lz::except(vec, to_except, std::less<int>{}); // excepted = { 1, 2, 4 }\n     * ```\n     * @param iterable1 The iterable to except.\n     * @param iterable2 The iterable that must be skipped in @p iterable1.\n     * @param binary_predicate The binary predicate to use for comparison.\n     * @return An iterable that contains every item from @p iterable1 that is not in @p iterable2.\n     */\n    template<class Iterable1, class Iterable2, class BinaryPredicate = LZ_BIN_OP(less, val_iterable_t<Iterable2>)>\n    LZ_NODISCARD constexpr except_iterable<remove_ref_t<Iterable1>, remove_ref_t<Iterable2>, BinaryPredicate>\n    operator()(Iterable1&& iterable1, Iterable2&& iterable2, BinaryPredicate binary_predicate = {}) const {\n        return { std::forward<Iterable1>(iterable1), std::forward<Iterable2>(iterable2), std::move(binary_predicate) };\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief Excepts an iterable with another iterable. This means that it returns every item that is not in the second iterable\n     * argument. Can be used with a custom comparer. Does not contain a .size() method, it's a bidirectional iterator if the first\n     * iterable is at least bidirectional and returns a sentinel if its first iterable has a sentinel or is forward only. The\n     * second iterable must be sorted in order for it to work.\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * std::vector<int> to_except = { 5, 3 };\n     * std::sort(to_except.begin(), to_except.end());\n     * auto excepted = lz::except(vec, to_except); // excepted = { 1, 2, 4 }\n     * // or\n     * auto excepted = lz::except(vec, to_except, std::less<int>{}); // excepted = { 1, 2, 4 }\n     * // or\n     * auto excepted = vec | lz::except(to_except); // excepted = { 1, 2, 4 }\n     * // or\n     * auto excepted = vec | lz::except(to_except, std::less<int>{}); // excepted = { 1, 2, 4 }\n     * ```\n     * @param iterable2 The iterable that must be skipped in @p iterable1.\n     * @param binary_predicate The binary predicate to use for comparison.\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class Iterable2, class BinaryPredicate = LZ_BIN_OP(less, val_iterable_t<Iterable2>)>\n    [[nodiscard]] constexpr fn_args_holder<adaptor, Iterable2, BinaryPredicate>\n    operator()(Iterable2&& iterable2, BinaryPredicate binary_predicate = {}) const\n        requires(!lz::iterable<BinaryPredicate>)\n    {\n        return { std::forward<Iterable2>(iterable2), std::move(binary_predicate) };\n    }\n\n#else\n\n    /**\n     * @brief Excepts an iterable with another iterable. This means that it returns every item that is not in the second iterable\n     * argument. Can be used with a custom comparer. Does not contain a .size() method, it's a bidirectional iterator if the first\n     * iterable is at least bidirectional and returns a sentinel if its first iterable has a sentinel or is forward only. The\n     * second iterable must be sorted in order for it to work.\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * std::vector<int> to_except = { 5, 3 };\n     * std::sort(to_except.begin(), to_except.end());\n     * auto excepted = lz::except(vec, to_except); // excepted = { 1, 2, 4 }\n     * // or\n     * auto excepted = lz::except(vec, to_except, std::less<int>{}); // excepted = { 1, 2, 4 }\n     * // or\n     * auto excepted = vec | lz::except(to_except); // excepted = { 1, 2, 4 }\n     * // or\n     * auto excepted = vec | lz::except(to_except, std::less<int>{}); // excepted = { 1, 2, 4 }\n     * ```\n     * @param iterable2 The iterable that must be skipped in @p iterable1.\n     * @param binary_predicate The binary predicate to use for comparison.\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class Iterable2, class BinaryPredicate = LZ_BIN_OP(less, val_iterable_t<Iterable2>)>\n    LZ_NODISCARD\n        LZ_CONSTEXPR_CXX_14 enable_if_t<!is_iterable<BinaryPredicate>::value, fn_args_holder<adaptor, Iterable2, BinaryPredicate>>\n        operator()(Iterable2&& iterable2, BinaryPredicate binary_predicate = {}) const {\n        return { std::forward<Iterable2>(iterable2), std::move(binary_predicate) };\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_EXCEPT_ADAPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/exclude.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCLUDE_ADAPTOR_HPP\n#define LZ_EXCLUDE_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/exclude.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct exclude_adaptor {\n    using adaptor = exclude_adaptor;\n\n    /**\n     * @brief Excludes elements from a container, using two indexes. The first index is means the start index, the second index\n     * means the end index. Contains a .size() method if the input iterable contains a .size() method, its iterator category is\n     * the same as the input iterable. Returns a sentinel if input iterable is forward or has a sentinel.\n     *\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n     * // Exclude index [3, 6)\n     * auto excluded = lz::exclude(vec, 3, 6); // excluded = { 1, 2, 3, 6, 7, 8, 9 }\n     * ```\n     * @param iterable The iterable to exclude elements from.\n     * @param from The start index to exclude.\n     * @param to The end index to exclude.\n     * @return An iterable that excludes the elements from the specified range.\n     */\n    template<class Iterable, class DiffType>\n    LZ_NODISCARD constexpr exclude_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, DiffType from, DiffType to) const {\n        return { std::forward<Iterable>(iterable), from, to };\n    }\n\n    /**\n     * @brief Excludes elements from a container, using two indexes. The first index is means the start index, the second index\n     * means the end index. Contains a .size() method if the input iterable contains a .size() method, its iterator category is\n     * the same as the input iterable. Returns a sentinel if input iterable is forward or has a sentinel.\n     *\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n     * // Exclude index [3, 6)\n     * auto excluded = vec | lz::exclude(3, 6); // excluded = { 1, 2, 3, 6, 7, 8, 9 }\n     * ```\n     * @param from The start index to exclude.\n     * @param to The end index to exclude.\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class DiffType>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, DiffType, DiffType> operator()(DiffType from, DiffType to) const {\n        return { from, to };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/exclusive_scan.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCLUSIVE_SCAN_ADAPTOR_HPP\n#define LZ_EXCLUSIVE_SCAN_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/exclusive_scan.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/is_invocable.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct exclusive_scan_adaptor {\n    using adaptor = exclusive_scan_adaptor;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief Performs an exclusive scan on a container. The first element will be the init value, the second element will be the\n     * first element of the container + the init value, the third element will be the second element + last returned element\n     * previously, etc. It contains a .size() method if the input iterable also has a .size() method. Its end() function returns a\n     * sentinel rather than an iterator. Its iterator category is forward. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * // std::plus is used as the default binary operation\n     * auto scan = lz::exclusive_scan(vec, 0); // scan = { 0, 1, 3, 6, 10, 15 }\n     * // so 0 (= 0), 0 + 1 (= 1), 1 + 2 (= 3), 3 + 3 (= 6), 6 + 4 (= 10), 10 + 5 (= 15)\n     *\n     * // you can also add a custom operator:\n     * auto scan = vec | lz::exclusive_scan(0, std::plus<int>{}); // scan = { 0, 1, 3, 6, 10, 15 }\n     * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions,\n     * // you can omit the init value, in which case it will be a default constructed object of the type of the container.\n     * // Example\n     * auto scan = lz::exclusive_scan(vec);\n     * auto scan = lz::exclusive_scan(vec, 0);\n     * // auto scan = vec | lz::exclusive_scan; // uses 0 and std::plus\n     * auto scan = vec | lz::exclusive_scan(0);\n     * ```\n     * @param iterable The iterable to perform the exclusive scan on.\n     * @param init The initial value to start the scan with.\n     * @param binary_op The binary operation to perform on the elements. Plus by default.\n     * @return An iterable that performs the exclusive scan on the specified iterable.\n     */\n    template<class Iterable, class T = val_iterable_t<Iterable>, class BinaryOp = LZ_BIN_OP(plus, T)>\n    [[nodiscard]] constexpr exclusive_scan_iterable<remove_ref_t<Iterable>, remove_cvref_t<T>, BinaryOp>\n    operator()(Iterable&& iterable, T&& init = {}, BinaryOp binary_op = {}) const\n        requires(std::invocable<BinaryOp, ref_iterable_t<Iterable>, remove_cvref_t<T>>)\n    {\n        return { std::forward<Iterable>(iterable), std::forward<T>(init), std::move(binary_op) };\n    }\n\n    /**\n     * @brief Performs an exclusive scan on a container. The first element will be the init value, the second element will be the\n     * first element of the container + the init value, the third element will be the second element + last returned element\n     * previously, etc. It contains a .size() method if the input iterable also has a .size() method. Its end() function returns a\n     * sentinel rather than an iterator. Its iterator category is forward. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * // std::plus is used as the default binary operation\n     *\n     * // you can also add a custom operator:\n     * auto scan = vec | lz::exclusive_scan(0, std::plus<int>{}); // scan = { 0, 1, 3, 6, 10, 15 }\n     * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions,\n     * you\n     * // can omit the init value, in which case it will be a default constructed object of the type of the container.\n     * // Example\n     * auto scan = lz::exclusive_scan(vec);\n     * auto scan = lz::exclusive_scan(vec, 0);\n     * auto scan = vec | lz::exclusive_scan; // uses 0 and std::plus\n     * auto scan = vec | lz::exclusive_scan(0);\n     * ```\n     * @param init The initial value to start the scan with.\n     * @param binary_op The binary operation to perform on the elements. Plus by default.\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class T, class BinaryOp = LZ_BIN_OP(plus, T)>\n    [[nodiscard]] constexpr fn_args_holder<adaptor, remove_cvref_t<T>, BinaryOp>\n    operator()(T&& init, BinaryOp binary_op = {}) const\n        requires(std::invocable<BinaryOp, remove_cvref_t<T>, remove_cvref_t<T>>)\n    {\n        return { std::forward<T>(init), std::move(binary_op) };\n    }\n\n#else\n\n    /**\n     * @brief Performs an exclusive scan on a container. The first element will be the init value, the second element will be the\n     * first element of the container + the init value, the third element will be the second element + last returned element\n     * previously, etc. It contains a .size() method if the input iterable also has a .size() method. Its end() function returns a\n     * sentinel rather than an iterator. Its iterator category is forward. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * // std::plus is used as the default binary operation\n     * auto scan = lz::exclusive_scan(vec, 0); // scan = { 0, 1, 3, 6, 10, 15 }\n     * // so 0 (= 0), 0 + 1 (= 1), 1 + 2 (= 3), 3 + 3 (= 6), 6 + 4 (= 10), 10 + 5 (= 15)\n     *\n     * // you can also add a custom operator:\n     * auto scan = vec | lz::exclusive_scan(0, std::plus<int>{}); // scan = { 0, 1, 3, 6, 10, 15 }\n     * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions,\n     * // you can omit the init value, in which case it will be a default constructed object of the type of the container.\n     * // Example\n     * auto scan = lz::exclusive_scan(vec);\n     * auto scan = lz::exclusive_scan(vec, 0);\n     * // auto scan = vec | lz::exclusive_scan; // uses 0 and std::plus\n     * auto scan = vec | lz::exclusive_scan(0);\n     * ```\n     * @param iterable The iterable to perform the exclusive scan on.\n     * @param init The initial value to start the scan with.\n     * @param binary_op The binary operation to perform on the elements. Plus by default.\n     * @return An iterable that performs the exclusive scan on the specified iterable.\n     */\n    template<class Iterable, class T = val_iterable_t<Iterable>, class BinaryOp = LZ_BIN_OP(plus, val_iterable_t<Iterable>)>\n    LZ_NODISCARD constexpr enable_if_t<is_invocable<BinaryOp, ref_iterable_t<Iterable>, remove_cvref_t<T>>::value,\n                                       exclusive_scan_iterable<remove_ref_t<Iterable>, remove_cvref_t<T>, BinaryOp>>\n    operator()(Iterable&& iterable, T&& init = {}, BinaryOp binary_op = {}) const {\n        return { std::forward<Iterable>(iterable), std::forward<T>(init), std::move(binary_op) };\n    }\n\n    // clang-format off\n    /**\n     * @brief Performs an exclusive scan on a container. The first element will be the init value, the second element will be the\n     * first element of the container + the init value, the third element will be the second element + last returned element\n     * previously, etc. It contains a .size() method if the input iterable also has a .size() method. Its end() function returns a\n     * sentinel rather than an iterator. Its iterator category is forward. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * // std::plus is used as the default binary operation\n     *\n     * // you can also add a custom operator:\n     * auto scan = vec | lz::exclusive_scan(0, std::plus<int>{}); // scan = { 0, 1, 3, 6, 10, 15 }\n     * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions,\n     * you\n     * // can omit the init value, in which case it will be a default constructed object of the type of the container.\n     * // Example\n     * auto scan = lz::exclusive_scan(vec); \n     * auto scan = lz::exclusive_scan(vec, 0);\n     * auto scan = vec | lz::exclusive_scan; // uses 0 and std::plus\n     * auto scan = vec | lz::exclusive_scan(0);\n     * ```\n     * @param init The initial value to start the scan with.\n     * @param binary_op The binary operation to perform on the elements. Plus by default.\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class T, class BinaryOp = LZ_BIN_OP(plus, T)>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14\n    enable_if_t<is_invocable<BinaryOp, remove_cvref_t<T>, remove_cvref_t<T>>::value, fn_args_holder<adaptor, remove_cvref_t<T>, BinaryOp>>\n    operator()(T&& init, BinaryOp binary_op = {}) const {\n        return { std::forward<T>(init), std::move(binary_op) };\n    }\n    // clang-format on\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#ifdef LZ_HAS_CONCEPTS\n\nLZ_MODULE_EXPORT template<class Iterable, class Adaptor>\n    requires(lz::iterable<Iterable>)\n[[nodiscard]] constexpr auto operator|(Iterable&& iterable, lz::detail::exclusive_scan_adaptor) {\n    return lz::detail::exclusive_scan_adaptor{}(std::forward<Iterable>(iterable), lz::detail::val_iterable_t<Iterable>{},\n                                                std::plus<>{});\n}\n\n#else\n\nLZ_MODULE_EXPORT template<class Iterable, class Adaptor>\nLZ_NODISCARD constexpr auto operator|(Iterable&& iterable, lz::detail::exclusive_scan_adaptor)\n    -> lz::detail::enable_if_t<lz::detail::is_iterable<Iterable>::value,\n                               decltype(lz::detail::exclusive_scan_adaptor{}(\n                                   std::forward<Iterable>(iterable), LZ_BIN_OP(plus, lz::detail::val_iterable_t<Iterable>){}))> {\n    return lz::detail::exclusive_scan_adaptor{}(std::forward<Iterable>(iterable),\n                                                LZ_BIN_OP(plus, lz::detail::val_iterable_t<Iterable>){});\n}\n\n#endif\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/filter.hpp",
    "content": "#pragma once\n\n#ifndef LZ_FILTER_ADAPTOR_HPP\n#define LZ_FILTER_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/filter.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct filter_adaptor {\n    using adaptor = filter_adaptor;\n\n    /**\n     * @brief Drops elements based on a condition predicate. If it returns `false`, the element in question is dropped. If it\n     * returns `true` then this item is will be yielded. If the input iterable is forward an end() sentinel is returned, otherwise\n     * the end() iterator is the same as the begin() iterator. It is bidirectional if the input iterable is also at least\n     * bidirectional. It does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto filtered = lz::filter(vec, [](int i) { return i % 2 == 0; }); // { 2, 4 }\n     * ```\n     * @param iterable The iterable to filter\n     * @param predicate The predicate to filter on\n     * @return A filter_iterable that will yield the filtered elements\n     */\n    template<class Iterable, class UnaryPredicate>\n    LZ_NODISCARD constexpr filter_iterable<remove_ref_t<Iterable>, UnaryPredicate>\n    operator()(Iterable&& iterable, UnaryPredicate predicate) const {\n        return { std::forward<Iterable>(iterable), std::move(predicate) };\n    }\n\n    /**\n     * @brief Drops elements based on a condition predicate. If it returns `false`, the element in question is dropped. If it\n     * returns `true` then this item is will be yielded. If the input iterable is forward an end() sentinel is returned, otherwise\n     * the end() iterator is the same as the begin() iterator. It is bidirectional if the input iterable is also at least\n     * bidirectional. It does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto filtered = vec | lz::filter([](int i) { return i % 2 == 0; }); // { 2, 4 }\n     * ```\n     * @param predicate The predicate to filter on\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class UnaryPredicate>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, UnaryPredicate> operator()(UnaryPredicate predicate) const {\n        return { std::move(predicate) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/flatten.hpp",
    "content": "#pragma once\n\n#ifndef LZ_FLATTEN_ADAPTOR_HPP\n#define LZ_FLATTEN_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/flatten.hpp>\n\nnamespace lz {\n\ntemplate<class, class = void>\nstruct dimensions;\n\n/**\n * @brief Gets the number of dimensions of an iterable. For instance, a vector of vectors will return 2, a vector of vectors of\n * vectors will return 3, etc. Example:\n * ```cpp\n * std::vector<std::vector<int>> vectors = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\n * auto dim = lz::dimensions<decltype(vectors)>::value; // 2\n * ```\n * @tparam Iterable The iterable type to get the dimensions of.\n */\ntemplate<class Iterable>\nstruct dimensions<Iterable, detail::enable_if_t<!std::is_array<Iterable>::value>> : detail::count_dims<Iterable> {};\n\n/**\n * @brief Gets the number of dimensions of an iterable. For instance, a vector of vectors will return 2, a vector of vectors of\n * vectors will return 3, etc. Example:\n * ```cpp\n * int arrs[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n * auto dim = lz::dimensions<decltype(arrs)>::value; // 2\n * ```\n * @tparam Iterable The iterable type to get the dimensions of.\n */\ntemplate<class Iterable>\nstruct dimensions<Iterable, detail::enable_if_t<std::is_array<Iterable>::value>>\n    : std::integral_constant<size_t, std::rank<detail::remove_cvref_t<Iterable>>::value> {};\n\n#ifdef LZ_HAS_CXX_14\n\n/**\n * @brief Gets the number of dimensions of an iterable. For instance, a vector of vectors will return 2, a vector of vectors of\n * vectors will return 3, etc. Example:\n * ```cpp\n * int arrs[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n * auto dim = lz::dimensions_v<decltype(arrs)>; // 2\n * std::vector<std::vector<int>> vectors = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\n * auto dim2 = lz::dimensions_v<decltype(vectors)>; // 2\n * ```\n * @tparam Iterable The iterable type to get the dimensions of.\n */\ntemplate<class Iterable>\nLZ_INLINE_VAR constexpr size_t dimensions_v = dimensions<Iterable>::value;\n\n#endif\n\nnamespace detail {\nstruct flatten_adaptor {\n    using adaptor = flatten_adaptor;\n\n    // clang-format off\n\n    /**\n     * @brief Flattens a nested iterable. For instance a vector of vectors will be flattened to a single iterable. Returns a\n     * bidirectional iterable if the input is bidirectional, otherwise the same as the input iterable. If its input iterable is\n     * forward or less, the end iterator will be a sentinel. Contains a .size() method if all the input iterables are sized. Example:\n     * ```cpp\n     * std::vector<std::vector<int>> vectors = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\n     * auto flattened = lz::flatten(vectors); // { 1, 2, 3, 4, 5, 6, 7 }\n     * // or\n     * auto flattened = vectors | lz::flatten; // { 1, 2, 3, 4, 5, 6, 7 }\n     * ```\n     * @param iterable The iterable(s) to flatten\n     * @return An iterable that is flattened, with the same type as the input iterable.\n     */\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14\n    flatten_iterable<remove_ref_t<Iterable>, dimensions<remove_ref_t<Iterable>>::value - !std::is_array<remove_ref_t<Iterable>>::value>\n    operator()(Iterable&& iterable) const {\n        using flattener = flatten_iterable<remove_ref_t<Iterable>, \n                                           dimensions<remove_ref_t<Iterable>>::value - !std::is_array<remove_ref_t<Iterable>>::value>;\n        using it = iter_t<Iterable>;\n\n        static_assert(std::is_default_constructible<it>::value, \"underlying iterator needs to be default constructible\");\n        static_assert(std::is_copy_assignable<it>::value || std::is_move_assignable<it>::value,\n                      \"underling iterator needs to be copy or move assignable\");\n\n        return flattener{ std::forward<Iterable>(iterable) };\n    }\n\n    // clang-format on\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/fn_args_holder.hpp",
    "content": "#pragma once\n\n#ifndef LZ_FN_ARGS_HOLDER_ADAPTOR_HPP\n#define LZ_FN_ARGS_HOLDER_ADAPTOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/index_sequence.hpp>\n#include <tuple>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Adaptor, class... Ts>\nclass fn_args_holder {\n    std::tuple<Ts...> _data;\n\npublic:\n    using adaptor = fn_args_holder<Adaptor, Ts...>;\n\n    template<class... Args>\n    LZ_CONSTEXPR_CXX_14 fn_args_holder(Args&&... args) : _data{ std::forward<Args>(args)... } {\n    }\n\nprivate:\n    template<class Iterable, size_t... I>\n    LZ_CONSTEXPR_CXX_14 auto\n    operator()(Iterable&& iterable,\n               index_sequence<I...>) const& -> decltype(std::declval<Adaptor>()(std::forward<Iterable>(iterable),\n                                                                                std::get<I>(_data)...)) {\n        return Adaptor{}(std::forward<Iterable>(iterable), std::get<I>(_data)...);\n    }\n\n    template<class Iterable, size_t... I>\n    LZ_CONSTEXPR_CXX_14 auto\n    operator()(Iterable&& iterable,\n               index_sequence<I...>) && -> decltype(std::declval<Adaptor>()(std::forward<Iterable>(iterable),\n                                                                            std::get<I>(std::move(_data))...)) {\n        return Adaptor{}(std::forward<Iterable>(iterable), std::get<I>(std::move(_data))...);\n    }\n\npublic:\n    template<class Iterable>\n    LZ_CONSTEXPR_CXX_14 auto operator()(Iterable&& iterable) const& -> decltype((*this)(std::forward<Iterable>(iterable),\n                                                                                        make_index_sequence<sizeof...(Ts)>{})) {\n        return (*this)(std::forward<Iterable>(iterable), make_index_sequence<sizeof...(Ts)>{});\n    }\n\n    template<class Iterable>\n    LZ_CONSTEXPR_CXX_14 auto\n    operator()(Iterable&& iterable) && -> decltype(std::move(*this)(std::forward<Iterable>(iterable),\n                                                                    make_index_sequence<sizeof...(Ts)>{})) {\n        return std::move(*this)(std::forward<Iterable>(iterable), make_index_sequence<sizeof...(Ts)>{});\n    }\n};\n}\n}\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/generate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GENERATE_ADAPTOR_HPP\n#define LZ_GENERATE_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/generate.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct generate_adaptor {\n    using adaptor = generate_adaptor;\n\n    /**\n     * @brief Generates n amount of elements using a generator function. Is a forward iterable, contains a .size() function and\n     * returns a sentinel. Example:\n     * ```cpp\n     * lz::generate([]() { return 10; }, 5); // Generates 5 times the number 10\n     * ```\n     * @param generator_func The generator function that generates the elements\n     * @param amount The amount of elements to generate\n     * @return A generate_iterable that generates the elements @p amount times\n     */\n    template<class GeneratorFunc>\n    LZ_NODISCARD constexpr generate_iterable<GeneratorFunc, false>\n    operator()(GeneratorFunc generator_func, const ptrdiff_t amount) const {\n        return { std::move(generator_func), amount };\n    }\n\n    /**\n     * @brief Generates infinite amount of elements using a generator function. Is a forward iterable, contains a .size() function\n     * and returns a sentinel. Example:\n     * ```cpp\n     * lz::generate([]() { return 10; }); // Generates infinite times the number 10\n     * ```\n     * @param generator_func The generator function that generates the elements\n     * @return A generate_iterable that infinitely generates the elements\n     */\n    template<class GeneratorFunc>\n    LZ_NODISCARD constexpr generate_iterable<GeneratorFunc, true> operator()(GeneratorFunc generator_func) const {\n        return generate_iterable<GeneratorFunc, true>{ std::move(generator_func) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/generate_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GENERATE_WHILE_ADAPTOR_HPP\n#define LZ_GENERATE_WHILE_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/generate_while.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct generate_while_adaptor {\n    using adaptor = generate_while_adaptor;\n\n    /**\n     * @brief Generates elements while the predicate returns true. The predicate must return an object that is compatible with\n     * std::get. The first element (std::get<0>) must be an object convertible to bool, the second element (std::get<1>) can be\n     * any type. This iterable does not contain a .size() member function. Its end() function returns a sentinel, rather than an\n     * actual iterator object. It returns a (std::)input_iterator(_tag). Example:\n     * ```cpp\n     * int i = 0;\n     * auto generator = lz::generate_while([&i]() {\n     *    auto copy = i++;\n     *    return std::make_pair(copy, copy != 4);\n     * }); // { 0, 1, 2, 3 }\n     * // or (cxx 14)\n     * auto generator = lz::generate_while([i = 0]() {\n     *   auto pair = std::make_pair(i, i != 4);\n     *    ++i;\n     *   return pair;\n     * }); // { 0, 1, 2, 3 }\n     * ```\n     * @param generator_func The generator function that returns a std::pair compatible object, where pair::first_type is\n     * convertible to bool.\n     * @return An iterable that generates elements while the predicate returns true.\n     */\n    template<class GeneratorFunc>\n    LZ_NODISCARD constexpr generate_while_iterable<GeneratorFunc> operator()(GeneratorFunc generator_func) const {\n        using pair = decltype(generator_func());\n        using pair_first = decltype(std::get<1>(std::declval<pair>()));\n        static_assert(std::is_convertible<remove_cvref_t<pair_first>, bool>::value,\n                      \"Function must return a std::pair compatible object (i.e. object::first, object::second), where \"\n                      \"object::first\"\n                      \"returns a bool like object.\");\n        return { std::move(generator_func) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/group_by.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GROUP_BY_ADAPTORS_HPP\n#define LZ_GROUP_BY_ADAPTORS_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/group_by.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct group_by_adaptor {\n    using adaptor = group_by_adaptor;\n\n    /**\n     * @brief Creates chunks of elements of which the predicate returns true. Input iterable must be sorted first before using\n     * this function. Its end iterator is a sentinel one if ithe input iterable is forward or has a sentinel. The iterable does\n     * not contain a .size() method. Its iterator category is bidirectional if possible. Example:\n     * ```cpp\n     * char str[] = \"aaabbccccd\";\n     * // normally, use std::sort(str.begin(), str.end()) before using this function, if it isn't sorted already\n     * auto grouper = lz::group_by(cstr, [](char a, char b) { return a == b; });\n     * // grouper = {{'a', 'a', 'a'}, {'b', 'b'}, {'c', 'c', 'c', 'c'}, {'d'}}\n     * // or\n     * auto grouper = cstr | lz::group_by([](char a, char b) { return a == b; });\n     * // grouper = {{'a', 'a', 'a'}, {'b', 'b'}, {'c', 'c', 'c', 'c'}, {'d'}}\n     * ```\n     * @param iterable The iterable to group by\n     * @param binary_predicate The predicate to group by\n     * @return An iterable that groups elements by the given predicate\n     */\n    template<class Iterable, class BinaryPredicate>\n    LZ_NODISCARD constexpr group_by_iterable<remove_ref_t<Iterable>, BinaryPredicate>\n    operator()(Iterable&& iterable, BinaryPredicate binary_predicate) const {\n        return { std::forward<Iterable>(iterable), std::move(binary_predicate) };\n    }\n\n    /**\n     * @brief Creates chunks of elements of which the predicate returns true. Input iterable must be sorted first before using\n     * this function. Its end iterator is a sentinel one if ithe input iterable is forward or has a sentinel. The iterable does\n     * not contain a .size() method. Its iterator category is bidirectional if possible. Example:\n     * ```cpp\n     * char str[] = \"aaabbccccd\";\n     * // normally, use std::sort(str.begin(), str.end()) before using this function, if it isn't sorted already\n     * auto grouper = cstr | lz::group_by([](char a, char b) { return a == b; });\n     * // grouper = {{'a', 'a', 'a'}, {'b', 'b'}, {'c', 'c', 'c', 'c'}, {'d'}}\n     * ```\n     * @param binary_predicate The predicate to group by\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class BinaryPredicate>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, BinaryPredicate>\n    operator()(BinaryPredicate binary_predicate) const {\n        return { std::move(binary_predicate) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_GROUP_BY_ADAPTORS_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/inclusive_scan.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INCLUSIVE_SCAN_ADAPTOR_HPP\n#define LZ_INCLUSIVE_SCAN_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/inclusive_scan.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/is_invocable.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct inclusive_scan_adaptor {\n    using adaptor = inclusive_scan_adaptor;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief Performs an inclusive scan on a container. The first element will be the result of the binary operation with init\n     * value and the first element of the input iterable. The second element will be the previous result + the next value of the\n     * input iterable, the third element will be previous result + the next value of the input iterable, etc. It contains a\n     * .size() method if the input iterable also has a .size() method. Its end() function returns a sentinel rather than an\n     * iterator and its iterator category is forward. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * // std::plus is used as the default binary operation\n     * auto scan = lz::inclusive_scan(vec, 0); // scan = { 1, 3, 6, 10, 15 }\n     * // so 0 + 1 (= 1), 1 + 2 (= 3), 3 + 3 (= 6), 6 + 4 (= 10), 10 + 5 (= 15)\n     *\n     * // or\n     * auto scan = vec | lz::inclusive_scan(0); // scan = { 1, 3, 6, 10, 15 }\n     * // you can also add a custom operator:\n     * auto scan = vec | lz::inclusive_scan(0, std::plus<int>{}); // scan = { 1, 3, 6, 10, 15 }\n     * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions,\n     * you\n     * // can omit the init value, in which case it will be a default constructed object of the type of the container.\n     * // Example\n     * auto scan = lz::inclusive_scan(vec);\n     * auto scan = lz::inclusive_scan(vec, 0);\n     * // auto scan = vec | lz::inclusive_scan; // uses 0 and std::plus\n     * auto scan = vec | lz::inclusive_scan(0);\n     * ```\n     * @param iterable The iterable to perform the inclusive scan on.\n     * @param init The initial value to start the inclusive scan with.\n     * @param binary_op The binary operation to perform on the elements. The default is std::plus.\n     * @return An iterable that performs an inclusive scan on the input iterable.\n     */\n    template<class Iterable, class T = val_iterable_t<Iterable>, class BinaryOp = LZ_BIN_OP(plus, val_iterable_t<Iterable>)>\n    [[nodiscard]] constexpr inclusive_scan_iterable<remove_ref_t<Iterable>, T, BinaryOp>\n    operator()(Iterable&& iterable, T init = {}, BinaryOp binary_op = {}) const\n        requires(std::invocable<BinaryOp, T, T>)\n    {\n        return { std::forward<Iterable>(iterable), std::move(init), std::move(binary_op) };\n    }\n\n    /**\n     * @brief Performs an inclusive scan on a container. The first element will be the result of the binary operation with init\n     * value and the first element of the input iterable. The second element will be the previous result + the next value of the\n     * input iterable, the third element will be previous result + the next value of the input iterable, etc. It contains a\n     * .size() method if the input iterable also has a .size() method. Its end() function returns a sentinel rather than an\n     * iterator and its iterator category is forward. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * // std::plus is used as the default binary operation\n     * auto scan = lz::inclusive_scan(vec, 0); // scan = { 1, 3, 6, 10, 15 }\n     * // so 0 + 1 (= 1), 1 + 2 (= 3), 3 + 3 (= 6), 6 + 4 (= 10), 10 + 5 (= 15)\n     *\n     * // you can also add a custom operator:\n     * auto scan = vec | lz::inclusive_scan(0, std::plus<int>{}); // scan = { 1, 3, 6, 10, 15 }\n     * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions,\n     * you\n     * // can omit the init value, in which case it will be a default constructed object of the type of the container.\n     * // Example\n     * auto scan = lz::inclusive_scan(vec);\n     * auto scan = lz::inclusive_scan(vec, 0);\n     * // auto scan = vec | lz::inclusive_scan; // uses 0 and std::plus\n     * auto scan = vec | lz::inclusive_scan(0);\n     * ```\n     * @param init The initial value to start the inclusive scan with.\n     * @param binary_op The binary operation to perform on the elements. The default is std::plus.\n     * @return An adaptor that can be used in pipe expressions.\n     */\n    template<class T, class BinaryOp = LZ_BIN_OP(plus, T)>\n    [[nodiscard]] constexpr fn_args_holder<adaptor, remove_cvref_t<T>, BinaryOp>\n    operator()(T&& init, BinaryOp binary_op = {}) const\n        requires(std::invocable<BinaryOp, remove_cvref_t<T>, remove_cvref_t<T>>)\n    {\n        return { std::forward<T>(init), std::move(binary_op) };\n    }\n\n#else\n\n    // clang-format off\n\n    /**\n     * @brief Performs an inclusive scan on a container. The first element will be the result of the binary operation with init value\n     * and the first element of the input iterable. The second element will be the previous result + the next value of the input\n     * iterable, the third element will be previous result + the next value of the input iterable, etc. It contains a .size() method\n     * if the input iterable also has a .size() method. Its end() function returns a sentinel rather than an iterator and its iterator\n     * category is forward. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * // std::plus is used as the default binary operation\n     * auto scan = lz::inclusive_scan(vec, 0); // scan = { 1, 3, 6, 10, 15 }\n     * // so 0 + 1 (= 1), 1 + 2 (= 3), 3 + 3 (= 6), 6 + 4 (= 10), 10 + 5 (= 15)\n     *\n     * // or\n     * auto scan = vec | lz::inclusive_scan(0); // scan = { 1, 3, 6, 10, 15 }\n     * // you can also add a custom operator:\n     * auto scan = vec | lz::inclusive_scan(0, std::plus<int>{}); // scan = { 1, 3, 6, 10, 15 }\n     * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions, you\n     * // can omit the init value, in which case it will be a default constructed object of the type of the container.\n     * // Example\n     * auto scan = lz::inclusive_scan(vec);\n     * auto scan = lz::inclusive_scan(vec, 0);\n     * // auto scan = vec | lz::inclusive_scan; // uses 0 and std::plus\n     * auto scan = vec | lz::inclusive_scan(0);\n     * ```\n     * @param iterable The iterable to perform the inclusive scan on.\n     * @param init The initial value to start the inclusive scan with.\n     * @param binary_op The binary operation to perform on the elements. The default is std::plus.\n     * @return An iterable that performs an inclusive scan on the input iterable.\n     */\n    template<class Iterable, class T = val_iterable_t<Iterable>, class BinaryOp = LZ_BIN_OP(plus, val_iterable_t<Iterable>)>\n    LZ_NODISCARD constexpr \n    enable_if_t<is_invocable<BinaryOp, T, T>::value, inclusive_scan_iterable<remove_ref_t<Iterable>, T, BinaryOp>>\n    operator()(Iterable&& iterable, T init = {}, BinaryOp binary_op = {}) const {\n        return { std::forward<Iterable>(iterable), std::move(init), std::move(binary_op) };\n    }\n\n    /**\n     * @brief Performs an inclusive scan on a container. The first element will be the result of the binary operation with init value\n     * and the first element of the input iterable. The second element will be the previous result + the next value of the input\n     * iterable, the third element will be previous result + the next value of the input iterable, etc. It contains a .size() method\n     * if the input iterable also has a .size() method. Its end() function returns a sentinel rather than an iterator and its iterator\n     * category is forward. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * // std::plus is used as the default binary operation\n     * auto scan = lz::inclusive_scan(vec, 0); // scan = { 1, 3, 6, 10, 15 }\n     * // so 0 + 1 (= 1), 1 + 2 (= 3), 3 + 3 (= 6), 6 + 4 (= 10), 10 + 5 (= 15)\n     *\n     * // you can also add a custom operator:\n     * auto scan = vec | lz::inclusive_scan(0, std::plus<int>{}); // scan = { 1, 3, 6, 10, 15 }\n     * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions, you\n     * // can omit the init value, in which case it will be a default constructed object of the type of the container.\n     * // Example\n     * auto scan = lz::inclusive_scan(vec);\n     * auto scan = lz::inclusive_scan(vec, 0);\n     * // auto scan = vec | lz::inclusive_scan; // uses 0 and std::plus\n     * auto scan = vec | lz::inclusive_scan(0);\n     * ```\n     * @param init The initial value to start the inclusive scan with.\n     * @param binary_op The binary operation to perform on the elements. The default is std::plus.\n     * @return An adaptor that can be used in pipe expressions.\n     */\n    template<class T, class BinaryOp = LZ_BIN_OP(plus, T)>\n    LZ_NODISCARD constexpr \n    enable_if_t<is_invocable<BinaryOp, remove_cvref_t<T>, remove_cvref_t<T>>::value, fn_args_holder<adaptor, remove_cvref_t<T>, BinaryOp>>\n    operator()(T&& init, BinaryOp binary_op = {}) const {\n        return { std::forward<T>(init), std::move(binary_op) };\n    }\n\n    // clang-format on\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#ifdef LZ_HAS_CONCEPTS\n\nLZ_MODULE_EXPORT template<class Iterable>\n    requires(lz::iterable<Iterable>)\n[[nodiscard]] constexpr auto operator|(Iterable&& iterable, lz::detail::inclusive_scan_adaptor) {\n    return lz::detail::inclusive_scan_adaptor{}(std::forward<Iterable>(iterable), lz::detail::val_iterable_t<Iterable>{},\n                                                LZ_BIN_OP(plus, val_iterable_t<Iterable>){});\n}\n\n#else\n\nLZ_MODULE_EXPORT template<class Iterable>\nLZ_NODISCARD constexpr auto operator|(Iterable&& iterable, lz::detail::inclusive_scan_adaptor)\n    -> lz::detail::enable_if_t<lz::detail::is_iterable<Iterable>::value,\n                               decltype(lz::detail::inclusive_scan_adaptor{}(std::forward<Iterable>(iterable),\n                                                                             lz::detail::val_iterable_t<Iterable>{},\n                                                                             LZ_BIN_OP(plus, lz::detail::val_iterable_t<Iterable>){}))> {\n    return lz::detail::inclusive_scan_adaptor{}(std::forward<Iterable>(iterable), lz::detail::val_iterable_t<Iterable>{},\n                                                LZ_BIN_OP(plus, lz::detail::val_iterable_t<Iterable>){});\n}\n\n#endif\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/interleave.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INTERLEAVED_ADAPTOR_HPP\n#define LZ_INTERLEAVED_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/interleave.hpp>\n\nnamespace lz {\nnamespace detail {\n\nstruct interleave_adaptor {\n    using adaptor = interleave_adaptor;\n\n    /**\n     * @brief Interleave the elements of the given iterables. This means it returns the element of the first iterable, then the\n     * second, etc... and resets when the last iterable is reached. The iterator will stop at the end of the shortest iterable.\n     * The iterator category is the same as its \"weakest\" input iterable. The reference type returned by operator* will:\n     * - be by value if one of the iterables yields by value\n     * - be by const reference if one of the iterables yield by const reference.\n     * - be by mutable reference if all iterables yield by mutable reference.\n     * Contains a .size() function if all iterables have a .size() function. The size is the sum of all the sizes of the\n     * iterables. Contains a sentinel if one of the iterables contains a sentinel or if one of them is forward. Its iterator\n     * category is the 'weakest' of the input iterables. It returns a sentinel if the input iterable has a forward iterator. If\n     * its input iterables all have a .size() method, then this iterable will also have a .size() method.\n     *\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::vector<int> vec1 = { 1, 2, 3 }, vec2 = { 4, 5, 6, 7 }, vec3 = { 8, 9, 10, 11, 12 };\n     * auto interleaved = lz::interleave(vec1, vec2, vec3); // interleaved = { 1, 4, 8, 2, 5, 9, 3, 6, 10 }\n     * // or\n     * auto interleaved = vec1 | lz::interleave(vec2, vec3); // interleaved = { 1, 4, 8, 2, 5, 9, 3, 6, 10 }\n     * auto c = lz::interleave(vec1, lz::range(5, 10)); // yields by value, not by reference\n     * ```\n     * @param iterable The first iterable to interleave.\n     * @param iterables The rest of the iterables to interleave.\n     * @return An iterable that interleaves the elements of the given iterables.\n     */\n    template<class Iterable, class... Iterables>\n    LZ_NODISCARD constexpr interleave_iterable<remove_ref_t<Iterable>, remove_ref_t<Iterables>...>\n    operator()(Iterable&& iterable, Iterables&&... iterables) const {\n        return { std::forward<Iterable>(iterable), std::forward<Iterables>(iterables)... };\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_INTERLEAVED_ADAPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/intersection.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INTERSECTION_ADAPTOR_HPP\n#define LZ_INTERSECTION_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/intersection.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct intersection_adaptor {\n    using adaptor = intersection_adaptor;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief Intersects the first iterable with the second iterable. The result is a new iterable containing the elements that\n     * are in both iterables. Returns a bidirectional iterable if the input iterable is at least bidirectional, otherwise forward.\n     * Returns a sentinel if it is a forward iterable or a sentinel. Does not contain a .size() method. Example:\n     * ```cpp\n     * std::string a = \"aaaabbcccddee\";\n     * std::string b = \"aabccce\";\n     *\n     * std::sort(a.begin(), a.end());\n     * std::sort(b.begin(), b.end());\n     *\n     * auto intersect = lz::intersection(a, b); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n     * // or\n     * auto intersect = lz::intersection(a, b, std::less<>{}); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n     * ```\n     * @param iterable The first iterable\n     * @param iterable2 The second iterable\n     * @param compare The comparison function. std::less<> by default\n     * @return An iterable that contains the intersection of the two iterables\n     */\n    template<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(less, val_iterable_t<Iterable>)>\n    [[nodiscard]] constexpr intersection_iterable<remove_ref_t<Iterable>, remove_ref_t<Iterable2>, BinaryPredicate>\n    operator()(Iterable&& iterable, Iterable2&& iterable2, BinaryPredicate compare = {}) const\n        requires(lz::iterable<Iterable2>)\n    {\n        return { std::forward<Iterable>(iterable), std::forward<Iterable2>(iterable2), std::move(compare) };\n    }\n\n    /**\n     * @brief Intersects the first iterable with the second iterable. The result is a new iterable containing the elements that\n     * are in both iterables.Returns a bidirectional iterable if the input iterable is at least bidirectional, otherwise forward.\n     * Returns a sentinel if it is a forward iterable or a sentinel. Does not contain a .size() method. Example:\n     * ```cpp\n     * std::string a = \"aaaabbcccddee\";\n     * std::string b = \"aabccce\";\n     *\n     * std::sort(a.begin(), a.end());\n     * std::sort(b.begin(), b.end());\n     *\n     * auto intersect = a | lz::intersection(b); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n     * // or\n     * auto intersect = a | lz::intersection(b, std::less<>{}); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n     * ```\n     * @param iterable2 The second iterable\n     * @param compare The comparison function. std::less<> by default\n     */\n    template<class Iterable2, class BinaryPredicate = LZ_BIN_OP(less, val_iterable_t<Iterable2>)>\n    [[nodiscard]] constexpr fn_args_holder<adaptor, Iterable2, BinaryPredicate>\n    operator()(Iterable2&& iterable2, BinaryPredicate compare = {}) const\n        requires(!iterable<BinaryPredicate>)\n    {\n        return { std::forward<Iterable2>(iterable2), std::move(compare) };\n    }\n\n#else\n\n    // clang-format off\n\n    /**\n     * @brief Intersects the first iterable with the second iterable. The result is a new iterable containing the elements that\n     * are in both iterables. Returns a bidirectional iterable if the input iterable is at least bidirectional, otherwise forward.\n     * Returns a sentinel if it is a forward iterable or a sentinel. Does not contain a .size() method. Example:\n     * ```cpp\n     * std::string a = \"aaaabbcccddee\";\n     * std::string b = \"aabccce\";\n     *\n     * std::sort(a.begin(), a.end());\n     * std::sort(b.begin(), b.end());\n     *\n     * auto intersect = lz::intersection(a, b); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n     * // or\n     * auto intersect = lz::intersection(a, b, std::less<>{}); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n     * ```\n     * @param iterable The first iterable\n     * @param iterable2 The second iterable\n     * @param compare The comparison function. std::less<> by default\n     * @return An iterable that contains the intersection of the two iterables\n     */\n    template<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(less, val_iterable_t<Iterable2>)>\n    LZ_NODISCARD constexpr\n    enable_if_t<is_iterable<Iterable2>::value, intersection_iterable<remove_ref_t<Iterable>, remove_ref_t<Iterable2>, BinaryPredicate>>\n    operator()(Iterable&& iterable, Iterable2&& iterable2, BinaryPredicate compare = {}) const {\n        return { std::forward<Iterable>(iterable), std::forward<Iterable2>(iterable2), std::move(compare) };\n    }\n\n    // clang-format on\n\n    /**\n     * @brief Intersects the first iterable with the second iterable. The result is a new iterable containing the elements that\n     * are in both iterables.Returns a bidirectional iterable if the input iterable is at least bidirectional, otherwise forward.\n     * Returns a sentinel if it is a forward iterable or a sentinel. Does not contain a .size() method. Example:\n     * ```cpp\n     * std::string a = \"aaaabbcccddee\";\n     * std::string b = \"aabccce\";\n     *\n     * std::sort(a.begin(), a.end());\n     * std::sort(b.begin(), b.end());\n     *\n     * auto intersect = a | lz::intersection(b); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n     * // or\n     * auto intersect = a | lz::intersection(b, std::less<>{}); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n     * ```\n     * @param iterable2 The second iterable\n     * @param compare The comparison function. std::less<> by default\n     */\n    template<class Iterable2, class BinaryPredicate = LZ_BIN_OP(less, val_iterable_t<Iterable2>)>\n    LZ_NODISCARD constexpr enable_if_t<!is_iterable<BinaryPredicate>::value, fn_args_holder<adaptor, Iterable2, BinaryPredicate>>\n    operator()(Iterable2&& iterable2, BinaryPredicate compare = {}) const {\n        return { std::forward<Iterable2>(iterable2), std::move(compare) };\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/iter_tools.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ITER_TOOLS_ADAPTORS_HPP\n#define LZ_ITER_TOOLS_ADAPTORS_HPP\n\n#include <Lz/as_iterator.hpp>\n#include <Lz/concatenate.hpp>\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/procs/tuple_expand.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n#include <Lz/drop.hpp>\n#include <Lz/drop_while.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/map.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/split.hpp>\n#include <Lz/util/string_view.hpp>\n#include <Lz/zip.hpp>\n#include <cctype>\n\nnamespace lz {\nnamespace detail {\ntemplate<class To>\nstruct convert_fn {\n    template<class From>\n    LZ_NODISCARD constexpr To operator()(From&& f) const noexcept(noexcept(static_cast<To>(f))) {\n        return static_cast<To>(f);\n    }\n};\n\ntemplate<size_t N>\nstruct get_fn {\n    template<class T>\n    LZ_NODISCARD constexpr auto operator()(T&& gettable) const -> decltype(std::get<N>(std::forward<T>(gettable))) {\n        return std::get<N>(std::forward<T>(gettable));\n    }\n};\n\nstruct trim_fn {\n    template<class CharT>\n    LZ_NODISCARD constexpr bool operator()(const CharT c) const noexcept {\n        return static_cast<bool>(std::isspace(static_cast<unsigned char>(c)));\n    }\n};\n\nstruct deref_fn {\n    template<class T>\n    LZ_NODISCARD constexpr auto operator()(T&& t) const -> decltype(*std::forward<T>(t)) {\n        return *std::forward<T>(t);\n    }\n};\n\ntemplate<class Iterable, class Predicate>\nusing unzip_with_iterable = map_iterable<Iterable, tuple_expand<Predicate>>;\n\nstruct unzip_with_adaptor {\n    using adaptor = unzip_with_adaptor;\n\n    /**\n     * @brief Unzips an iterable of tuple-like elements using the provided predicate function. The predicate function should take\n     * the same number of arguments as the number of elements in the tuples and return a value. Example:\n     * ```cpp\n     * std::vector<std::tuple<int, int>> zipped = { std::make_tuple(1, 6), std::make_tuple(2, 7), std::make_tuple(3, 8) };\n     * auto unzipped = lz::unzip_with(zipped, [](int a, int b) { return a + b; }); // {7, 9, 11}\n     * ```\n     *\n     * @param iterable The iterable to unzip.\n     * @param predicate The function that will be applied to each tuple element.\n     * @return An iterable that contains the results of applying the predicate to each tuple element.\n     */\n    template<class Iterable, class Predicate>\n    LZ_NODISCARD constexpr unzip_with_iterable<remove_ref_t<Iterable>, Predicate>\n    operator()(Iterable&& iterable, Predicate predicate) const {\n        return lz::map(std::forward<Iterable>(iterable), make_expand_fn(std::move(predicate)));\n    }\n\n    /**\n     * @brief Unzips an iterable of tuple-like elements using the provided\n     * predicate function. The predicate function should take the same number of\n     * arguments as the number of elements in the tuples and return a value.\n     * Example:\n     * ```cpp\n     * std::vector<std::tuple<int, int>> zipped = { std::make_tuple(1, 6),\n     * std::make_tuple(2, 7), std::make_tuple(3, 8) }; auto unzipped = zipped |\n     * lz::unzip_with([](int a, int b) { return a + b; }); // {7, 9, 11}\n     * ```\n     *\n     * @param predicate The function that will be applied to each tuple element.\n     * @return An iterable that contains the results of applying the predicate\n     * to each tuple element.\n     */\n    template<class Predicate>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, Predicate> operator()(Predicate predicate) const {\n        return { std::move(predicate) };\n    }\n};\n\ntemplate<class CharT, class Iterable>\nusing lines_iterable = split_iterable<basic_string_view<CharT>, Iterable, CharT>;\n\nstruct lines_adaptor {\n    using adaptor = lines_adaptor;\n\n    /**\n     * @brief Returns a split_iterable, that splits the string on `'\\n'` using `lz::sv_split`. Returns string_views to its\n     * substrings. Example:\n     * ```cpp\n     * std::string str = \"Hello\\nWorld\\n!\";\n     * auto splitted = lz::lines(str); // {\"Hello\", \"World\", \"!\"}\n     * // or\n     * auto splitted = str | lz::lines; // {\"Hello\", \"World\", \"!\"}\n     * // or, little bit slower since lz::c_string is used here, which is not random access\n     * auto splitted = lz::lines(lz::c_string(\"Hello\\nWorld\\n!\")); // {\"Hello\", \"World\", \"!\"}\n     * // or with string_view\n     * lz::string_view str = \"Hello\\nWorld\\n!\";\n     * auto splitted = lz::lines(str); // {\"Hello\", \"World\", \"!\"}\n     * ```\n     * @param string The string to split on \"\\n\".\n     * @return A lines_iterable that can be iterated over, containing the substrings.\n     */\n    template<class String>\n    LZ_NODISCARD constexpr lines_iterable<val_iterable_t<String>, remove_ref_t<String>> operator()(String&& string) const {\n        using char_type = val_iterable_t<String>;\n        return lz::sv_split(std::forward<String>(string), static_cast<char_type>('\\n'));\n    }\n\n    /**\n     * @brief Returns a split_iterable, that splits the string on `'\\n'` using `lz::sv_split`. Returns string_views to its\n     * substrings. Example:\n     * ```cpp\n     * lz::basic_string str = \"Hello\\nWorld\\n!\";\n     * auto splitted = lz::lines(str); // {\"Hello\", \"World\", \"!\"}. `str` will not be by reference, because string_view is easy to\n     * // copy\n     * ```\n     * @param string The string to split on '\\n'.\n     * @return A lines_iterable that can be iterated over, containing the substrings.\n     */\n    template<class CharT>\n    LZ_NODISCARD constexpr lines_iterable<CharT, copied_basic_sv<CharT>> operator()(const basic_string_view<CharT> string) const {\n        return lz::sv_split(copied_basic_sv<CharT>(string), static_cast<CharT>('\\n'));\n    }\n};\n\ntemplate<class Iterable, class T>\nusing as_iterable = map_iterable<Iterable, convert_fn<T>>;\n\n/**\n * @tparam T The type to convert the elements to.\n */\ntemplate<class T>\nstruct as_adaptor {\n    using adaptor = as_adaptor;\n\n    /**\n     * @brief Returns an iterable that converts the elements in the given\n     * container to the type @p `T` using `lz::map`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto floats = lz::as<float>(vec); // { 1.f, 2.f, 3.f, 4.f, 5.f }\n     * // or\n     * auto floats = vec | lz::as<float>; // { 1.f, 2.f, 3.f, 4.f, 5.f }\n     * auto floats = vec | lz::as<float>{}; // { 1.f, 2.f, 3.f, 4.f, 5.f } (for\n     * cxx11)\n     * ```\n     * @param iterable The iterable to convert.\n     * @return An as_iterable that can be iterated over, containing the\n     * converted elements.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr as_iterable<remove_ref_t<Iterable>, T> operator()(Iterable&& iterable) const {\n        return lz::map(std::forward<Iterable>(iterable), convert_fn<T>{});\n    }\n};\n\ntemplate<class Iterable, size_t N>\nusing get_nth_iterable = map_iterable<Iterable, get_fn<N>>;\n\ntemplate<size_t N>\nstruct get_n_adaptor {\n    using adaptor = get_n_adaptor<N>;\n\n    /**\n     * @brief Gets the nth element from a std::get-able container, using `std::get<N>` and `lz::map`. Example:\n     * ```cpp\n     * std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n     * auto actual = lz::get_nth<2>(three_tuple_vec); // {3, 6, 9}\n     * // or\n     * auto actual = three_tuple_vec | lz::get_nth<2>; // {3, 6, 9} (for cxx11 use lz::get_nth<2>{} instead of lz::get_nth<2>)\n     * ```\n     *\n     * @param iterable The iterable to get the nth element from.\n     * @return A map iterable that can be iterated over, containing the nth elements of the tuples in the iterable.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr get_nth_iterable<remove_ref_t<Iterable>, N> operator()(Iterable&& iterable) const {\n        return lz::map(std::forward<Iterable>(iterable), get_fn<N>{});\n    }\n};\n\ntemplate<class Iterable, size_t... N>\nusing get_nths_iterable = zip_iterable<get_nth_iterable<Iterable, N>...>;\n\ntemplate<size_t... N>\nstruct get_nths_adaptor {\n    using adaptor = get_nths_adaptor<N...>;\n\n    /**\n     * @brief Gets nths indexes from a std::get-able container, using `std::get<N>` and `lz::zip`. Example:\n     * ```cpp\n     * std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n     * auto actual = lz::get_nths<0, 2>(three_tuple_vec); // { {1, 3}, {4, 6}, {7, 9} }\n     * // or\n     * auto actual = three_tuple_vec | lz::get_nths<0, 2>; // { {1, 3}, {4, 6}, {7, 9} }\n     * // (for cxx11 use lz::get_nths<0, 2>{}() instead of lz::get_nths<0, 2>())\n     * ```\n     * @param iterable The iterable to get the nth elements from.\n     * @return A zip iterable that can be iterated over, containing the nth elements of the tuples in the iterable.\n     */\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 get_nths_iterable<remove_ref_t<Iterable>, N...> operator()(Iterable&& iterable) const {\n        return lz::zip(get_n_adaptor<N>{}(std::forward<Iterable>(iterable))...);\n    }\n};\n\ntemplate<class Iterable, class UnaryFilterPredicate, class UnaryMapOp>\nusing filter_map_iterable = map_iterable<filter_iterable<Iterable, UnaryFilterPredicate>, UnaryMapOp>;\n\nstruct filter_map_adaptor {\n    using adaptor = filter_map_adaptor;\n\n    /**\n     * @brief Filters an iterable using `predicate` and then maps those elements using `fn`, using `lz::filter` and `lz::map`.\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto fm = lz::filter_map(vec, [](int i) { return i % 2 == 0; }, [](int i) { return i * 2; }); // {4, 8}\n     * ```\n     * @param iterable The iterable to filter and map.\n     * @param predicate The predicate to filter the elements with.\n     * @param unary_op The function to map the filtered elements with.\n     * @return A filter_map_iterable that can be iterated over, containing the mapped elements.\n     */\n    template<class Iterable, class UnaryFilterPredicate, class UnaryMapOp>\n    LZ_NODISCARD constexpr filter_map_iterable<remove_ref_t<Iterable>, UnaryFilterPredicate, UnaryMapOp>\n    operator()(Iterable&& iterable, UnaryFilterPredicate predicate, UnaryMapOp unary_op) const {\n        return lz::map(lz::filter(std::forward<Iterable>(iterable), std::move(predicate)), std::move(unary_op));\n    }\n\n    /**\n     * @brief Filters an iterable using `predicate` and then maps those elements\n     * using `fn`, using `lz::filter` and `lz::map`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto fm = vec | lz::filter_map([](int i) { return i % 2 == 0; }, [](int\n     * i) { return i * 2; }); // {4, 8}\n     * ```\n     * @param predicate The predicate to filter the elements with.\n     * @param map_op The function to map the filtered elements with.\n     * @return A filter_map_iterable that can be iterated over, containing the\n     * mapped elements.\n     */\n    template<class UnaryFilterPredicate, class UnaryMapOp>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, UnaryFilterPredicate, UnaryMapOp>\n    operator()(UnaryFilterPredicate predicate, UnaryMapOp map_op) const {\n        return { std::move(predicate), std::move(map_op) };\n    }\n};\n\ntemplate<class Iterable, class SelectorIterable>\nusing select_iterable = filter_map_iterable<zip_iterable<Iterable, SelectorIterable>, get_fn<1>, get_fn<0>>;\n\nstruct select_adaptor {\n    using adaptor = select_adaptor;\n\n    /**\n     * @brief Selects elements from the first iterable if the corresponding element in the second iterable is `true`, using\n     * `lz::filter`, `lz::zip` and `lz::map`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * std::vector<bool> bools = { true, false, true, false, true };\n     * auto selected = lz::select(vec, bools); // {1, 3, 5}\n     * ```\n     * @param iterable The iterable to select elements from.\n     * @param selectors The iterable of selectors (booleans).\n     * @return A select_iterable that can be iterated over, containing the selected elements.\n     */\n    template<class Iterable, class SelectorIterable>\n    LZ_NODISCARD constexpr select_iterable<remove_ref_t<Iterable>, remove_ref_t<SelectorIterable>>\n    operator()(Iterable&& iterable, SelectorIterable&& selectors) const {\n        return filter_map_adaptor{}(lz::zip(std::forward<Iterable>(iterable), std::forward<SelectorIterable>(selectors)),\n                                    get_fn<1>{}, get_fn<0>{});\n    }\n\n    /**\n     * @brief Selects elements from the first iterable if the corresponding element in the second iterable is `true`, using\n     * `lz::filter`, `lz::zip` and `lz::map`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * std::vector<bool> bools = { true, false, true, false, true };\n     * auto selected = vec | lz::select(bools); // {1, 3, 5}\n     * ```\n     * @param selectors The iterable of selectors (booleans).\n     * @return A select_iterable that can be iterated over, containing the selected elements.\n     */\n    template<class SelectorIterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, SelectorIterable> operator()(SelectorIterable&& selectors) const {\n        return { std::forward<SelectorIterable>(selectors) };\n    }\n};\n\ntemplate<class Iterable>\nusing drop_back_iterable = lz::reverse_iterable<drop_while_iterable<lz::reverse_iterable<Iterable>>>;\n\nstruct drop_back_while_adaptor {\n    using adaptor = drop_back_while_adaptor;\n\n    /**\n     * @brief Drops elements from the back of the iterable as long as `predicate` returns `true`, then reverses the iterable\n     * again. Essentially turning the predicate into a not function, using `lz::reverse`, `lz::drop_while` followed by another\n     * `lz::reverse`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto dropped = lz::drop_back_while(vec, [](int i) { return i > 3; }); // {1, 2, 3}\n     * ```\n     * @param iterable The iterable to drop elements from the back.\n     * @param predicate The predicate to determine which elements to drop from the back.\n     * @return A drop_back_iterable that can be iterated over, containing the elements\n     */\n    template<class Iterable, class UnaryPredicate>\n    LZ_NODISCARD constexpr drop_back_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, UnaryPredicate predicate) const {\n        return lz::reverse(lz::drop_while(lz::reverse(std::forward<Iterable>(iterable)), std::move(predicate)));\n    }\n\n    /**\n     * @brief Drops elements from the back of the iterable as long as `predicate` returns `true`, then reverses the iterable\n     * again. Essentially turning the predicate into a not function, using `lz::reverse`, `lz::drop_while` followed by another\n     * `lz::reverse`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto dropped = vec | lz::drop_back_while([](int i) { return i > 3; }); // {1, 2, 3}\n     * ```\n     * @param predicate The predicate to determine which elements to drop from the back.\n     * @return A drop_back_iterable that can be iterated over, containing the elements\n     */\n    template<class UnaryPredicate>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, UnaryPredicate> operator()(UnaryPredicate predicate) const {\n        return { std::move(predicate) };\n    }\n};\n\ntemplate<class Iterable>\nusing trim_iterable = drop_back_iterable<drop_while_iterable<Iterable>>;\n\nstruct trim_adaptor {\n    using adaptor = trim_adaptor;\n\n    /**\n     * @brief Trims the front and back of the iterable using the provided predicates. The first predicate is used to drop elements\n     * from the front, and the second predicate is used to drop elements from the back. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto trimmed = lz::trim(vec, [](int i) { return i < 3; }, [](int i) { return i > 4; }); // {3, 4}\n     * ```\n     * @param iterable The iterable to trim.\n     * @param first The predicate to determine which elements to drop from the front.\n     * @param last The predicate to determine which elements to drop from the back.\n     * @return A trim_iterable that can be iterated over, containing the trimmed elements.\n     */\n    template<class Iterable, class UnaryPredicateFirst, class UnaryPredicateLast>\n    LZ_NODISCARD constexpr trim_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, UnaryPredicateFirst first, UnaryPredicateLast last) const {\n        return drop_back_while_adaptor{}(lz::drop_while(std::forward<Iterable>(iterable), std::move(first)), std::move(last));\n    }\n\n    /**\n     * @brief Trims whitespaces on the front and back of the given string.\n     * @param iterable The string to trim.\n     * ```cpp\n     * std::string str = \"  hello  \";\n     * auto trimmed = lz::trim(str); // \"hello\"\n     * // or\n     * auto trimmed = str | lz::trim; // \"hello\"\n     * @return A trim_iterable that can be iterated over, containing the trimmed\n     * string.\n     */\n    template<class CharT>\n    LZ_NODISCARD constexpr trim_iterable<const std::basic_string<CharT>>\n    operator()(const std::basic_string<CharT>& iterable) const {\n        return (*this)(iterable, trim_fn{}, trim_fn{});\n    }\n\n    /**\n     * @brief Trims whitespaces on the front and back of the given string.\n     * @param iterable The string to trim.\n     * ```cpp\n     * std::string str = \"  hello  \";\n     * auto trimmed = lz::trim(str); // \"hello\"\n     * // or\n     * auto trimmed = str | lz::trim; // \"hello\"\n     * @return A trim_iterable that can be iterated over, containing the trimmed\n     * string.\n     */\n    template<class CharT>\n    LZ_NODISCARD constexpr trim_iterable<copied_basic_sv<CharT>> operator()(lz::basic_string_view<CharT> iterable) const {\n        return (*this)(copied_basic_sv<CharT>(iterable), trim_fn{}, trim_fn{});\n    }\n\n    /**\n     * @brief Trims the front and back of the iterable using the provided predicates. The first predicate is used to drop elements\n     * from the front, and the second predicate is used to drop elements from the back. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto trimmed = vec | lz::trim([](int i) { return i < 3; }, [](int i) { return i > 4; }); // {3, 4}\n     * ```\n     * @param first The predicate to determine which elements to drop from the front.\n     * @param last The predicate to determine which elements to drop from the back.\n     * @return A trim_iterable that can be iterated over, containing the trimmed elements.\n     */\n    template<class UnaryPredicateFirst, class UnaryPredicateLast>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, UnaryPredicateFirst, UnaryPredicateLast>\n    operator()(UnaryPredicateFirst first, UnaryPredicateLast last) const {\n        return { std::move(first), std::move(last) };\n    }\n};\n\nstruct iter_decay {\n    using adaptor = iter_decay;\n\n    /**\n     * @brief Decays the given iterable to an iterator category of the given tag\n     * @p IteratorTag iterable, using `lz::as_iterator` and `lz::map` to\n     * dereference the iterators. This can be handy to return sentinels at some\n     * point or prevent `lz::eager_size` calls. Example:\n     * ```cpp\n     * auto v1 = {1, 2, 3};\n     * auto v2 = {1, 2, 3};\n     * // f1 is not sized and returns a bidirectional iterator\n     * auto f1 = lz::filter(v1, [](auto&& i) {\n     *     return i > 1;\n     * });\n     * // f2 is not sized and returns a bidirectional iterator\n     * auto f2 = lz::filter(v2, [](auto&& i) {\n     *     return i > 1;\n     * });\n     *\n     * // lz::zip calls lz::eager_size if:\n     * // - the iterable is at least bidirectional.\n     * // - the iterable is not sentinelled\n     * // f1 and f2 are both bidirectional, so lz::zip will call lz::eager_size\n     * on them.\n     * // In this case we don't want to call lz::eager_size because we're not\n     * interested in going bidirectionally.\n     * // We can use lz::iter_decay(std::forward_iterator_tag{}) to decay the\n     * iterable to a forward iterator\n     *\n     * iterable auto zipper = lz::zip(lz::iter_decay(f1,\n     * std::forward_iterator_tag{}), f2);\n     *\n     * // f1 or f2 can be forward, or both, for it not to call lz::eager_size on\n     * .end(). auto end = zipper.end(); // does not call lz::eager_size because\n     * f1 is decayed to a forward iterator.\n     * ```\n     * @param iterable The iterable to decay.\n     * @param it The tag that specifies the iterator category to decay to.\n     * @return An iterable with the iterator category of the given tag @p\n     * IteratorTag.\n     */\n    template<class Iterable, class IteratorTag>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 map_iterable<as_iterator_iterable<remove_ref_t<Iterable>, IteratorTag>, deref_fn>\n    operator()(Iterable&& iterable, IteratorTag it) const {\n        static_cast<void>(it);\n        return lz::map(lz::as_iterator(std::forward<Iterable>(iterable), IteratorTag{}), deref_fn{});\n    }\n\n    /**\n     * @brief Decays the given iterable to an iterator category of the given tag\n     * @p IteratorTag iterable, using `lz::as_iterator` and `lz::map` to\n     * dereference the iterators. This can be handy to return sentinels at some\n     * point or prevent `lz::eager_size` calls. Example:\n     * ```cpp\n     * auto v1 = {1, 2, 3};\n     * auto v2 = {1, 2, 3};\n     * // f1 is not sized and returns a bidirectional iterator\n     * auto f1 = lz::filter(v1, [](auto&& i) {\n     *     return i > 1;\n     * });\n     * // f2 is not sized and returns a bidirectional iterator\n     * auto f2 = lz::filter(v2, [](auto&& i) {\n     *     return i > 1;\n     * });\n     *\n     * // lz::zip calls lz::eager_size if:\n     * // - the iterable is at least bidirectional.\n     * // - the iterable is not sentinelled\n     * // f1 and f2 are both bidirectional, so lz::zip will call lz::eager_size\n     * on them.\n     * // In this case we don't want to call lz::eager_size because we're not\n     * interested in going bidirectionally.\n     * // We can use lz::iter_decay(iterable, std::forward_iterator_tag{}) to\n     * decay the iterables to a forward iterator\n     *\n     * iterable auto zipper = lz::zip(f1 |\n     * lz::iter_decay(std::forward_iterator_tag{}), f2);\n     *\n     * // f1 or f2 can be forward, or both, for it not to call lz::eager_size on\n     * .end(). auto end = zipper.end(); // does not call lz::eager_size because\n     * f1 is decayed to a forward iterator.\n     * ```\n     * @param tag The tag that specifies the iterator category to decay to.\n     * @return An iterable with the iterator category of the given tag @p\n     * IteratorTag.\n     */\n    template<class IteratorTag>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, IteratorTag> operator()(IteratorTag tag) const {\n        static_cast<void>(tag);\n        return {};\n    }\n};\n\ntemplate<class Iterable, class T>\nusing pad_iterable = concatenate_iterable<Iterable, lz::repeat_iterable<T>>;\n\nstruct pad_adaptor {\n    using adaptor = pad_adaptor;\n\n    /**\n     * @brief Pads the given iterable to a certain size, using `lz::concatenate` and `lz::repeat`. A size is needed to be provided\n     * to pad it to a certain size. It will return the most suitable reference type. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3 };\n     * auto padded = lz::pad(vec, 0, 2); // {1, 2, 3, 0, 0} by value\n     * auto to_pad = 3;\n     * auto padded = lz::pad(vec, to_pad, 0); // {1, 2, 3, 0, 0} by ref\n     * auto padded = lz::pad(vec, std::ref(to_pad), 0); // {1, 2, 3, 0, 0} by ref for more flexibility (copy ctors etc.)\n     * ```\n     * @param iterable The iterable to pad.\n     * @param value The value to pad the iterable with.\n     * @param amount The amount of times to repeat the value.\n     */\n    template<class Iterable, class T>\n    pad_iterable<remove_ref_t<Iterable>, remove_rvalue_reference_t<T>>\n    operator()(Iterable&& iterable, T&& value, const ptrdiff_t amount) const {\n        return lz::concat(std::forward<Iterable>(iterable), lz::repeat(std::forward<T>(value), amount));\n    }\n\n    /**\n     * @brief Pads the given iterable to a certain size, using `lz::concatenate` and `lz::repeat`. A size is needed to be provided\n     * to pad it to a certain size. It will return the most suitable reference type. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3 };\n     * auto padded = vec | lz::pad(0, 2); // {1, 2, 3, 0, 0} by value\n     * ```\n     * @param value The value to pad the iterable with.\n     * @param amount The amount of times to repeat the value.\n     */\n    template<class T>\n    fn_args_holder<adaptor, T, ptrdiff_t> operator()(T&& value, const ptrdiff_t amount) const {\n        return { std::forward<T>(value), amount };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_ITER_TOOLS_ADAPTORS_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/join_where.hpp",
    "content": "#pragma once\n\n#ifndef LZ_JOIN_WHERE_ADAPTOR_HPP\n#define LZ_JOIN_WHERE_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/join_where.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct join_where_adaptor {\n    using adaptor = join_where_adaptor;\n\n    /**\n     * @brief Performs an SQL-like join on two iterables where the join condition is specified by the selectors. Its end()\n     * function returns a sentinel, it contains a forward iterator. It also does not contain a size() method. The second iterable\n     * must be sorted first. It is therfore recommended to sort the smallest iterable, performance wise. Operator< is used to\n     * compare the first and the second selector. For instance, in the example below operator< is used to compare int with int.\n     * Example:\n     * ```cpp\n     * std::vector<std::pair<int, int>> vec = {{1, 2}, {2, 3}, {3, 4}, {4, 5}};\n     * std::vector<std::pair<int, int>> vec2 = {{1, 2}, {3, 4}, {4, 5}, {5, 6}};\n     * auto joined = lz::join_where(vec, vec2,\n     *                              [](const auto& a) { return a.first; }, // join on the first element of the pair in vec\n     *                              [](const auto& a) { return a.first; }, // join on the first element of the pair in vec2\n     *                              [](const auto& a, const auto& b) { return std::make_pair(a.first, b.second); }\n     * ); // joined contains: {{1, 2}, {3, 4}, {4, 5}}\n     * ```\n     * @param iterable_a The first iterable to join on\n     * @param iterable_b The second iterable to join on\n     * @param a The selector for the first iterable, this is the value that will be compared with the second selector\n     * @param b The selector for the second iterable, this is the value that will be compared with the first selector\n     * @param result_selector The result selector, this is the value that will be returned when the join condition (`a(*iter_a) <\n     * b(*iter_b)`) is met\n     * @return A join_where_iterable that can be used to iterate over the joined elements\n     */\n    template<class IterableA, class IterableB, class SelectorA, class SelectorB, class ResultSelector>\n    LZ_NODISCARD constexpr join_where_iterable<remove_ref_t<IterableA>, remove_ref_t<IterableB>, SelectorA, SelectorB,\n                                               ResultSelector>\n    operator()(IterableA&& iterable_a, IterableB&& iterable_b, SelectorA a, SelectorB b, ResultSelector result_selector) const {\n        return { std::forward<IterableA>(iterable_a), std::forward<IterableB>(iterable_b), std::move(a), std::move(b),\n                 std::move(result_selector) };\n    }\n\n    /**\n     * @brief Performs an SQL-like join on two iterables where the join condition is specified by the selectors. Its end()\n     * function returns a sentinel, it contains a forward iterator. It also does not contain a size() method. The second iterable\n     * must be sorted first. It is therfore recommended to sort the smallest iterable, performance wise. Operator< is used to\n     * compare the first and the second selector. For instance, in the example below operator< is used to compare int with int.\n     * Example:\n     * ```cpp\n     * std::vector<std::pair<int, int>> vec = {{1, 2}, {2, 3}, {3, 4}, {4, 5}};\n     * std::vector<std::pair<int, int>> vec2 = {{1, 2}, {3, 4}, {4, 5}, {5, 6}};\n     * auto joined = vec | lz::join_where(vec2,\n     *                                   [](const auto& a) { return a.first; }, // join on the first element of the pair in vec\n     *                                   [](const auto& a) { return a.first; }, // join on the first element of the pair in vec2\n     *                                   [](const auto& a, const auto& b) { return std::make_pair(a.first, b.second); }\n     * ); // joined contains: {{1, 2}, {3, 4}, {4, 5}}\n     * ```\n     * @param iterable_b The second iterable to join on\n     * @param a The selector for the first iterable, this is the value that will be compared with the second selector\n     * @param b The selector for the second iterable, this is the value that will be compared with the first selector\n     * @param result_selector The result selector, this is the value that will be returned when the join condition (`a(*iter_a) <\n     * b(*iter_b)`) is met\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class IterableB, class SelectorA, class SelectorB, class ResultSelector>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, IterableB, SelectorA, SelectorB, ResultSelector>\n    operator()(IterableB&& iterable_b, SelectorA a, SelectorB b, ResultSelector result_selector) const {\n        return { std::forward<IterableB>(iterable_b), std::move(a), std::move(b), std::move(result_selector) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/loop.hpp",
    "content": "#pragma once\n\n#ifndef LZ_LOOP_ADAPTOR_HPP\n#define LZ_LOOP_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/loop.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct loop_adaptor {\n    using adaptor = loop_adaptor;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief Loops over an iterable infinitely. Does not contain a .size() method. Loop infinitely over the input iterable. Its\n     * input iterator category will always be forward. It also returns a default_sentinel_t. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4 };\n     * auto looper = lz::loop(vec); // {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, ...}\n     * // or\n     * auto looper = vec | lz::loop; // {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, ...}\n     * ```\n     * @param iterable The iterable to loop over\n     * @return A loop_iterable that will loop over the input iterable infinitely.\n     */\n    template<class Iterable>\n    [[nodiscard]] constexpr loop_iterable<remove_ref_t<Iterable>, true> operator()(Iterable&& iterable) const\n        requires(!std::is_integral_v<remove_cvref_t<Iterable>>)\n    {\n        return loop_iterable<remove_ref_t<Iterable>, true>{ std::forward<Iterable>(iterable) };\n    }\n\n#else\n\n    /**\n     * @brief Loops over an iterable infinitely. Does not contain a .size() method. Loop infinitely over the input iterable. Its\n     * input iterator category will always be forward. It also returns a default_sentinel_t. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4 };\n     * auto looper = lz::loop(vec); // {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, ...}\n     * // or\n     * auto looper = vec | lz::loop; // {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, ...}\n     * ```\n     * @param iterable The iterable to loop over\n     * @return A loop_iterable that will loop over the input iterable infinitely.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr enable_if_t<!std::is_integral<remove_cvref_t<Iterable>>::value,\n                                       loop_iterable<remove_ref_t<Iterable>, true>>\n    operator()(Iterable&& iterable) const {\n        return loop_iterable<remove_ref_t<Iterable>, true>{ std::forward<Iterable>(iterable) };\n    }\n\n#endif\n\n    /**\n     * @brief Loops n times over an iterable. Contains a .size() method if the input iterable has a .size()\n     * method. Will return an actual iterator if the input iterable is at least bidirectional and isn't sentinelled. Otherwise it\n     * will return a default_sentinel_t. Its input iterator category will be the same as the input iterator category. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4 };\n     * auto looper = lz::loop(vec, 2); // {1, 2, 3, 4, 1, 2, 3, 4}\n     * // or\n     * auto looper = vec | lz::loop(2); // {1, 2, 3, 4, 1, 2, 3, 4}\n     * ```\n     * @param iterable The iterable to loop over\n     * @param amount The amount of times to loop over the iterable\n     * @return A loop_iterable that will loop over the input iterable n times.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr loop_iterable<remove_ref_t<Iterable>, false>\n    operator()(Iterable&& iterable, const diff_iterable_t<Iterable> amount) const {\n        return { std::forward<Iterable>(iterable), amount };\n    }\n\n    /**\n     * @brief Loops n times over an iterable. Contains a .size() method if the input iterable has a\n     * .size() method. Will return an actual iterator if the input iterable is at least bidirectional and isn't sentinelled.\n     * Otherwise it will return a default_sentinel_t. Its input iterator category will be the same as the input iterator category.\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4 };\n     * auto looper = vec | lz::loop(2); // {1, 2, 3, 4, 1, 2, 3, 4}\n     * ```\n     * @param amount The amount of times to loop over the iterable\n     * @return An adaptor that can be used in pipe expressions\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, ptrdiff_t> operator()(const ptrdiff_t amount) const {\n        return { amount };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_LOOP_ADAPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/map.hpp",
    "content": "#pragma once\n\n#ifndef LZ_MAP_ADAPTOR_HPP\n#define LZ_MAP_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/map.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct map_adaptor {\n    using adaptor = map_adaptor;\n\n    /**\n     * @brief This adaptor is used to apply a function to each element in an iterable. The iterator category is the same as the\n     * input iterator category. Its end() function will return a sentinel, if the input iterable has a forward iterator or has a\n     * sentinel. If its input iterable has a .size() method, then this iterable will also have a .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto map = lz::map(vec, [](int i) { return i * 2; }); // map = { 2, 4, 6, 8, 10 }\n     * ```\n     * @param iterable The iterable to apply the function to\n     * @param function The function to apply to each element in the iterable\n     * @return A map_iterable that applies the function to each element in the iterable\n     */\n    template <class Iterable, class Function>\n    constexpr map_iterable<remove_ref_t<Iterable>, Function>\n    operator()(Iterable &&iterable, Function function) const {\n      return {std::forward<Iterable>(iterable), std::move(function)};\n    }\n\n    /**\n     * @brief This adaptor is used to apply a function to each element in an iterable. The iterator category is the same as the\n     * input iterator category. Its end() function will return a sentinel, if the input iterable has a forward iterator or has a\n     * sentinel. If its input iterable has a .size() method, then this iterable will also have a .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto map = vec | lz::map([](int i) { return i * 2; }); // map = { 2, 4, 6, 8, 10 }\n     * ```\n     * @param function The function to apply to each element in the iterable\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class Function>\n    LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, Function> operator()(Function&& function) const {\n        return { std::move(function) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/pairwise.hpp",
    "content": "#pragma once\n\n#ifndef LZ_PAIRWISE_ADAPTOR_HPP\n#define LZ_PAIRWISE_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/pairwise.hpp>\n\nnamespace lz {\nnamespace detail {\n\nstruct pairwise_adaptor {\n    using adaptor = pairwise_adaptor;\n    /**\n     * @brief Creates a pairwise iterable with pairs of size @p pair_size from the given @p iterable\n     * ```cpp\n     * auto vec = std::vector{1, 2, 3, 4, 5};\n     * auto paired = lz::pairwise(vec, 2); // {{1, 2}, {2, 3}, {3, 4}, {4, 5}}\n     * ```\n     * @param iterable The iterable to create the pairwise iterable from\n     * @param pair_size The size of the pairs\n     * @return A pairwise iterable with pairs of size @p pair_size\n     */\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 pairwise_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, const diff_iterable_t<Iterable> pair_size) const {\n        return { std::forward<Iterable>(iterable), pair_size };\n    }\n\n    /**\n     * @brief Creates a function object that can be used to create a pairwise iterable with pairs of size @p pair_size\n     * from the given iterable\n     * ```cpp\n     * auto vec = std::vector{1, 2, 3, 4, 5};\n     * auto pairwise_by_2 = vec | lz::pairwise(2); // {{1, 2}, {2, 3}, {3, 4}, {4, 5}}\n     * ```\n     * @param pair_size The size of the pairs\n     * @return A function object that can be used to create a pairwise iterable with pairs of size @p pair_size\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, ptrdiff_t> operator()(const ptrdiff_t pair_size) const {\n        return { pair_size };\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/random.hpp",
    "content": "#pragma once\n\n#ifndef LZ_RANDOM_ADAPTOR_HPP\n#define LZ_RANDOM_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/random.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <algorithm>\n#include <random>\n\nnamespace lz {\nnamespace detail {\ntemplate<size_t N>\nclass seed_sequence {\npublic:\n    using result_type = std::seed_seq::result_type;\n\nprivate:\n    using seed_array = result_type[N];\n    seed_array _seed;\n\n    template<class Iter>\n    LZ_CONSTEXPR_CXX_20 void create(Iter begin, Iter end) {\n        using value_type = val_t<Iter>;\n        std::transform(begin, end, detail::begin(_seed), [](const value_type val) { return static_cast<result_type>(val); });\n    }\n\n    result_type T(const result_type x) const {\n        return x ^ (x >> 27u);\n    }\n\npublic:\n    constexpr seed_sequence() = default;\n\n    explicit seed_sequence(std::random_device& rd) {\n        std::generate(detail::begin(_seed), detail::end(_seed), [&rd]() { return rd(); });\n    }\n\n    template<class T>\n    LZ_CONSTEXPR_CXX_20 seed_sequence(std::initializer_list<T> values) {\n        create(values.begin(), values.end());\n    }\n\n    template<class Iter>\n    LZ_CONSTEXPR_CXX_20 seed_sequence(Iter first, Iter last) {\n        create(first, last);\n    }\n\n    seed_sequence(const seed_sequence&) = delete;\n    seed_sequence& operator=(const seed_sequence&) = delete;\n\n    template<class Iter>\n    LZ_CONSTEXPR_CXX_20 void generate(Iter begin, Iter end) const {\n        if (begin == end) {\n            return;\n        }\n\n        using iter_value_type = val_t<Iter>;\n\n        std::fill(begin, end, 0x8b8b8b8b);\n        const auto n = static_cast<size_t>(end - begin);\n        constexpr auto s = N;\n        const size_t m = std::max(s + 1, n);\n        const size_t t = (n >= 623) ? 11 : (n >= 68) ? 7 : (n >= 39) ? 5 : (n >= 7) ? 3 : (n - 1) / 2;\n        const size_t p = (n - t) / 2;\n        const size_t q = p + t;\n\n        auto mask = static_cast<iter_value_type>(1) << 31;\n        mask <<= 1;\n        mask -= 1;\n\n        for (size_t k = 0; k < m - 1; k++) {\n            const size_t k_mod_n = k % n;\n            const size_t k_plus_mod_n = (k + p) % n;\n            const result_type r1 = 1664525 * T(begin[k_mod_n] ^ begin[k_plus_mod_n] ^ begin[(k - 1) % n]);\n\n            result_type r2;\n            if (k == 0) {\n                r2 = static_cast<result_type>((r1 + s) & mask);\n            }\n            else if (k <= s) {\n                r2 = static_cast<result_type>((r1 + k_mod_n + _seed[k - 1]) & mask);\n            }\n            else {\n                r2 = static_cast<result_type>((r1 + k_mod_n) & mask);\n            }\n\n            begin[k_plus_mod_n] += (r1 & mask);\n            begin[(k + q) % n] += (r2 & mask);\n            begin[k_mod_n] = r2;\n        }\n\n        for (size_t k = m; k < m + n - 1; k++) {\n            const size_t k_mod_n = k % n;\n            const size_t k_plus_mod_n = (k + p) % n;\n            const result_type r3 = 1566083941 * T(begin[k_mod_n] + begin[k_plus_mod_n] + begin[(k - 1) % n]);\n            const auto r4 = static_cast<result_type>((r3 - k_mod_n) & mask);\n\n            begin[k_plus_mod_n] ^= (r3 & mask);\n            begin[(k + q) % n] ^= (r4 & mask);\n            begin[k_mod_n] = r4;\n        }\n    }\n\n    template<class Iter>\n    LZ_CONSTEXPR_CXX_20 void param(Iter output_iter) const {\n        std::copy(_seed.begin(), _seed.end(), output_iter);\n    }\n\n    static constexpr size_t size() noexcept {\n        return N;\n    }\n};\n\nusing prng_engine = std::mt19937;\n\ninline prng_engine create_engine() {\n    std::random_device rd;\n    seed_sequence<8> seed_seq(rd);\n    return prng_engine(seed_seq);\n}\n\ntemplate<bool UseSentinel>\nstruct random_adaptor {\n    using adaptor = random_adaptor<UseSentinel>;\n\n    /**\n     * @brief Generates n amount of random numbers. May or may not contain a sentinel, depending on the adaptor used. Use\n     * `lz::common_random` for no sentinel, otherwise use `lz::random`. Prefer using `lz::random` if you do not need an actual end\n     * iterator to avoid unnecessary overhead. Further, it contains a .size() method and is random access. Example:\n     * ```cpp\n     * std::mt19937 gen;\n     * std::uniform_real_distribution<double> dist(0., 1.);\n     * auto random = lz::random(dist, gen, 5); // random = { 0.1, 0.2, 0.3, 0.4, 0.5 } (random * numbers)\n     * ```\n     * @param distribution The random distribution to use, for instance std::uniform_real_distribution<double>.\n     * @param generator The random engine to use, for instance std::mt19937.\n     * @param amount The amount of random numbers to generate.\n     */\n    template<class Distribution, class Generator>\n    LZ_NODISCARD constexpr random_iterable<typename Distribution::result_type, Distribution, Generator, UseSentinel>\n    operator()(const Distribution& distribution, Generator& generator, const ptrdiff_t amount) const {\n        return { distribution, generator, amount };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    /**\n     * @brief Generates n amount of random numbers. May or may not contain a sentinel, depending on the adaptor used. Use\n     * `lz::common_random` for no sentinel, otherwise use `lz::random`. Prefer using `lz::random` if you do not need an actual end\n     * iterator to avoid unnecessary overhead. Further, it contains a .size() method and is random access. Example:\n     * ```cpp\n     * // Uses std::uniform_real_distribution<double> as distribution, a seed length of 8 random numbers (using\n     * // std::random_device) and a std::mt19937 engine.\n     * auto random = lz::common_random(0., 1., 5); // random = { 0.1, 0.2, 0.3, 0.4, 0.5 } 5 random double numbers between 0 and 1\n     * // (inclusive)\n     * // with integers. Uses std::uniform_int_distribution<int> as distribution, a seed length of 8 random numbers (using\n     * // std::random_device) and a std::mt19937 engine.\n     * auto random = lz::common_random(0, 10, 5); // random = { 1, 2, 3, 4, 5 } 5 random integers between 0 and 10 (inclusive)\n     * ```\n     * @param min The minimum value of the random numbers (inclusive).\n     * @param max The maximum value of the random numbers (inclusive).\n     * @param amount The amount of random numbers to generate.\n     */\n    template<class T>\n    [[nodiscard]] auto operator()(const T min, const T max, const ptrdiff_t amount) const {\n        static auto gen = create_engine();\n        if constexpr (std::is_integral_v<T>) {\n            return (*this)(std::uniform_int_distribution<T>{ min, max }, gen, amount);\n        }\n        else if constexpr (std::is_floating_point_v<T>) {\n            return (*this)(std::uniform_real_distribution<T>{ min, max }, gen, amount);\n        }\n        else {\n            static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>,\n                          \"Type must be either integral or floating point for random generation.\");\n        }\n    }\n#else\n\n    /**\n     * @brief Generates n amount of random numbers. May or may not contain a sentinel, depending on the adaptor used. Use\n     * `lz::common_random` for no sentinel, otherwise use `lz::random`. Prefer using `lz::random` if you do not need an actual end\n     * iterator to avoid unnecessary overhead. Further, it contains a .size() method. Example:\n     * ```cpp\n     * // Uses std::uniform_real_distribution<double> as distribution, a seed length of 8 random numbers (using\n     * // std::random_device) and a std::mt19937 engine.\n     * auto random = lz::common_random(0., 1., 5); // random = { 0.1, 0.2, 0.3, 0.4, 0.5 } 5 random double numbers between 0 and 1\n     * // (inclusive)\n     * // with integers. Uses std::uniform_int_distribution<int> as distribution, a seed length of 8 random numbers (using\n     * // std::random_device) and a std::mt19937 engine.\n     * auto random = lz::common_random(0, 10, 5); // random = { 1, 2, 3, 4, 5 } 5 random integers between 0 and 10 (inclusive)\n     * ```\n     * @param min The minimum value of the random numbers (inclusive).\n     * @param max The maximum value of the random numbers (inclusive).\n     * @param amount The amount of random numbers to generate.\n     */\n    template<class Integral>\n    LZ_NODISCARD enable_if_t<std::is_integral<Integral>::value,\n                             random_iterable<Integral, std::uniform_int_distribution<Integral>, prng_engine, UseSentinel>>\n    operator()(const Integral min, const Integral max, const ptrdiff_t amount) const {\n        static auto gen = create_engine();\n        return (*this)(std::uniform_int_distribution<Integral>{ min, max }, gen, amount);\n    }\n\n    /**\n     * @brief Generates n amount of random numbers. May or may not contain a sentinel, depending on the adaptor used. Use\n     * `lz::common_random` for no sentinel, otherwise use `lz::random`. Prefer using `lz::random` if you do not need an actual end\n     * iterator to avoid unnecessary overhead. Further, it contains a .size() method. Example:\n     * ```cpp\n     * // Uses std::uniform_real_distribution<double> as distribution, a seed length of 8 random numbers (using\n     * // std::random_device) and a std::std::mt19937 engine.\n     * auto random = lz::common_random(0., 1., 5); // random = { 0.1, 0.2, 0.3, 0.4, 0.5 } 5 random double numbers between 0 and 1\n     * // (inclusive). Uses std::uniform_int_distribution<int> as distribution, a seed length of 8 random numbers (using\n     * // std::random_device) and a std::std::mt19937 engine.\n     * auto random = lz::common_random(0, 10, 5); // random = { 1, 2, 3, 4, 5 } 5 random integers between 0 and 10 (inclusive)\n     * ```\n     * @param min The minimum value of the random numbers (inclusive).\n     * @param max The maximum value of the random numbers (inclusive).\n     * @param amount The amount of random numbers to generate.\n     */\n    template<class Floating>\n    LZ_NODISCARD enable_if_t<std::is_floating_point<Floating>::value,\n                             random_iterable<Floating, std::uniform_real_distribution<Floating>, prng_engine, UseSentinel>>\n    operator()(const Floating min, const Floating max, const ptrdiff_t amount) const {\n        static auto gen = create_engine();\n        return (*this)(std::uniform_real_distribution<Floating>{ min, max }, gen, amount);\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/range.hpp",
    "content": "#pragma once\n\n#ifndef LZ_RANGE_ADAPTOR_HPP\n#define LZ_RANGE_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/range.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct range_adaptor {\n    using adaptor = range_adaptor;\n\n    /**\n     * @brief Creates n amount of numbers starting from 0 (or specified otherwise). It contains a\n     * .size() method and is random access. Example:\n     * ```\n     * auto range = lz::range(0.0, 1.0, 0.1); // {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9} (T = double)\n     * auto range = lz::range(0.0, 1.0, 0.2); // {0.0, 0.2, 0.4, 0.6, 0.8} (T = double)\n     * auto range = lz::range(0, 10); // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} (T = int)\n     * auto range = lz::range(0, 10, 2) | lz::reverse; // {8, 6, 4, 2, 0} (T = int)\n     * ```\n     * @param start The start of the range\n     * @param end The end of the range (exclusive)\n     * @param step The step of the range\n     * @return A range_iterable that can be used in range-based for loops or with other algorithms\n     */\n    template<class Arithmetic>\n    LZ_NODISCARD constexpr range_iterable<Arithmetic, true>\n    operator()(const Arithmetic start, const Arithmetic end, const Arithmetic step) const noexcept {\n        return { start, end, step };\n    }\n\n    /**\n     * @brief Creates n amount of numbers starting from 0 (or specified otherwise). It contains a\n     * .size() method and is random access. Example:\n     * ```\n     * // This will create a range from 0 to 4\n     * auto range = lz::range(5); // {0, 1, 2, 3, 4} (T = int)\n     * ```\n     * @param end The end of the range (exclusive)\n     * @return A range_iterable that can be used in range-based for loops or with other algorithms\n     */\n    template<class Arithmetic>\n    LZ_NODISCARD constexpr range_iterable<Arithmetic, false>\n    operator()(const Arithmetic start, const Arithmetic end) const noexcept {\n        return { start, end };\n    }\n\n    /**\n     * @brief Creates n amount of numbers starting from 0 (or specified otherwise). It contains a\n     * .size() method and is random access. Example:\n     * ```\n     * // This will create a range from 0 to 4\n     * auto range = lz::range(5); // {0, 1, 2, 3, 4} (T = int)\n     * ```\n     * @param end The end of the range (exclusive)\n     * @return A range_iterable that can be used in range-based for loops or with other algorithms\n     */\n    template<class Arithmetic>\n    LZ_NODISCARD constexpr range_iterable<Arithmetic, false> operator()(const Arithmetic end) const noexcept {\n        return { 0, end };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/regex_split.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REGEX_SPLIT_ADAPTOR_HPP\n#define LZ_REGEX_SPLIT_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/regex_split.hpp>\n#include <regex>\n\nnamespace lz {\nnamespace detail {\nstruct regex_split_adaptor {\n    using adaptor = regex_split_adaptor;\n\n    template<class String>\n    using iter = decltype(detail::begin(std::declval<const String>()));\n\n    template<class String>\n    using regex_it = std::regex_token_iterator<iter<String>>;\n\n    /**\n     * @brief Splits a string based on a regex. The regex must be by reference. The `begin()` and `end()` types are different, but\n     * `end()` of type `RegexTokenIterator`. For std::regex, this means `end() == std::regex_token_iterator<>{}` and `begin() =\n     * lz::regex_split_iterator`. It does not contain a .size() method and its iterator category is forward. Example:\n     * ```cpp\n     * std::regex r1(R\"(\\s+)\");\n     * std::string s = \"    Hello, world! How are you?\";\n     * auto splitter = lz::regex_split(s, r1); // { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" }\n     * ```\n     * @param s The string to split\n     * @param regex The regex to use to split the string with\n     * @return An iterable that can be used to iterate over the split strings\n     */\n    template<class String>\n    LZ_NODISCARD regex_split_iterable<regex_it<String>, regex_it<String>>\n    operator()(const String& s, const std::basic_regex<typename String::value_type>& regex) const {\n        using token_iter = std::regex_token_iterator<iter<String>>;\n        token_iter first(s.begin(), s.end(), regex, -1);\n        return (*this)(std::move(first), token_iter{});\n    }\n\n    /**\n     * @brief Splits a string based on a regex. The regex must be by reference. The `begin()` and `end()` types are different, but\n     * `end()` of type `RegexTokenIterator`. For std::regex, this means `end() == std::regex_token_iterator<>{}` and `begin() =\n     * lz::regex_split_iterator`. It does not contain a .size() method and its iterator category is forward. Example:\n     * ```cpp\n     * std::regex r1(R\"(\\s+)\");\n     * std::string s = \"    Hello, world! How are you?\";\n     * auto splitter = s | lz::regex_split(r1); // { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" }\n     * ```\n     * @param regex The regex to use to split the string with\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class CharT>\n    LZ_NODISCARD fn_args_holder<adaptor, const std::basic_regex<CharT>&> operator()(const std::basic_regex<CharT>& regex) const {\n        return { regex };\n    }\n\n    /**\n     * @brief Splits a string based on a regex. The regex must be by reference. The `begin()` and `end()` types are different, but\n     * `end()` of type `RegexTokenIterator`. For std::regex, this means `end() == std::regex_token_iterator<>{}` and `begin() =\n     * lz::regex_split_iterator`. It does not contain a .size() method and its iterator category is forward. Example:\n     * ```cpp\n     * std::regex r1(R\"(\\s+)\");\n     * std::string s = \"    Hello, world! How are you?\";\n     * auto begin = std::sregex_token_iterator(s.begin(), s.end(), r1, -1); // -1 means it will skip the matches (which is\n     * // exactly what we want), rather than returning the matches\n     * auto end = std::sregex_token_iterator<>();\n     * auto splitter = lz::regex_split(begin, end); // { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" }\n     * ```\n     * @param first The first regex iterator\n     * @param last The last regex iterator\n     * @return An iterable that can be used to iterate over the split strings\n     */\n    template<class RegexTokenIter, class RegexTokenSentinel>\n    LZ_NODISCARD regex_split_iterable<RegexTokenIter, RegexTokenSentinel>\n    operator()(RegexTokenIter first, RegexTokenSentinel last) const {\n        return { std::move(first), std::move(last) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/repeat.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REPEAT_ADAPTOR_HPP\n#define LZ_REPEAT_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/repeat.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct repeat_adaptor {\n    using adaptor = repeat_adaptor;\n\n    /**\n     * @brief Returns an element n (or infinite) times. It returns a sentinel as its end() iterator. It contains a .size() method\n     * which is equal to the amount of times the element is repeated (if it is not infinite). Its iterator category is random\n     * access. Will return the same reference type as the reference type passed. Example:\n     * ```cpp\n     * auto repeater = lz::repeat(20, 5); // {20, 20, 20, 20, 20} by value\n     * // infinite variant\n     * auto repeater = lz::repeat(20); // {20, 20, 20, ...} by value\n     * auto val = 3;\n     * auto repeater =  lz::repeat(val, 5); // {3, 3, 3, 3, 3}, by reference\n     * auto repeater = lz::repeat(std::ref(val), 5); // {3, 3, 3, 3, 3}, by reference for more flexibility (copy ctors etc.)\n     * auto repeater =  lz::repeat(std::as_const(val)); // {3, 3, 3, ...}, by const reference\n     *\n     * ```\n     * @param value The value to repeat\n     * @param amount The amount of times to repeat the value\n     * @return A repeat_iterable that will yield the value @p `amount` times.\n     */\n    template<class T>\n    LZ_NODISCARD constexpr repeat_iterable<remove_rvalue_reference_t<T>, false> operator()(T&& value, const ptrdiff_t amount) const {\n        return { std::forward<T>(value), amount };\n    }\n\n    /**\n     * @brief Returns an element infinite times. It returns a sentinel as its end() iterator. It does not\n     * contains a .size() method. Its iterator category is random access. Its iterator category is random\n     * access. Will return the same reference type as the reference type passed. Example:\n     * ```cpp\n     * auto repeater = lz::repeat(20, 5); // {20, 20, 20, 20, 20} by value\n     * auto val = 3;\n     * auto repeater = lz::repeat(std::as_const(val)); // {3, 3, 3, ...}, by const reference\n     * auto repeater = lz::repeat(val); // {3, 3, 3, ...}, by reference\n     * auto repeater = lz::repeat(std::ref(val)); // {3, 3, 3, ...}, by reference for more flexibility (copy ctors etc.)\n     * ```\n     * @param value The value to repeat\n     * @return A repeat_iterable that will yield the value infinite times.\n     */\n    template<class T>\n    LZ_NODISCARD constexpr repeat_iterable<T, true> operator()(T&& value) const {\n        return repeat_iterable<T, true>{ std::forward<T>(value) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/reverse.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REVERSE_ADAPTOR_HPP\n#define LZ_REVERSE_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/reverse.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<bool Cached>\nstruct reverse_adaptor {\n    using adaptor = reverse_adaptor;\n\n    /**\n     * @brief Reveres the iterable. Contains a size method if the iterable has a size method. Example:\n     * ```cpp\n     * std::vector<int> v = { 1, 2, 3, 4, 5 };\n     * auto reversed = lz::reverse(v); // { 5, 4, 3, 2, 1 }\n     * // or\n     * auto reversed = v | lz::reverse; // { 5, 4, 3, 2, 1 }\n     * auto reversed_cached = v | lz::cached_reverse; // { 5, 4, 3, 2, 1 }\n     * // or\n     * auto reversed_cached = lz::cached_reverse(v); // { 5, 4, 3, 2, 1 }\n     * ```\n     * @param iterable The iterable to reverse.\n     * @return A (cached_)reverse_iterable object that can be used to iterate over the reversed iterable.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr reverse_iterable<remove_ref_t<Iterable>, Cached> operator()(Iterable&& iterable) const {\n        return reverse_iterable<remove_ref_t<Iterable>, Cached>{ std::forward<Iterable>(iterable) };\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_REVERSE_ADAPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/rotate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ROTATE_ADAPTOR_HPP\n#define LZ_ROTATE_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/rotate.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct rotate_adaptor {\n    using adaptor = rotate_adaptor;\n\n    /**\n     * @brief Rotates the input iterable by n elements.  If `n` is greater than or equal to its size then the iterable will be\n     * empty. Contains a .size() method if the input iterable also has a .size() method. Its iterator category is the same as the\n     * input iterable. If the input iterable is forward or has a sentinel, it will return the end type of its input iterable,\n     * rather than a rotate_iterator/default_sentinel_t. If n == iterable.size() then calling begin()/end() is undefined. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto rotated = lz::rotate(vec, 2); // rotated = { 3, 4, 5, 1, 2 }\n     * auto rotated = lz::rotate(vec, 50); // rotated = {}\n     *\n     * // or, in case input iterable is not forward:\n     * auto str = lz::c_string(\"Hello, World!\");\n     * auto rotated = lz::rotate(str, 7); // rotated = \"World!Hello, \"\n     * // rotated.end() will be a sentinel, rather than an actual iterator, as rotated str is forward\n     * ```\n     * @param iterable The input iterable to rotate.\n     * @param start The amount of elements to rotate the input iterable by.\n     * @return A rotate_iterable that can be used to iterate over the rotated elements.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr rotate_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, const diff_iterable_t<Iterable> start) const {\n        return { std::forward<Iterable>(iterable), start };\n    }\n\n    /**\n     * @brief Rotates the input iterable by n elements.  If `n` is greater than or equal to its size then the iterable will be\n     * empty. Contains a .size() method if the input iterable also has a .size() method. Its iterator category is the same as the\n     * input iterable. If the input iterable is forward or has a sentinel, it will return the end type of its input iterable,\n     * rather than a rotate_iterator/default_sentinel_t. If n == iterable.size() then calling begin()/end() is undefined. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n     * auto rotated = vec | lz::rotate(2); // rotated = { 3, 4, 5, 1, 2 }\n     * auto rotated = vec | lz::rotate(50); // rotated = {}\n     *\n     * // or, in case input iterable is not forward:\n     * auto str = lz::c_string(\"Hello, World!\");\n     * auto rotated = str | lz::rotate(7); // rotated = \"World!Hello, \"\n     * // rotated.end() will be a sentinel, rather than an actual iterator, as rotated str is forward\n     * ```\n     * @param start The amount of elements to rotate the input iterable by.\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class DiffT>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, DiffT> operator()(const DiffT start) const {\n        return { start };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/slice.hpp",
    "content": "#pragma once\n\n#ifndef LZ_SLICE_ADAPTOR_HPP\n#define LZ_SLICE_ADAPTOR_HPP\n\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/drop.hpp>\n#include <Lz/take.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable>\nusing slice_iterable = detail::take_iterable<detail::drop_iterable<Iterable>>;\n\nstruct slice_adaptor {\n    using adaptor = slice_adaptor;\n\n    /**\n     * @brief This adaptor is used to slice an iterable from `from` to `to`. The iterator category is the same as the input\n     * iterator category. Its end() function will return a sentinel, if the input iterable has a forward iterator or has a\n     * sentinel. If its input iterable has a .size() method, then this iterable will also have a .size() method.Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n     * auto slice = lz::slice(vec, 2, 6); // slice = { 3, 4, 5, 6 }\n     * ```\n     * @param iterable The iterable to slice\n     * @param from The start index of the slice\n     * @param to The end index of the slice\n     * @return A slice_iterable that can be used to iterate over the sliced elements.\n     */\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 slice_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, const ptrdiff_t from, const ptrdiff_t to) const {\n        LZ_ASSERT(to > from, \"`to` must be greater than `from`\");\n        return { lz::drop(std::forward<Iterable>(iterable), from), to - from };\n    }\n\n    /**\n     * @brief This adaptor is used to slice an iterable from `from` to `to`. The iterator category is the same as the input\n     * iterator category. Its end() function will return a sentinel, if the input iterable has a forward iterator or has a\n     * sentinel. If its input iterable has a .size() method, then this iterable will also have a .size() method.Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n     * auto slice = vec | lz::slice(2, 6); // slice = { 3, 4, 5, 6 }\n     * ```\n     * @param from The start index of the slice\n     * @param to The end index of the slice\n     * @return An adaptor that can be used in pipe expressions\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 detail::fn_args_holder<adaptor, ptrdiff_t, ptrdiff_t>\n    operator()(const ptrdiff_t from, const ptrdiff_t to) const {\n        return { from, to };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/split.hpp",
    "content": "#pragma once\n\n#ifndef LZ_SPLIT_ADAPTOR_HPP\n#define LZ_SPLIT_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/split.hpp>\n#include <Lz/util/string_view.hpp>\n\nnamespace lz {\n\n/**\n * @brief Wrapper for lz::copied<lz::basic_string_view<CharT>>.\n *\n * @tparam CharT The character type of the string view.\n */\ntemplate<class CharT>\nusing copied_basic_sv = lz::copied<lz::basic_string_view<CharT>>;\n\n/**\n * @brief Wrapper for lz::copied<lz::string_view>.\n */\nusing copied_sv = copied_basic_sv<char>;\n\nnamespace detail {\n\ntemplate<class ValueType>\nstruct split_adaptor {\n    using adaptor = split_adaptor<ValueType>;\n\n#ifdef LZ_HAS_CXX_17\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * using iterable = lz::basic_iterable<decltype(arr.begin)>;\n     * auto splitted = lz::t_split<iterable>(arr, 2); // { { 1 }, { 3, 4, 5 } } where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>\n     * // which is equal to lz::split(arr, 2)\n     * auto to_split_on = {1, 2};\n     * auto splitted2 = lz::t_split<iterable>(arr, to_split_on); // {{3, 4, 5}} where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>. to_split_on must be a reference in this case\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The delimiter to split on. Must be a reference if it is an iterable (string_view\n     * or c-strings excluded)\n     * @return A split_iterable that splits the iterable on the delimiter.\n     */\n    template<class Iterable, class T>\n    constexpr auto operator()(Iterable&& iterable, T&& delimiter) const {\n        using T2 = std::remove_cv_t<remove_ref_t<T>>;\n\n        if constexpr (is_iterable_v<T2>) {\n            using splitter = split_iterable<ValueType, remove_ref_t<Iterable>, remove_ref_t<T>>;\n            return splitter{ std::forward<Iterable>(iterable), std::forward<T>(delimiter) };\n        }\n        else {\n            using splitter = split_iterable<ValueType, remove_ref_t<Iterable>, T2>;\n            return splitter{ std::forward<Iterable>(iterable), std::forward<T>(delimiter) };\n        }\n    }\n\n#else\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * using iterable = lz::basic_iterable<decltype(arr.begin)>;\n     * auto splitted = lz::t_split<iterable>(arr, 2); // { { 1 }, { 3, 4, 5 } } where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>\n     * // which is equal to lz::split(arr, 2)\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The delimiter to split on. Doesn't have to be a reference\n     * @return A split_iterable that splits the iterable on the delimiter.\n     */\n    template<class Iterable, class T>\n    constexpr enable_if_t<!is_iterable<T>::value, split_iterable<ValueType, remove_ref_t<Iterable>, T>>\n    operator()(Iterable&& iterable, T delimiter) const {\n        return { std::forward<Iterable>(iterable), std::forward<T>(delimiter) };\n    }\n\n    // clang-format off\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * std::vector<int> to_split = { 3, 4 }; // must be by reference\n     * using iterable = lz::basic_iterable<decltype(arr.begin)>;\n     * auto splitted = lz::t_split<iterable>(arr, to_split); // {{1, 2}, {5}} where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>\n     * // which is equal to lz::split(arr, to_split)\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The delimiter to split on. Must be a reference if it is an iterable (string_view\n     * or c-strings excluded)\n     * @return A split_iterable that splits the iterable on the delimiter.\n     */\n    template<class Iterable, class Iterable2>\n    LZ_NODISCARD constexpr\n    enable_if_t<is_iterable<Iterable2>::value, split_iterable<ValueType, remove_ref_t<Iterable>, remove_ref_t<Iterable2>>>\n    operator()(Iterable&& iterable, Iterable2&& delimiter) const {\n        return { std::forward<Iterable>(iterable), std::forward<Iterable2>(delimiter) };\n    }\n\n    // clang-format on\n\n#endif\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t\n     * and the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<char, 5> arr = { 'h', 'e', 'l', 'l', 'o' };\n     * using iterable = lz::basic_iterable<decltype(arr.begin)>;\n     * auto splitted = lz::t_split<iterable>(arr, \"ll\"); // {{'h', 'e'}, {'o'}} where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>\n     * // which is equal as lz::split(arr, \"ll\")\n     *\n     * auto splitted = lz::t_split<std::string>(arr, \"ll\"); // {\"he\", \"o\"} where value_type = std::string\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The delimiter to split on for c-strings\n     * @return A split_iterable that splits the iterable on the delimiter.\n     */\n    template<class Iterable, class CharT>\n    constexpr split_iterable<ValueType, remove_ref_t<Iterable>, copied_basic_sv<CharT>>\n    operator()(Iterable&& iterable, const CharT* delimiter) const {\n        return (*this)(std::forward<Iterable>(iterable), lz::string_view(delimiter));\n    }\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t\n     * and the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<char, 5> arr = { 'h', 'e', 'l', 'l', 'o' };\n     * using iterable = lz::basic_iterable<decltype(arr.begin)>;\n     * auto splitted = lz::t_split<iterable>(arr, lz::string_view(\"ll\")); // {{'h', 'e'}, {'o'}} where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The string_view delimiter, it's copied and therefore does not have to be a reference.\n     * @return A split_iterable that splits the iterable on the delimiter.\n     */\n    template<class Iterable, class CharT>\n    constexpr split_iterable<ValueType, remove_ref_t<Iterable>, copied_basic_sv<CharT>>\n    operator()(Iterable&& iterable, const basic_string_view<CharT> delimiter) const {\n        return (*this)(std::forward<Iterable>(iterable), copied_basic_sv<CharT>(delimiter));\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * std::vector<int> to_split = { 3, 4 }; // must be by reference\n     * using iterable = lz::basic_iterable<decltype(arr.begin)>;\n     * auto splitted = arr | lz::t_split<iterable>(to_split); // {{1, 2}, {5}} where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>\n     * // which is equal to arr | lz::split(to_split)\n     * auto to_split_on = {1, 2};\n     * auto splitted2 = arr | lz::t_split<iterable>(to_split_on); // {{3, 4, 5}} where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>. to_split_on must be a reference in this case\n     * ```\n     * @param delimiter The iterable to split. Must be a reference if it is an iterable (string_view\n     * or c-strings excluded)\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class T>\n    LZ_CONSTEXPR_CXX_14 auto operator()(T&& delimiter) const {\n        using T2 = std::remove_cv_t<remove_ref_t<T>>;\n\n        if constexpr (is_iterable_v<T2>) {\n            return fn_args_holder<adaptor, T>{ std::forward<T>(delimiter) };\n        }\n        else {\n            return fn_args_holder<adaptor, T2>{ std::forward<T>(delimiter) };\n        }\n    }\n\n#else\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * std::vector<int> to_split = { 3, 4 }; // must be by reference\n     * using iterable = lz::basic_iterable<decltype(arr.begin)>;\n     * auto splitted = arr | lz::t_split<iterable>(to_split); // {{1, 2}, {5}} where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>\n     * // which is equal to arr | lz::split(to_split)\n     * ```\n     * @param delimiter The iterable to split. Must be a reference if it is an iterable (string_view\n     * or c-strings excluded)\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class T>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<is_iterable<T>::value, fn_args_holder<adaptor, T>> operator()(T&& delimiter) const {\n        return { std::forward<T>(delimiter) };\n    }\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * using iterable = lz::basic_iterable<decltype(arr.begin)>;\n     * auto splitted = arr | lz::t_split<iterable>(3); // {{1, 2}, {4, 5}} where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>\n     * // which is equal to arr | lz::split(3)\n     * ```\n     * @param delimiter The iterable to split.\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class T>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!is_iterable<T>::value, fn_args_holder<adaptor, T>> operator()(T delimiter) const {\n        return { std::move(delimiter) };\n    }\n\n#endif\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<char, 5> arr = { 'h', 'e', 'l', 'l', 'o' };\n     * auto splitted = arr | lz::t_split<std::vector<char>>(\"ll\"); // {{'h', 'e'}, {'o'}}\n     * ```\n     * @param delimiter The delimiter to split on, for c-strings.\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class CharT>\n    LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, copied_basic_sv<CharT>> operator()(const CharT* delimiter) const {\n        return (*this)(lz::string_view(delimiter));\n    }\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t\n     * and the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<char, 5> arr = { 'h', 'e', 'l', 'l', 'o' };\n     * auto splitted = arr | lz::t_split<std::vector<char>>(lz::string_view(\"ll\")); // {{'h', 'e'}, {'o'}}\n     * ```\n     * @param delimiter The string_view delimiter, it's copied and therefore does not have to be a reference.\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class CharT>\n    LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, copied_basic_sv<CharT>>\n    operator()(const basic_string_view<CharT> delimiter) const {\n        return (*this)(copied_basic_sv<CharT>(delimiter));\n    }\n};\n\ntemplate<>\nstruct split_adaptor<void> {\n    using adaptor = split_adaptor<void>;\n\n    template<class Iterable, class Iterble2>\n    using splitter_iterable =\n        split_iterable<basic_iterable<iter_t<Iterable>, sentinel_t<Iterable>>, remove_ref_t<Iterable>, remove_ref_t<Iterble2>>;\n\n    template<class Iterable, class T>\n    using splitter_t_iterable = split_iterable<basic_iterable<iter_t<Iterable>, sentinel_t<Iterable>>, remove_ref_t<Iterable>, T>;\n\n#ifdef LZ_HAS_CXX_17\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * auto splitted = lz::split(arr, 2); // { { 1 }, { 3, 4, 5 } } where value_type = lz::basic_iterable<decltype(arr.begin)>\n     *\n     * std::vector<int> to_split = { 3, 4 }; // must be by reference\n     * auto splitted2 = lz::split(arr, to_split); // {{1, 2}, {5}} where value_type = lz::basic_iterable<decltype(arr.begin)>\n     * // to_split must be a reference in this case\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The delimiter to split on. Must be a reference if it is an iterable (string_view\n     * or c-strings excluded)\n     * @return A split_iterable that splits the iterable on the delimiter.\n\n     */\n    template<class Iterable, class T>\n    LZ_NODISCARD constexpr auto operator()(Iterable&& iterable, T&& delimiter) const {\n        using T2 = std::remove_cv_t<remove_ref_t<T>>;\n\n        if constexpr (is_iterable_v<T2>) {\n            using a = split_adaptor<basic_iterable<iter_t<Iterable>, sentinel_t<Iterable>>>;\n            return a{}(std::forward<Iterable>(iterable), std::forward<T>(delimiter));\n        }\n        else {\n            using splitter = splitter_t_iterable<Iterable, T2>;\n            return splitter{ std::forward<Iterable>(iterable), std::forward<T>(delimiter) };\n        }\n    }\n\n#else\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * auto splitted = lz::split(arr, 2); // { { 1 }, { 3, 4, 5 } } where value_type = lz::basic_iterable<decltype(arr.begin)>\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The delimiter to split on. Doesn't have to be a reference\n     * @return A split_iterable that splits the iterable on the delimiter.\n     */\n    template<class Iterable, class T>\n    LZ_NODISCARD constexpr enable_if_t<!is_iterable<T>::value, splitter_t_iterable<Iterable, T>>\n    operator()(Iterable&& iterable, T delimiter) const {\n        return { std::forward<Iterable>(iterable), std::move(delimiter) };\n    }\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns an iterable of `lz::basic_iterable` as iterator is dereferenced.\n     * Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * std::vector<int> to_split = { 3, 4 }; // must be by reference\n     * auto splitted = lz::split(arr, to_split); // {{1, 2}, {5}} where value_type = lz::basic_iterable<decltype(arr.begin)>\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The delimiter to split on. Must be a reference if it is an iterable (string_view\n     * or c-strings excluded)\n     * @return A split_iterable that splits the iterable on the delimiter.\n     */\n    template<class Iterable, class Iterable2>\n    LZ_NODISCARD constexpr enable_if_t<is_iterable<Iterable2>::value, splitter_iterable<Iterable, Iterable2>>\n    operator()(Iterable&& iterable, Iterable2&& delimiter) const {\n        using a = split_adaptor<basic_iterable<iter_t<Iterable>, sentinel_t<Iterable>>>;\n        return a{}(std::forward<Iterable>(iterable), std::forward<Iterable2>(delimiter));\n    }\n\n#endif\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns an iterable of `lz::basic_iterable` as iterator is dereferenced.\n     * Example:\n     * ```cpp\n     * std::array<char, 5> arr = { 'h', 'e', 'l', 'l', 'o' };\n     * auto splitted = lz::split(arr, \"ll\"); // {{'h', 'e'}, {'o'}} where value_type = lz::basic_iterable<decltype(arr.begin)>\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The delimiter to split on, for c-strings.\n     * @return A split_iterable that splits the iterable on the delimiter.\n     */\n    template<class Iterable, class CharT>\n    LZ_NODISCARD constexpr splitter_iterable<Iterable, copied_basic_sv<CharT>>\n    operator()(Iterable&& iterable, const CharT* delimiter) const {\n        return (*this)(std::forward<Iterable>(iterable), lz::string_view(delimiter));\n    }\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns an iterable of `lz::basic_iterable` as iterator is dereferenced.\n     * Example:\n     * ```cpp\n     * std::array<char, 5> arr = { 'h', 'e', 'l', 'l', 'o' };\n     * auto splitted = lz::split(arr, lz::string_view(\"ll\")); // {{'h', 'e'}, {'o'}}\n     * // where value_type = lz::basic_iterable<decltype(arr.begin)>\n     * ```\n     * @param iterable The iterable to split. Must be an actual reference.\n     * @param delimiter The string_view delimiter, it's copied and therefore does not have to be a reference.\n     * @return A split_iterable that splits the iterable on the delimiter.\n     */\n    template<class Iterable, class CharT>\n    LZ_NODISCARD constexpr splitter_iterable<Iterable, copied_basic_sv<CharT>>\n    operator()(Iterable&& iterable, const basic_string_view<CharT> delimiter) const {\n        return (*this)(std::forward<Iterable>(iterable), copied_basic_sv<CharT>(delimiter));\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns an iterable of `lz::basic_iterable` as iterator is dereferenced.\n     * Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * std::vector<int> to_split = { 3, 4 }; // must be by reference\n     * auto splitted = arr | lz::split(to_split); // {{1, 2}, {5}} where value_type = lz::basic_iterable<decltype(arr.begin)>\n     * auto splitted2 = arr | lz::split(2); // {{1}, {3, 4, 5}} where value_type = lz::basic_iterable<decltype(arr.begin)>\n     * // splitted2's delimiter does not have to be a reference\n     * ```\n     * @param delimiter The iterable to split. Must be an actual reference.\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class T>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 auto operator()(T&& delimiter) const {\n        if constexpr (is_iterable_v<T>) {\n            return fn_args_holder<adaptor, T>{ std::forward<T>(delimiter) };\n        }\n        else {\n            using T2 = std::remove_cv_t<remove_ref_t<T>>;\n            return fn_args_holder<adaptor, T2>{ std::forward<T>(delimiter) };\n        }\n    }\n\n#else\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns an iterable of `lz::basic_iterable` as iterator is dereferenced.\n     * Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * std::vector<int> to_split = { 3, 4 }; // must be by reference\n     * auto splitted = arr | lz::split(to_split); // {{1, 2}, {5}} where value_type = lz::basic_iterable<decltype(arr.begin)>\n     * ```\n     * @param delimiter The iterable to split.  Must be a reference if it is an iterable (string_view\n     * or c-strings excluded)\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_iterable<Iterable>::value, fn_args_holder<adaptor, Iterable>>\n    operator()(Iterable&& delimiter) const {\n        return { std::forward<Iterable>(delimiter) };\n    }\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns `ValueType` as iterator is dereferenced. Example:\n     * ```cpp\n     * std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n     * using iterable = lz::basic_iterable<decltype(arr.begin)>;\n     * auto splitted = arr | lz::t_split<iterable>(3); // {{1, 2}, {4, 5}} where value_type =\n     * // lz::basic_iterable<decltype(arr.begin)>\n     * // which is equal to arr | lz::split(3)\n     * ```\n     * @param delimiter The iterable to split.\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class T>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_iterable<T>::value, fn_args_holder<adaptor, T>>\n    operator()(T delimiter) const {\n        return { std::move(delimiter) };\n    }\n\n#endif\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns an iterable of `lz::basic_iterable` as iterator is dereferenced.\n     * Example:\n     * ```cpp\n     * std::array<char, 5> arr = { 'h', 'e', 'l', 'l', 'o' };\n     * auto splitted = arr | lz::split(\"ll\"); // {{'h', 'e'}, {'o'}} where value_type = lz::basic_iterable<decltype(arr.begin)>\n     * ```\n     * @param delimiter The delimiter to split on, for c-strings.\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class CharT>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, copied_basic_sv<CharT>> operator()(const CharT* delimiter) const {\n        return (*this)(lz::string_view(delimiter));\n    }\n\n    /**\n     * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n     * the iterable does not contain a .size() method. Returns an iterable of `lz::basic_iterable` as iterator is dereferenced.\n     * Example:\n     * ```cpp\n     * std::array<char, 5> arr = { 'h', 'e', 'l', 'l', 'o' };\n     * auto splitted = arr | lz::split(lz::basic_string_view(\"ll\")); // {{'h', 'e'}, {'o'}} where value_type =\n     * lz::basic_iterable<decltype(arr.begin)>\n     * ```\n     * @param delimiter The delimiter to split on. Doesn't have to be a reference.\n     * @return A split_iterable adaptor that can be used in pipe expressions\n     */\n    template<class CharT>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, copied_basic_sv<CharT>>\n    operator()(const basic_string_view<CharT> delimiter) const {\n        return (*this)(copied_basic_sv<CharT>(delimiter));\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/adaptors/take.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_ADAPTOR_HPP\n#define LZ_TAKE_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/take.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct take_adaptor {\n    using adaptor = take_adaptor;\n\n    /**\n     * @brief This adaptor is used to take the first n elements of an iterable or iterator. The iterator category is the same as\n     * the input iterator category. Has a .size() method that essentially returns\n     * `n` or min(n, size(input_iterable)).\n     *\n     * If the parameter @p iter is an iterable:\n     * - returns a default_sentinel_t if the input iterable is not bidirectional or has a sentinel\n     * - if `n` is larger than `lz::eager_size(input_iterable)` then the total iterable size is equal to\n     *   `lz::eager_size(input_iterable)`, thus preventing out of bounds. Traverses the entire iterable while taking the first `n`\n     *   for non random access iterables.\n     * - If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     *   traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether,\n     *   you can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     *   iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     *   entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     *   traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     *   but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     *   - `lz::chunks`\n     *   - `lz::enumerate`\n     *   - `lz::exclude`\n     *   - `lz::interleave`\n     *   - `lz::rotate`\n     *   - `lz::take`\n     *   - `lz::take_every`\n     *   - `lz::zip_longest`\n     *   - `lz::zip`\n     *\n     * If the parameter @p iter is an iterator:\n     * - if `n` is larger than the actual size if the iterator then this is undefined behaviour.\n     * - `lz::eager_size` is not used, so it is not guaranteed that the iterator will not go out of bounds.\n     * - never returns a sentinel\n     *\n     * Example:\n     * ```cpp\n     * auto vec = std::vector<int>{1, 2, 3, 4, 5};\n     * auto res = lz::take(vec, 2); // res = {1, 2}\n     * // or\n     * auto res = vec | lz::take(2); // res = {1, 2}\n     * // or\n     * auto res = lz::take(vec, 20); // res = {1, 2, 3, 4, 5}\n     * // or\n     * auto res = vec | lz::take(20); // res = {1, 2, 3, 4, 5}\n     * auto res = lz::take(vec.begin(), 2); // res = {1, 2}\n     * // auto res = lz::take(vec.begin(), 20); // undefined behaviour, as the iterator will go out of bounds\n     * // vec.begin() | lz::take(2); // Not supported, as piping only works for iterables, not for iterators\n     * ```\n     * @param iter The iterable / iterator to take the first n elements from.\n     * @param n The amount of elements to take.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr take_iterable<remove_ref_t<Iterable>> operator()(Iterable&& iter, const ptrdiff_t n) const {\n        return { std::forward<Iterable>(iter), n };\n    }\n\n    /**\n     * @brief This adaptor is used to take the first n elements of an iterable or iterator. The iterator category is the same as\n     * the input iterator category. Its end() function will return a sentinel, if the input iterable has a forward iterator. Has a\n     * .size() method that essentially returns `n`. If `n` is larger than `lz::eager_size(input_iterable)` then\n     * `lz::eager_size(input_iterable)` is used, thus preventing out of bounds.\n     *\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * auto vec = std::vector<int>{1, 2, 3, 4, 5};\n     * auto res = lz::take(vec, 2); // res = {1, 2}\n     * // or\n     * auto res = vec | lz::take(2); // res = {1, 2}\n     * // or\n     * auto res = lz::take(vec, 20); // res = {1, 2, 3, 4, 5}\n     * // or\n     * auto res = vec | lz::take(20); // res = {1, 2, 3, 4, 5}\n     * ```\n     * @param n The amount of elements to take. Piping only works for iterables, not for iterators such as\n     * `std::vector<int>::iterator`.\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, ptrdiff_t> operator()(const ptrdiff_t n) const {\n        return { n };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_TAKE_ADAPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/take_every.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_EVERY_ADAPTOR_HPP\n#define LZ_TAKE_EVERY_ADAPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/take_every.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct take_every_adaptor {\n    using adaptor = take_every_adaptor;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief Takes every `offset` element from the iterable, starting from `start`. Returns the same iterator category as the\n     * input iterable. Contains a size() method if the input iterable is sized. Will return a sentinel if the input iterable\n     * contains a sentinel or is forward.\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4 };\n     * auto take_every = lz::take_every(vec, 2); // 1, 3\n     * auto take_every = lz::take_every(vec, 2, 1); // 2, 4\n     * ```\n     * @param iterable The iterable to take every `offset` element from\n     * @param offset The offset to take every element from\n     * @param start The start offset\n     * @return A take_every_iterable that can be used to iterate over the taken elements.\n     */\n    template<class Iterable>\n    [[nodiscard]] constexpr take_every_iterable<remove_ref_t<Iterable>>\n    operator()(Iterable&& iterable, const diff_iterable_t<Iterable> offset, const diff_iterable_t<Iterable> start = 0) const\n        requires(lz::iterable<Iterable>)\n    {\n        return { std::forward<Iterable>(iterable), offset, start };\n    }\n\n#else\n\n    /**\n     * @brief Takes every `offset` element from the iterable, starting from `start`. Returns the same iterator category as the\n     * input iterable. Contains a size() method if the input iterable is sized. Will return a sentinel if the input iterable\n     * contains a sentinel or is forward.\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4 };\n     * auto take_every = lz::take_every(vec, 2); // 1, 3\n     * auto take_every = lz::take_every(vec, 2, 1); // 2, 4\n     * ```\n     * @param iterable The iterable to take every `offset` element from\n     * @param offset The offset to take every element from\n     * @param start The start offset\n     * @return A take_every_iterable that can be used to iterate over the taken elements.\n     */\n    template<class Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_iterable<Iterable>::value, take_every_iterable<remove_ref_t<Iterable>>>\n    operator()(Iterable&& iterable, const diff_iterable_t<Iterable> offset, const diff_iterable_t<Iterable> start = 0) const {\n        return { std::forward<Iterable>(iterable), offset, start };\n    }\n\n#endif\n\n    /**\n     * @brief Takes every `offset` element from the iterable, starting from `start`. Returns the same iterator category as the\n     * input iterable. Contains a size() method if the input iterable is sized.\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 2, 3, 4 };\n     * auto take_every = vec | lz::take_every(2); // 1, 3\n     * auto take_every = vec | lz::take_every(2, 1); // 2, 4\n     * ```\n     * @param offset The offset to take every element from\n     * @param start The start offset\n     * @return An adaptor that can be used in pipe expressions\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, ptrdiff_t, ptrdiff_t>\n    operator()(const ptrdiff_t offset, const ptrdiff_t start = 0) const {\n        return { offset, start };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_TAKE_EVERY_ADAPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/take_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_WHILE_ADAPTOR\n#define LZ_TAKE_WHILE_ADAPTOR\n\n#include <Lz/detail/iterables/take_while.hpp>\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct take_while_adaptor {\n    using adaptor = take_while_adaptor;\n\n    /**\n     * @brief Takes elements from an iterable while the given predicte returns `true`. Will return a bidirectional iterable if the\n     * given iterable is at least bidirectional and doesn't have a sentinel. Does not contain a size() method and if its input\n     * iterable is forward, will return a default_sentinel_t. Example:\n     * ```cpp\n     * std::vector<int> vec = {1, 2, 3, 4, 5};\n     * auto take_while = lz::take_while(vec, [](int i) { return i < 3; }); // {1, 2}\n     * ```\n     * @param iterable The iterable to take elements from.\n     * @param unary_predicate The predicate that indicates while to take elements.\n     * @return A take_while_iterable that will yield elements from the input iterable while the predicate returns true.\n     */\n    template<class Iterable, class UnaryPredicate>\n    LZ_NODISCARD constexpr take_while_iterable<remove_ref_t<Iterable>, UnaryPredicate>\n    operator()(Iterable&& iterable, UnaryPredicate unary_predicate) const {\n        return { std::forward<Iterable>(iterable), std::move(unary_predicate) };\n    }\n\n    /**\n     * @brief Takes elements from an iterable while the given predicte returns `true`. Will return a bidirectional iterable if the\n     * given iterable is at least bidirectional and doesn't have a sentinel. Does not contain a size() method and if its input\n     * iterable is forward, will return a default_sentinel_t. Example:\n     * ```cpp\n     * std::vector<int> vec = {1, 2, 3, 4, 5};\n     * auto take_while = vec | lz::take_while([](int i) { return i < 3; }); // {1, 2}\n     * ```\n     * @param unary_predicate The predicate that indicates while to take elements.\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class UnaryPredicate>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, UnaryPredicate> operator()(UnaryPredicate unary_predicate) const {\n        return { std::move(unary_predicate) };\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_TAKE_WHILE_ADAPTOR\n"
  },
  {
    "path": "include/Lz/detail/adaptors/unique.hpp",
    "content": "#pragma once\n\n#ifndef LZ_UNIQUE_ADPTOR_HPP\n#define LZ_UNIQUE_ADPTOR_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/iterables/unique.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct unique_adaptor {\n    using adaptor = unique_adaptor;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    /**\n     * @brief Makes the input iterable unique. Every element therefore only occurs once. The input iterable must be sorted\n     * beforehand. This iterator will 'decay' into a bidirectional one if the input iterator is bidirectional or higher. If the\n     * input iterable is not bidirectional or higher, then the output iterator will be forward, and will also return a sentinel\n     * (or if the input iterable has a sentinel), rather than an iterator. It will also return a sentinel if the input iterable\n     * contains a sentinel. This method does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 1, 2, 3, 3, 3, 4, 5, 5 };\n     * std::sort(vec.begin(), vec.end());\n     * auto unique = lz::unique(vec);\n     * auto unique = lz::unique(vec, std::less<>{});\n     * ```\n     * @param iterable The iterable to make unique. Must be sorted beforehand.\n     * @param predicate The predicate to compare the elements with. The default is std::less<>{}\n     * @return An iterable that contains only unique elements from the input iterable.\n     */\n    template<class Iterable, class BinaryPredicate = LZ_BIN_OP(less, void)>\n    [[nodiscard]] constexpr unique_iterable<remove_ref_t<Iterable>, BinaryPredicate>\n    operator()(Iterable&& iterable, BinaryPredicate predicate = {}) const\n        requires(lz::iterable<Iterable>)\n    {\n        return { std::forward<Iterable>(iterable), std::move(predicate) };\n    }\n\n    /**\n     * @brief Makes the input iterable unique. Every element therefore only occurs once. The input iterable must be sorted\n     * beforehand. This iterator will 'decay' into a bidirectional one if the input iterator is bidirectional or higher. If the\n     * input iterable is not bidirectional or higher, then the output iterator will be forward, and will also return a sentinel\n     * (or if the input iterable has a sentinel), rather than an iterator. It will also return a sentinel if the input iterable\n     * contains a sentinel.This method does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 1, 2, 3, 3, 3, 4, 5, 5 };\n     * std::sort(vec.begin(), vec.end());\n     * auto unique = vec | lz::unique;\n     * // custom comparer can be passed as argument as well:\n     * auto unique = vec | lz::unique(std::less<>{});\n     * ```\n     * @param predicate The predicate to compare the elements with. The default is std::less<>{}\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class BinaryPredicate = LZ_BIN_OP(less, void)>\n    [[nodiscard]] constexpr fn_args_holder<adaptor, BinaryPredicate> operator()(BinaryPredicate predicate = {}) const\n        requires(!iterable<BinaryPredicate>)\n    {\n        return { std::move(predicate) };\n    }\n\n#else\n\n    /**\n     * @brief Makes the input iterable unique. Every element therefore only occurs once. The input iterable must be sorted\n     * beforehand. This iterator will 'decay' into a bidirectional one if the input iterator is bidirectional or higher. If the\n     * input iterable is not bidirectional or higher, then the output iterator will be forward, and will also return a sentinel\n     * (or if the input iterable has a sentinel), rather than an iterator. It will also return a sentinel if the input iterable\n     * contains a sentinel. This method does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 1, 2, 3, 3, 3, 4, 5, 5 };\n     * std::sort(vec.begin(), vec.end());\n     * auto unique = lz::unique(vec);\n     * auto unique = lz::unique(vec, std::less<>{});\n     * ```\n     * @param iterable The iterable to make unique. Must be sorted beforehand.\n     * @param predicate The predicate to compare the elements with. The default is std::less<>{}\n     * @return An iterable that contains only unique elements from the input iterable.\n     */\n    template<class Iterable, class BinaryPredicate = LZ_BIN_OP(less, val_iterable_t<Iterable>)>\n    LZ_NODISCARD constexpr enable_if_t<is_iterable<Iterable>::value, unique_iterable<remove_ref_t<Iterable>, BinaryPredicate>>\n    operator()(Iterable&& iterable, BinaryPredicate predicate = {}) const {\n        return { std::forward<Iterable>(iterable), std::move(predicate) };\n    }\n\n    /**\n     * @brief Makes the input iterable unique. Every element therefore only occurs once. The input iterable must be sorted\n     * beforehand. This iterator will 'decay' into a bidirectional one if the input iterator is bidirectional or higher. If the\n     * input iterable is not bidirectional or higher, then the output iterator will be forward, and will also return a sentinel\n     * (or if the input iterable has a sentinel), rather than an iterator. It will also return a sentinel if the input iterable\n     * contains a sentinel.This method does not contain a .size() method. Example:\n     * ```cpp\n     * std::vector<int> vec = { 1, 1, 2, 3, 3, 3, 4, 5, 5 };\n     * std::sort(vec.begin(), vec.end());\n     * auto unique = vec | lz::unique;\n     * // custom comparer can be passed as argument as well:\n     * auto unique = vec | lz::unique(std::less<>{});\n     * ```\n     * @param predicate The predicate to compare the elements with. The default is std::less<>{}\n     * @return An adaptor that can be used in pipe expressions\n     */\n    template<class BinaryPredicate>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_iterable<BinaryPredicate>::value, fn_args_holder<adaptor, BinaryPredicate>>\n    operator()(BinaryPredicate predicate) const {\n        return { std::move(predicate) };\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#ifdef LZ_HAS_CONCEPTS\n\nLZ_MODULE_EXPORT template<class Iterable, class Adaptor>\n    requires(lz::iterable<Iterable>)\n[[nodiscard]] constexpr auto operator|(Iterable&& iterable, lz::detail::unique_adaptor) {\n    return lz::detail::unique_adaptor{}(std::forward<Iterable>(iterable), lz::detail::val_iterable_t<Iterable>{},\n                                        LZ_BIN_OP(less, val_iterable_t<Iterable>){});\n}\n\n#else\n\nLZ_MODULE_EXPORT template<class Iterable, class Adaptor>\nLZ_NODISCARD constexpr auto operator|(Iterable&& iterable, lz::detail::unique_adaptor)\n    -> lz::detail::enable_if_t<lz::detail::is_iterable<Iterable>::value,\n                               decltype(lz::detail::unique_adaptor{}(std::forward<Iterable>(iterable),\n                                                                     lz::detail::val_iterable_t<Iterable>{},\n                                                                     LZ_BIN_OP(less, lz::detail::val_iterable_t<Iterable>){}))> {\n    return lz::detail::unique_adaptor{}(std::forward<Iterable>(iterable), lz::detail::val_iterable_t<Iterable>{},\n                                        LZ_BIN_OP(less, lz::detail::val_iterable_t<Iterable>){});\n}\n\n#endif\n\n#endif // LZ_UNIQUE_ADPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/zip.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ZIP_ADAPTOR_HPP\n#define LZ_ZIP_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/zip.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct zip_adaptor {\n    using adaptor = zip_adaptor;\n\n    /**\n     * @brief Zips two or more iterables together. If the sizes of the iterables are different, the shortest one will be used. It\n     * contains a size() method if all the iterables have a size() method. It is the same iterator category as the 'weakest' of\n     * the input iterables. If the weakest is a forward iterator or a sentinel, then the end() function will also return a\n     * sentinel.\n     *\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again,\n     * but will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::vector<int> a = { 1, 2, 3 };\n     * std::vector<int> b = { 4, 5, 6 };\n     * std::vector<int> c = { 7, 8, 9 };\n     * auto zipped = lz::zip(a, b, c); // { (1, 4, 7), (2, 5, 8), (3, 6, 9) }\n     *\n     * // or\n     *\n     * auto zipped = a | lz::zip(b, c); // { (1, 4, 7), (2, 5, 8), (3, 6, 9) }\n     *\n     * std::vector<int> d = { 10 };\n     * auto zipped2 = lz::zip(a, d); // { (1, 10) }\n     * ```\n     * @param iterables The iterables to zip together\n     * @return A zip_iterable containing the zipped iterables\n     */\n    template<class... Iterables>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 zip_iterable<remove_ref_t<Iterables>...> operator()(Iterables&&... iterables) const {\n        return { std::forward<Iterables>(iterables)... };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_ZIP_ADAPTOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/adaptors/zip_longest.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ZIP_LONGEST_ADAPTOR_HPP\n#define LZ_ZIP_LONGEST_ADAPTOR_HPP\n\n#include <Lz/detail/iterables/zip_longest.hpp>\n\nnamespace lz {\nnamespace detail {\nstruct zip_longest_adaptor {\n    using adaptor = zip_longest_adaptor;\n\n    /**\n     * @brief Zips two or more iterables together. Returns a sentinel if one of the iterables has a sentinel or is forward. Its\n     * value_type, a tuple, contains an optional of `std::reference_wrapper`s to the elements itself. If one of the iterables is\n     * shorter than the others, it will return an empty optional instead of a non empty optional. Contains a size() method if all\n     * the iterables are sized. Will return the size of the largest iterable. Its iterator category is the same as its 'weakest'\n     * input iterables.\n     *\n     * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\n     * traversed to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you\n     * can use `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the\n     * iterable. `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its\n     * entire size is therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will\n     * traverse the iterable once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but\n     * will return the cached size instead. The following iterables require a(n) (eagerly)sized iterable:\n     * - `lz::chunks`\n     * - `lz::enumerate`\n     * - `lz::exclude`\n     * - `lz::interleave`\n     * - `lz::rotate`\n     * - `lz::take`\n     * - `lz::take_every`\n     * - `lz::zip_longest`\n     * - `lz::zip`\n     *\n     * Example:\n     * ```cpp\n     * std::vector<int> a = { 1, 2, 3 };\n     * std::vector<int> b = { 4, 5 };\n     * auto zipped = a | lz::zip_longest(b); // {(1, 4), (2, 5), (3, lz::nullopt)}\n     *\n     * auto filter = lz::filter(a, [](int i) { return i % 2 == 0; });\n     * auto zipped = lz::zip_longest(a, filter); // {(1, lz::nullopt), (2, 2), (3, lz::nullopt)}\n     * // zipped will be bidirectional. However, the entire sequence of `filter` will be traversed in order to get the size of the\n     * // iterable. For `a` however, `a.size()` is used.\n     * ```\n     * @param iterables The iterables to zip together\n     * @return A zip_longest_iterable containing the zipped iterables\n     **/\n    template<class... Iterables>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 zip_longest_iterable<remove_ref_t<Iterables>...> operator()(Iterables&&... iterables) const {\n        return { std::forward<Iterables>(iterables)... };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/accumulate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_ACCUMULATE_HPP\n#define LZ_DETAIL_ALGORITHM_ACCUMULATE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n\n#ifdef LZ_HAS_CXX_20\n#include <Lz/detail/procs/get_end.hpp>\n#include <numeric>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_20\n\ntemplate<class Iterator, class S, class T, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 T accumulate(Iterator begin, S end, T init, UnaryPredicate unary_predicate) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        return std::accumulate(begin, detail::get_end(begin, end), std::move(init), std::move(unary_predicate));\n    }\n    else {\n        for (; begin != end; ++begin) {\n            init = unary_predicate(std::move(init), *begin);\n        }\n        return init;\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class T, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 T accumulate(Iterator begin, S end, T init, UnaryPredicate unary_predicate) {\n    for (; begin != end; ++begin) {\n        init = unary_predicate(std::move(init), *begin);\n    }\n    return init;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_DETAIL_ALGORITHM_ACCUMULATE_HPP\n"
  },
  {
    "path": "include/Lz/detail/algorithm/adjacent_find.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_ADJACENT_FIND_HPP\n#define LZ_DETAIL_ALGORITHM_ADJACENT_FIND_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/get_end.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\n#include <algorithm>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class BinaryPredicate>\nconstexpr Iterator adjacent_find(Iterator begin, S end, BinaryPredicate binary_predicate) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        return std::adjacent_find(begin, detail::get_end(begin, end), std::move(binary_predicate));\n    }\n    else {\n        if (begin == end) {\n            return begin;\n        }\n        auto next = begin;\n        for (++next; next != end; ++begin, ++next) {\n            if (binary_predicate(*begin, *next)) {\n                return begin;\n            }\n        }\n        return next;\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<Iterator, S>::value, Iterator>\nadjacent_find(Iterator begin, S end, BinaryPredicate binary_predicate) {\n    if (begin == end) {\n        return begin;\n    }\n\n    auto next = begin;\n    for (++next; next != end; ++begin, ++next) {\n        if (binary_predicate(*begin, *next)) {\n            return begin;\n        }\n    }\n    return next;\n}\n\ntemplate<class Iterator, class S, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat<Iterator, S>::value, Iterator>\nadjacent_find(Iterator begin, S end, BinaryPredicate binary_predicate) {\n    return std::adjacent_find(begin, detail::get_end(begin, end), std::move(binary_predicate));\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/back.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_BACK_HPP\n#define LZ_DETAIL_ALGORITHM_BACK_HPP\n\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/procs/get_end.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S>\nconstexpr ref_t<Iterator> back(Iterator begin, S end) {\n    LZ_ASSERT(begin != end, \"Cannot get back of empty iterable\");\n\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        auto last = detail::get_end(begin, end);\n        return *--last;\n    }\n    else {\n        auto prev = begin++;\n\n        while (begin != end) {\n            prev = begin;\n            ++begin;\n        }\n\n        return *prev;\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat<Iterator, S>::value, ref_t<Iterator>> back(Iterator begin, S end) {\n    LZ_ASSERT(begin != end, \"Cannot get back of empty iterable\");\n    auto last = detail::get_end(begin, end);\n    return *--last;\n}\n\ntemplate<class Iterator, class S>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<Iterator, S>::value, ref_t<Iterator>> back(Iterator begin, S end) {\n    LZ_ASSERT(begin != end, \"Cannot get back of empty iterable\");\n\n    auto prev = begin++;\n    while (begin != end) {\n        prev = begin;\n        ++begin;\n    }\n    return *prev;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/copy.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_COPY_HPP\n#define LZ_DETAIL_ALGORITHM_COPY_HPP\n\n#include <Lz/detail/procs/get_end.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class OutputIterator>\nconstexpr void copy(Iterator begin, S end, OutputIterator out) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        static_cast<void>(std::copy(begin, detail::get_end(begin, end), out));\n    }\n    else {\n        for (; begin != end; ++begin, ++out) {\n            *out = *begin;\n        }\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class OutputIterator>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<Iterator, S>::value> copy(Iterator begin, S end, OutputIterator out) {\n    for (; begin != end; ++begin, ++out) {\n        *out = *begin;\n    }\n}\n\ntemplate<class Iterator, class S, class OutputIterator>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat<Iterator, S>::value> copy(Iterator begin, S end, OutputIterator out) {\n    static_cast<void>(std::copy(begin, detail::get_end(begin, end), out));\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/count.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_COUNT_HPP\n#define LZ_DETAIL_ALGORITHM_COUNT_HPP\n\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n#include <algorithm>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class I, class S, class T>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<I, S>::value, diff_type<I>> count(I begin, S end, const T& value) {\n    diff_type<I> count = 0;\n    for (; begin != end; ++begin) {\n        if (*begin == value) {\n            ++count;\n        }\n    }\n    return count;\n}\n\ntemplate<class I, class S, class T>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat<I, S>::value, diff_type<I>> count(I begin, S end, const T& value) {\n    return std::count(begin, detail::get_end(begin, end), std::move(value));\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/count_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_COUNT_IF_HPP\n#define LZ_DETAIL_ALGORITHM_COUNT_IF_HPP\n\n#include <Lz/detail/procs/get_end.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <algorithm>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nconstexpr diff_type<Iterator> count_if(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        return std::count_if(begin, detail::get_end(begin, end), std::move(unary_predicate));\n    }\n    else {\n        diff_type<Iterator> count = 0;\n        for (; begin != end; ++begin) {\n            if (unary_predicate(*begin)) {\n                ++count;\n            }\n        }\n        return count;\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat<Iterator, S>::value, diff_type<Iterator>>\ncount_if(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    return std::count_if(begin, detail::get_end(begin, end), std::move(unary_predicate));\n}\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<Iterator, S>::value, diff_type<Iterator>>\ncount_if(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    diff_type<Iterator> count = 0;\n    for (; begin != end; ++begin) {\n        if (unary_predicate(*begin)) {\n            ++count;\n        }\n    }\n    return count;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n#endif // LZ_DETAIL_ALGORITHM_COUNT_IF_HPP\n"
  },
  {
    "path": "include/Lz/detail/algorithm/ends_with.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_ENDS_WITH_HPP\n#define LZ_DETAIL_ALGORITHM_ENDS_WITH_HPP\n\n#include <Lz/algorithm/starts_with.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/algorithm/equal.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_17 bool ends_with(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    std::reverse_iterator<S1> rbegin{ end };\n    std::reverse_iterator<Iterator1> rend{ begin };\n    return starts_with(std::move(rbegin), std::move(rend), std::reverse_iterator<Iterator2>(std::move(end2)),\n                       std::reverse_iterator<S2>(std::move(begin2)), std::move(binary_predicate));\n}\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 bool ends_with(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate,\n                                   size_t size_of_iterable1, size_t size_of_iterable2) {\n    if (size_of_iterable1 < size_of_iterable2) {\n        return false;\n    }\n\n    const auto distance = static_cast<diff_type<Iterator1>>(size_of_iterable1 - size_of_iterable2);\n    begin = std::next(begin, distance);\n    using std::equal;\n    using lz::equal;\n    return equal(begin, end, begin2, end2, std::move(binary_predicate));\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_DETAIL_ALGORITHM_ENDS_WITH_HPP\n"
  },
  {
    "path": "include/Lz/detail/algorithm/equal.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_EQUAL_HPP\n#define LZ_DETAIL_ALGORITHM_EQUAL_HPP\n\n#include <Lz/detail/procs/get_end.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nconstexpr bool equal(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    if constexpr (std_algo_compat2_v<Iterator1, S1, Iterator2, S2>) {\n        auto last1 = detail::get_end(begin, end);\n        auto last2 = detail::get_end(begin2, end2);\n        return std_equal_helper(begin, last1, begin2, last2, std::move(binary_predicate));\n    }\n    else {\n        for (; begin2 != end2; ++begin, (void)++begin2) {\n            if (begin == end || !binary_predicate(*begin, *begin2)) {\n                return false;\n            }\n        }\n        return true;\n    }\n}\n\n#elif defined(LZ_HAS_CXX_14)\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat2<Iterator1, S1, Iterator2, S2>::value, bool>\nequal(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    auto last1 = detail::get_end(begin, end);\n    auto last2 = detail::get_end(begin2, end2);\n    return std_equal_helper(begin, last1, begin2, last2, std::move(binary_predicate));\n}\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat2<Iterator1, S1, Iterator2, S2>::value, bool>\nequal(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    for (; begin != end && begin2 != end2; ++begin, (void)++begin2) {\n        if (!binary_predicate(*begin, *begin2)) {\n            return false;\n        }\n    }\n    return begin == end && begin2 == end2;\n}\n\n#else\n// cxx 11\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_ra<Iterator1>::value || !is_ra<Iterator2>::value, bool>\nequal(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    for (; begin != end && begin2 != end2; ++begin, (void)++begin2) {\n        if (!binary_predicate(*begin, *begin2)) {\n            return false;\n        }\n    }\n    return begin == end && begin2 == end2;\n}\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_ra<Iterator1>::value && is_ra<Iterator2>::value, bool>\nequal(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    if ((end2 - begin2) != (end - begin)) {\n        return false;\n    }\n    return std_equal_helper(begin, detail::get_end(begin, end), begin2, detail::get_end(begin2, end2),\n                            std::move(binary_predicate));\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/find_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_FIND_IF_HPP\n#define LZ_DETAIL_ALGORITHM_FIND_IF_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/get_end.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\n#include <algorithm>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 Iterator find_if(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        return std::find_if(begin, detail::get_end(begin, end), std::move(unary_predicate));\n    }\n    else {\n        for (; begin != end; ++begin) {\n            if (unary_predicate(*begin)) {\n                break;\n            }\n        }\n        return begin;\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat<Iterator, S>::value, Iterator>\nfind_if(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    return std::find_if(begin, detail::get_end(begin, end), std::move(unary_predicate));\n}\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<Iterator, S>::value, Iterator>\nfind_if(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    for (; begin != end; ++begin) {\n        if (unary_predicate(*begin)) {\n            break;\n        }\n    }\n    return begin;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/find_last_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_FIND_LAST_IF_HPP\n#define LZ_DETAIL_ALGORITHM_FIND_LAST_IF_HPP\n\n#include <Lz/detail/iterables/reverse.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/algorithm/find_if.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterator, class S>\nLZ_CONSTEXPR_CXX_14 Iterator unwrap_reverse_iterator(std::reverse_iterator<Iterator> it, Iterator begin, S end) {\n    auto base = it.base();\n    if (base == begin) {\n        return end;\n    }\n    return --base;\n}\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterable, class UnaryPredicate>\nconstexpr iter_t<Iterable> find_last_if(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    if constexpr (is_bidi_v<iter_t<remove_ref_t<Iterable>>>) {\n        auto pos = lz::find_if(reverse_iterable<remove_ref_t<Iterable>, false>{ iterable }, std::move(unary_predicate));\n        return unwrap_reverse_iterator(std::move(pos), detail::begin(iterable), detail::end(iterable));\n    }\n    else {\n        auto current = detail::begin(iterable);\n        auto it = current;\n        auto end = detail::end(iterable);\n        for (; it != end; ++it) {\n            if (unary_predicate(*it)) {\n                current = it;\n            }\n        }\n        // Check if current was never updated\n        if (it == end && current == detail::begin(iterable) && !unary_predicate(*current)) {\n            return it;\n        }\n        return current;\n    }\n}\n\n#else\n\ntemplate<class Iterable, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<is_bidi<iter_t<Iterable>>::value, iter_t<Iterable>>\nfind_last_if(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    auto pos = lz::find_if(reverse_iterable<remove_ref_t<Iterable>, false>{ iterable }, std::move(unary_predicate));\n    return unwrap_reverse_iterator(std::move(pos), detail::begin(iterable), detail::end(iterable));\n}\n\ntemplate<class Iterable, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!is_bidi<iter_t<Iterable>>::value, iter_t<Iterable>>\nfind_last_if(Iterable&& iterable, UnaryPredicate unary_predicate) {\n    auto current = detail::begin(iterable);\n    auto it = current;\n    auto end = detail::end(iterable);\n    for (; it != end; ++it) {\n        if (unary_predicate(*it)) {\n            current = it;\n        }\n    }\n    // Check if current was never updated\n    if (it == end && current == detail::begin(iterable) && !unary_predicate(*current)) {\n        return it;\n    }\n    return current;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_DETAIL_ALGORITHM_FIND_LAST_IF_HPP\n"
  },
  {
    "path": "include/Lz/detail/algorithm/for_each.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_FOR_EACH_HPP\n#define LZ_DETAIL_ALGORITHM_FOR_EACH_HPP\n\n#include <Lz/detail/procs/get_end.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n#include <algorithm>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class Func>\nconstexpr void for_each(Iterator begin, S end, Func func) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        static_cast<void>(std::for_each(begin, detail::get_end(begin, end), std::move(func)));\n    }\n    else {\n        for (; begin != end; ++begin) {\n            func(*begin);\n        }\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class Func>\nenable_if_t<std_algo_compat<Iterator, S>::value> for_each(Iterator begin, S end, Func func) {\n    static_cast<void>(std::for_each(begin, detail::get_end(begin, end), std::move(func)));\n}\n\ntemplate<class Iterator, class S, class Func>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<Iterator, S>::value> for_each(Iterator begin, S end, Func func) {\n    for (; begin != end; ++begin) {\n        func(*begin);\n    }\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_DETAIL_ALGORITHM_FOR_EACH_HPP\n"
  },
  {
    "path": "include/Lz/detail/algorithm/for_each_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_FOR_EACH_WHILE_HPP\n#define LZ_DETAIL_ALGORITHM_FOR_EACH_WHILE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 void for_each_while(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    for (; begin != end; ++begin) {\n        if (!unary_predicate(*begin)) {\n            break;\n        }\n    }\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/for_each_while_n.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_FOR_EACH_WHILE_N_HPP\n#define LZ_DETAIL_ALGORITHM_FOR_EACH_WHILE_N_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/procs/eager_size.hpp>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterable, class UnaryOp>\nconstexpr void for_each_while_n(Iterable&& iterable, size_t n, UnaryOp unary_op) {\n    auto it = detail::begin(iterable);\n\n    if constexpr (is_ra_v<iter_t<Iterable>>) {\n        using diff_t = diff_iterable_t<Iterable>;\n        const auto diff = static_cast<diff_t>(detail::min_variadic2(n, static_cast<size_t>(lz::eager_size(iterable))));\n        const auto last = detail::begin(iterable) + diff;\n\n        for (; it != last; ++it) {\n            if (!unary_op(*it)) {\n                break;\n            }\n        }\n    }\n    else if constexpr (is_sized_v<Iterable>) {\n        auto count = detail::min_variadic2(n, static_cast<size_t>(lz::eager_size(iterable)));\n        for (; count-- != 0; ++it) {\n            if (!unary_op(*it)) {\n                break;\n            }\n        }\n    }\n    else {\n        auto begin = detail::begin(iterable);\n        const auto end = detail::end(iterable);\n\n        for (size_t count = 0; begin != end && count != n; ++begin, ++count) {\n            if (!unary_op(*begin)) {\n                break;\n            }\n        }\n    }\n}\n\n#else\n\ntemplate<class Iterable, class UnaryOp>\nLZ_CONSTEXPR_CXX_14 enable_if_t<is_ra<iter_t<Iterable>>::value>\nfor_each_while_n(Iterable&& iterable, size_t n, UnaryOp unary_op) {\n    using diff_t = diff_iterable_t<Iterable>;\n\n    const auto diff = static_cast<diff_t>(detail::min_variadic2(n, static_cast<size_t>(lz::eager_size(iterable))));\n    const auto last = detail::begin(iterable) + diff;\n\n    for (auto it = detail::begin(iterable); it != last; ++it) {\n        if (!unary_op(*it)) {\n            break;\n        }\n    }\n}\n\ntemplate<class Iterable, class UnaryOp>\nLZ_CONSTEXPR_CXX_14 enable_if_t<is_sized<Iterable>::value && !is_ra<iter_t<Iterable>>::value>\nfor_each_while_n(Iterable&& iterable, size_t n, UnaryOp unary_op) {\n    auto count = detail::min_variadic2(n, lz::eager_size(iterable));\n\n    for (auto it = detail::begin(iterable); count-- != 0; ++it) {\n        if (!unary_op(*it)) {\n            break;\n        }\n    }\n}\ntemplate<class Iterable, class UnaryOp>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!is_sized<Iterable>::value && !is_ra<iter_t<Iterable>>::value>\nfor_each_while_n(Iterable&& iterable, size_t n, UnaryOp unary_op) {\n    auto begin = detail::begin(iterable);\n    const auto end = detail::end(iterable);\n\n    for (size_t count = 0; begin != end && count != n; ++begin, ++count) {\n        if (!unary_op(*begin)) {\n            break;\n        }\n    }\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/index_of_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_INDEX_OF_IF_HPP\n#define LZ_DETAIL_ALGORITHM_INDEX_OF_IF_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/algorithm/npos.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 size_t index_of_if(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    for (size_t index = 0; begin != end; ++begin, ++index) {\n        if (unary_predicate(*begin)) {\n            return index;\n        }\n    }\n    return npos;\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/is_sorted.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_IS_SORTED_HPP\n#define LZ_DETAIL_ALGORITHM_IS_SORTED_HPP\n\n#include <Lz/detail/traits/iterator_categories.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class BinaryPredicate>\nconstexpr bool is_sorted(Iterator begin, S end, BinaryPredicate binary_predicate) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        return std::is_sorted(begin, detail::get_end(begin, end), std::move(binary_predicate));\n    }\n    else {\n        if (begin == end) {\n            return true;\n        }\n        auto next = begin;\n        for (++next; next != end; ++begin, ++next) {\n            if (binary_predicate(*next, *begin)) {\n                return false;\n            }\n        }\n        return true;\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat<Iterator, S>::value, bool>\nis_sorted(Iterator begin, S end, BinaryPredicate binary_predicate) {\n    return std::is_sorted(begin, detail::get_end(begin, end), std::move(binary_predicate));\n}\n\ntemplate<class Iterator, class S, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<Iterator, S>::value, bool>\nis_sorted(Iterator begin, S end, BinaryPredicate binary_predicate) {\n    if (begin == end) {\n        return true;\n    }\n    auto next = begin;\n    for (++next; next != end; ++begin, ++next) {\n        if (binary_predicate(*next, *begin)) {\n            return false;\n        }\n    }\n    return true;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/lower_bound.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_LOWER_BOUND_HPP\n#define LZ_DETAIL_ALGORITHM_LOWER_BOUND_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/procs/eager_size.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterator, class T, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 Iterator sized_lower_bound(Iterator begin, const T& value, BinaryPredicate binary_predicate,\n                                               diff_type<Iterator> count) {\n    while (count > 0) {\n        const auto step = count / 2;\n        auto it = std::next(begin, step);\n        if (binary_predicate(*it, value)) {\n            begin = ++it;\n            count -= step + 1;\n        }\n        else {\n            count = step;\n        }\n    }\n    return begin;\n}\n\ntemplate<class Iterable, class T, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 iter_t<Iterable> lower_bound(Iterable&& iterable, const T& value, BinaryPredicate binary_predicate) {\n    using diff = diff_type<iter_t<Iterable>>;\n    return sized_lower_bound(detail::begin(iterable), value, std::move(binary_predicate),\n                             static_cast<diff>(lz::eager_size(iterable)));\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_DETAIL_ALGORITHM_LOWER_BOUND_HPP\n"
  },
  {
    "path": "include/Lz/detail/algorithm/max_element.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_MAX_ELEMENT_HPP\n#define LZ_DETAIL_ALGORITHM_MAX_ELEMENT_HPP\n\n#include <Lz/detail/procs/get_end.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <algorithm>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 Iterator max_element(Iterator begin, S end, BinaryPredicate binary_predicate) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        return std::max_element(begin, detail::get_end(begin, end), std::move(binary_predicate));\n    }\n    else {\n        if (begin == end) {\n            return begin;\n        }\n        auto max = begin;\n        for (++begin; begin != end; ++begin) {\n            if (binary_predicate(*max, *begin)) {\n                max = begin;\n            }\n        }\n        return max;\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat<Iterator, S>::value, Iterator>\nmax_element(Iterator begin, S end, BinaryPredicate binary_predicate) {\n    return std::max_element(begin, detail::get_end(begin, end), std::move(binary_predicate));\n}\n\ntemplate<class Iterator, class S, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<Iterator, S>::value, Iterator>\nmax_element(Iterator begin, S end, BinaryPredicate binary_predicate) {\n    if (begin == end) {\n        return begin;\n    }\n    auto max = begin;\n    for (++begin; begin != end; ++begin) {\n        if (binary_predicate(*max, *begin)) {\n            max = begin;\n        }\n    }\n    return max;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/mean.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_MEAN_HPP\n#define LZ_DETAIL_ALGORITHM_MEAN_HPP\n\n#include <Lz/detail/algorithm/accumulate.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\n#include <numeric>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class BinaryOp>\nconstexpr double mean(Iterator begin, S end, BinaryOp binary_op) {\n    if constexpr (is_ra_v<Iterator>) {\n        const auto len = end - begin;\n        return std::accumulate(begin, begin + len, val_t<Iterator>{}, std::move(binary_op)) / static_cast<double>(len);\n    }\n    else {\n        using T = remove_cvref_t<decltype(binary_op(*begin, *begin))>;\n\n        diff_type<Iterator> distance{ 0 };\n        T sum{};\n\n        for (; begin != end; ++begin, ++distance) {\n            sum = binary_op(std::move(sum), *begin);\n        }\n        if (distance == 0) {\n            return 0;\n        }\n\n        return static_cast<double>(sum) / static_cast<double>(distance);\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class BinaryOp>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!is_ra<Iterator>::value, double> mean(Iterator begin, S end, BinaryOp binary_op) {\n    using T = remove_cvref_t<decltype(binary_op(*begin, *begin))>;\n\n    diff_type<Iterator> distance{ 0 };\n    T sum{};\n\n    for (; begin != end; ++begin, ++distance) {\n        sum = binary_op(std::move(sum), *begin);\n    }\n    if (distance == 0) {\n        return 0;\n    }\n\n    return static_cast<double>(sum) / static_cast<double>(distance);\n}\n\ntemplate<class Iterator, class S, class BinaryOp>\nLZ_CONSTEXPR_CXX_14 enable_if_t<is_ra<Iterator>::value, double> mean(Iterator begin, S end, BinaryOp binary_op) {\n    const auto len = end - begin;\n    return std::accumulate(begin, begin + len, val_t<Iterator>{}, std::move(binary_op)) / static_cast<double>(len);\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n#endif // LZ_DETAIL_ALGORITHM_MEAN_HPP\n"
  },
  {
    "path": "include/Lz/detail/algorithm/partition.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_PARTITION_HPP\n#define LZ_DETAIL_ALGORITHM_PARTITION_HPP\n\n#include <Lz/algorithm/find_if_not.hpp>\n#include <Lz/detail/compiler_config.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nconstexpr Iterator partition(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        return std::partition(begin, detail::get_end(begin, end), std::move(unary_predicate));\n    }\n    else {\n        begin =\n            detail::find_if(begin, end, [p = std::move(unary_predicate)](detail::ref_t<Iterator> value) { return !p(value); });\n        if (begin == end) {\n            return begin;\n        }\n\n        for (auto i = std::next(begin); i != end; ++i) {\n            if (!unary_predicate(*i)) {\n                continue;\n            }\n            std::swap(*i, *begin);\n            ++begin;\n        }\n\n        return begin;\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat<Iterator, S>::value, Iterator>\npartition(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    return std::partition(begin, detail::get_end(begin, end), std::move(unary_predicate));\n}\n\ntemplate<class Iterator, class S, class UnaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat<Iterator, S>::value, Iterator>\npartition(Iterator begin, S end, UnaryPredicate unary_predicate) {\n    begin = detail::find_if(begin, end, [&unary_predicate](detail::ref_t<Iterator> value) { return !unary_predicate(value); });\n    if (begin == end) {\n        return begin;\n    }\n\n    for (auto i = std::next(begin); i != end; ++i) {\n        if (!unary_predicate(*i)) {\n            continue;\n        }\n        std::swap(*i, *begin);\n        ++begin;\n    }\n\n    return begin;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_DETAIL_ALGORITHM_PARTITION_HPP\n"
  },
  {
    "path": "include/Lz/detail/algorithm/peek.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_PEEK_HPP\n#define LZ_DETAIL_ALGORITHM_PEEK_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/optional.hpp>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator>\nconstexpr auto get_value_or_reference(Iterator begin) {\n    if constexpr (std::is_lvalue_reference_v<ref_t<Iterator>>) {\n        return optional<std::reference_wrapper<remove_ref_t<ref_t<Iterator>>>>{ *begin };\n    }\n    else {\n        return optional<val_t<Iterator>>{ *begin };\n    }\n}\n\n#else\n\ntemplate<class Iterator>\nLZ_CONSTEXPR_CXX_14\n    enable_if_t<std::is_lvalue_reference<ref_t<Iterator>>::value, optional<std::reference_wrapper<remove_ref_t<ref_t<Iterator>>>>>\n    get_value_or_reference(Iterator begin) {\n    return std::reference_wrapper<remove_ref_t<ref_t<Iterator>>>{ *begin };\n}\n\ntemplate<class Iterator>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std::is_lvalue_reference<ref_t<Iterator>>::value, optional<val_t<Iterator>>>\nget_value_or_reference(Iterator begin) {\n    return optional<val_t<Iterator>>{ *begin };\n}\n\n#endif\n\ntemplate<class Iterator, class S>\nLZ_CONSTEXPR_CXX_14 auto peek(Iterator begin, S end) -> decltype(get_value_or_reference(begin)) {\n    if (begin == end || ++begin == end) {\n        return nullopt;\n    }\n    return get_value_or_reference(begin);\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/search.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_SEARCH_HPP\n#define LZ_DETAIL_ALGORITHM_SEARCH_HPP\n\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <utility>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterator, class Iterator2, class BinaryPredicate>\nstd::pair<Iterator, Iterator>\nstd_search(Iterator begin, Iterator end, Iterator2 begin2, Iterator2 end2, BinaryPredicate binary_predicate) {\n    auto pos = std::search(begin, end, begin2, end2, std::move(binary_predicate));\n    return pos == end ? std::make_pair(pos, pos) : std::make_pair(pos, std::next(pos, std::distance(begin2, end2)));\n}\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class Iterator2, class S2, class BinaryPredicate>\nconstexpr std::pair<Iterator, Iterator>\nsearch(Iterator begin, S end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    if constexpr (std_algo_compat2_v<Iterator, S, Iterator2, S2>) {\n        auto end_common = detail::get_end(begin, end);\n        auto end2_common = detail::get_end(begin2, end2);\n        return std_search(begin, end_common, begin2, end2_common, std::move(binary_predicate));\n    }\n    else {\n        while (begin != end) {\n            auto it = begin;\n            auto it2 = begin2;\n            while (it2 != end2 && it != end && binary_predicate(*it, *it2)) {\n                ++it;\n                ++it2;\n            }\n            if (it2 == end2) {\n                return { begin, it };\n            }\n            ++begin;\n        }\n        return { begin, begin };\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class Iterator2, class S2, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!std_algo_compat2<Iterator, S, Iterator2, S2>::value, std::pair<Iterator, Iterator>>\nsearch(Iterator begin, S end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    while (begin != end) {\n        auto it = begin;\n        auto it2 = begin2;\n        while (it2 != end2 && it != end && binary_predicate(*it, *it2)) {\n            ++it;\n            ++it2;\n        }\n        if (it2 == end2) {\n            return { begin, it };\n        }\n        ++begin;\n    }\n    return { begin, begin };\n}\n\ntemplate<class Iterator, class S, class Iterator2, class S2, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<std_algo_compat2<Iterator, S, Iterator2, S2>::value, std::pair<Iterator, Iterator>>\nsearch(Iterator begin, S end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    return std_search(begin, detail::get_end(begin, end), begin2, detail::get_end(begin2, end2), std::move(binary_predicate));\n}\n\n#endif // LZ_HAS_CXX_17\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/starts_with.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_STARTS_WITH_HPP\n#define LZ_DETAIL_ALGORITHM_STARTS_WITH_HPP\n\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\n#include <algorithm>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nconstexpr bool starts_with(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    if constexpr (is_ra_v<Iterator2> && is_ra_v<Iterator1>) {\n        auto len = end2 - begin2;\n        if ((end - begin) < len) {\n            return false;\n        }\n        return std_equal_helper(begin2, end2, begin, begin + len, std::move(binary_predicate));\n    }\n    else {\n        for (; begin2 != end2; ++begin, (void)++begin2) {\n            if (begin == end || !binary_predicate(*begin, *begin2)) {\n                return false;\n            }\n        }\n        return true;\n    }\n}\n\n#elif defined(LZ_HAS_CXX_14)\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<is_ra<Iterator1>::value && is_ra<Iterator2>::value, bool>\nstarts_with(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    auto len = end2 - begin2;\n    if ((end - begin) < len) {\n        return false;\n    }\n    return std_equal_helper(begin2, end2, begin, begin + len, std::move(binary_predicate));\n}\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPreidcate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!is_ra<Iterator1>::value || !is_ra<Iterator2>::value, bool>\nstarts_with(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPreidcate binary_predicate) {\n    for (; begin2 != end2; ++begin, (void)++begin2) {\n        if (begin == end || !binary_predicate(*begin, *begin2)) {\n            return false;\n        }\n    }\n    return true;\n}\n\n#else\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<!is_ra<Iterator1>::value || !is_ra<Iterator2>::value, bool>\nstarts_with(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    for (; begin2 != end2; ++begin, (void)++begin2) {\n        if (begin == end || !binary_predicate(*begin, *begin2)) {\n            return false;\n        }\n    }\n    return true;\n}\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 enable_if_t<is_ra<Iterator1>::value && is_ra<Iterator2>::value, bool>\nstarts_with(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    auto len = end2 - begin2;\n    if ((end - begin) < len) {\n        return false;\n    }\n    return std_equal_helper(begin2, end2, begin, begin + len, std::move(binary_predicate));\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/algorithm/transform.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ALGORITHM_TRANSFORM_HPP\n#define LZ_DETAIL_ALGORITHM_TRANSFORM_HPP\n\n#include <Lz/detail/procs/get_end.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/std_algo_compat.hpp>\n#include <algorithm>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S, class OutputIterator, class UnaryOp>\nLZ_CONSTEXPR_CXX_14 void transform(Iterator begin, S end, OutputIterator output, UnaryOp unary_op) {\n    if constexpr (std_algo_compat_v<Iterator, S>) {\n        static_cast<void>(std::transform(begin, detail::get_end(begin, end), output, std::move(unary_op)));\n    }\n    else {\n        for (; begin != end; ++begin, ++output) {\n            *output = unary_op(*begin);\n        }\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S, class OutputIterator, class UnaryOp>\nLZ_CONSTEXPR_CXX_14 detail::enable_if_t<std_algo_compat<Iterator, S>::value>\ntransform(Iterator begin, S end, OutputIterator output, UnaryOp unary_op) {\n    static_cast<void>(std::transform(begin, detail::get_end(begin, end), output, std::move(unary_op)));\n}\n\ntemplate<class Iterator, class S, class OutputIterator, class UnaryOp>\nLZ_CONSTEXPR_CXX_14 detail::enable_if_t<!std_algo_compat<Iterator, S>::value>\ntransform(Iterator begin, S end, OutputIterator output, UnaryOp unary_op) {\n    for (; begin != end; ++begin, ++output) {\n        *output = unary_op(*begin);\n    }\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_DETAIL_ALGORITHM_TRANSFORM_HPP\n"
  },
  {
    "path": "include/Lz/detail/compiler_config.hpp",
    "content": "#pragma once\n\n// clang-format off\n\n#ifndef LZ_DETAIL_COMPILER_CHECKS_HPP\n#define LZ_DETAIL_COMPILER_CHECKS_HPP\n\n#define LZ_VERSION_MAJOR 9\n#define LZ_VERSION_MINOR 0\n#define LZ_VERSION_PATCH 0\n#define LZ_VERSION ((LZ_VERSION_MAJOR * 10000) + (LZ_VERSION_MINOR * 100) + (LZ_VERSION_PATCH))\n#define LZ_VERSION_STRING \"9.0.0\"\n\n\n#if defined(__has_include)\n  #define LZ_HAS_INCLUDE(FILE) __has_include(FILE)\n#else\n  #define LZ_HAS_INCLUDE(FILE) 0\n#endif // __has_include\n\n#if defined(__has_cpp_attribute)\n  #define LZ_HAS_ATTRIBUTE(ATTR) __has_cpp_attribute(ATTR)\n#else\n  #define LZ_HAS_ATTRIBUTE(ATTR) 0\n#endif // __has_cpp_attribute\n\n#if ((__cplusplus >= 201103L) && (__cplusplus < 201402L))\n  #define LZ_HAS_CXX_11\n#endif // end has cxx 11\n\n#if __cplusplus >= 201402L\n  #define LZ_HAS_CXX_14\n#endif\n\n#if (__cplusplus >= 201300)\n  #define LZ_CONSTEXPR_CXX_14 constexpr\n#else\n  #define LZ_CONSTEXPR_CXX_14 inline\n#endif // has cxx 14\n\n#if (__cplusplus >= 201703L)\n  #define LZ_HAS_CXX_17\n  #define LZ_CONSTEXPR_CXX_17 constexpr\n#else\n #define LZ_CONSTEXPR_CXX_17 inline\n#endif // Has cxx 17\n\n\n#if (__cplusplus >= 202002L)\n  #define LZ_HAS_CXX_20\n  #if defined(__cpp_constexpr_dynamic_alloc) && defined(__cpp_lib_constexpr_dynamic_alloc) &&                                      \\\n      defined(__cpp_lib_constexpr_string) && defined(__cpp_lib_constexpr_vector) && defined(__cpp_lib_constexpr_algorithms)\n    #define LZ_CONSTEXPR_CXX_20 constexpr\n  #else\n    #define LZ_CONSTEXPR_CXX_20 inline\n  #endif // cpp constexpr new/algo\n#else\n  #define LZ_CONSTEXPR_CXX_20 inline\n#endif // Has cxx 20\n\n#if (__cplusplus >= 202300L)\n  #define LZ_HAS_CXX_23\n#endif\n\n#ifdef __cpp_inline_variables\n  #define LZ_INLINE_VAR inline\n#else\n  #define LZ_INLINE_VAR\n#endif // __cpp_inline_variables\n\n#if LZ_HAS_ATTRIBUTE(nodiscard) && defined(LZ_HAS_CXX_17)\n  #define LZ_NODISCARD [[nodiscard]]\n#else\n  #define LZ_NODISCARD\n#endif // LZ_HAS_ATTRIBUTE(nodiscard)\n\n#if LZ_HAS_INCLUDE(<string_view>) && (defined(LZ_HAS_CXX_17))\n  #define LZ_HAS_STRING_VIEW\n#endif // has string view\n\n#if LZ_HAS_INCLUDE(<concepts>) && (defined(LZ_HAS_CXX_20))\n  #define LZ_HAS_CONCEPTS\n#endif // Have concepts\n\n#ifdef __cpp_if_constexpr\n  #define LZ_CONSTEXPR_IF constexpr\n#else\n  #define LZ_CONSTEXPR_IF\n#endif // __cpp_if_constexpr\n\n#ifndef LZ_MODULE_EXPORT\n  #define LZ_MODULE_EXPORT\n  #define LZ_MODULE_EXPORT_SCOPE_BEGIN\n  #define LZ_MODULE_EXPORT_SCOPE_END\n#endif\n\n#if defined(LZ_HAS_CXX_20) && defined(__cpp_lib_format) && (__cpp_lib_format >= 201907L)\n  #define LZ_HAS_FORMAT\n#endif // format\n\nnamespace lz {\nnamespace detail {\nusing size_t = decltype(sizeof(0));\nusing ptrdiff_t = decltype(static_cast<char*>(nullptr) - static_cast<char*>(nullptr));\n}\n}\n\n#endif // LZ_COMPILER_CHECK_HPP\n\n// clang-format on\n"
  },
  {
    "path": "include/Lz/detail/fake_ptr_proxy.hpp",
    "content": "#pragma once\n\n#ifndef LZ_FAKE_POINTER_PROXY_HPP\n#define LZ_FAKE_POINTER_PROXY_HPP\n\n#include <type_traits>\n#include <Lz/detail/procs/addressof.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nclass fake_ptr_proxy {\n    T _t;\n\n    using ptr = decltype(detail::addressof(_t));\n\npublic:\n    constexpr explicit fake_ptr_proxy(const T& t) noexcept(std::is_nothrow_copy_constructible<T>::value) : _t{ t } {\n    }\n\n    LZ_NODISCARD constexpr ptr operator->() const noexcept {\n        return detail::addressof(_t);\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 ptr operator->() noexcept {\n        return detail::addressof(_t);\n    }\n};\n} // namespace detail\n} // namespace lz\n#endif // LZ_FAKE_POINTER_PROXY_HPP\n"
  },
  {
    "path": "include/Lz/detail/func_container.hpp",
    "content": "#pragma once\n\n#ifndef LZ_FUNCTION_CONTAINER_HPP\n#define LZ_FUNCTION_CONTAINER_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/addressof.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <type_traits>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Func>\nclass func_container {\n    Func _func{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr func_container()\n        requires(std::default_initializable<Func>)\n    = default;\n\n#else\n\n    template<class F = Func, class = enable_if_t<std::is_default_constructible<F>::value>>\n    constexpr func_container() noexcept(std::is_nothrow_default_constructible<Func>::value) {\n    }\n\n#endif\n\n    explicit func_container(const Func& func) : _func{ func } {\n    }\n\n    explicit func_container(Func&& func) noexcept(std::is_nothrow_move_constructible<Func>::value) : _func{ std::move(func) } {\n    }\n\n    func_container(const func_container& other) : _func{ other._func } {\n    }\n\n    func_container(func_container&& other) noexcept(std::is_nothrow_copy_constructible<Func>::value) :\n        _func(std::move(other._func)) {\n    }\n\n    func_container& operator=(const func_container& other) {\n        ::new (static_cast<void*>(detail::addressof(_func))) Func(other._func);\n        return *this;\n    }\n\n    func_container& operator=(func_container&& other) noexcept(std::is_nothrow_move_assignable<Func>::value) {\n        _func.~Func();\n        ::new (static_cast<void*>(detail::addressof(_func))) Func(static_cast<Func&&>(other._func));\n        return *this;\n    }\n\n    template<class F = Func, class... Args>\n    LZ_NODISCARD constexpr auto operator()(Args&&... args) const& -> decltype(_func(std::forward<Args>(args)...)) {\n        return _func(std::forward<Args>(args)...);\n    }\n\n    template<class F = Func, class... Args>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 auto operator()(Args&&... args) & -> decltype(_func(std::forward<Args>(args)...)) {\n        return _func(std::forward<Args>(args)...);\n    }\n\n    template<class F = Func, class... Args>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 auto\n    operator()(Args&&... args) && -> decltype(std::move(_func)(std::forward<Args>(args)...)) {\n        return std::move(_func)(std::forward<Args>(args)...);\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_FUNCTION_CONTAINER_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/as_iterator.hpp",
    "content": "#pragma once\n\n#ifndef LZ_AS_ITERATOR_ITERABLE_HPP\n#define LZ_AS_ITERATOR_ITERABLE_HPP\n\n#include <Lz/detail/iterators/as_iterator.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class IterCat>\nclass as_iterator_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n\npublic:\n    using value_type = iter_t<Iterable>;\n    using iterator = as_iterator_iterator<value_type, sentinel_t<Iterable>, IterCat>;\n    using const_iterator = iterator;\n\nprivate:\n    using sentinel = typename iterator::sentinel;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr as_iterator_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr as_iterator_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    explicit constexpr as_iterator_iterable(I&& iterable) : _iterable{ std::forward<I>(iterable) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#else\n\n    template<class T = is_sized<Iterable>>\n    LZ_NODISCARD constexpr enable_if_t<T::value, size_t> size() const {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#endif\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return iterator{ _iterable.begin() };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    LZ_NODISCARD constexpr auto end() const {\n        if constexpr (!is_sentinel_v<value_type, sentinel_t<Iterable>>) {\n            return iterator{ _iterable.end() };\n        }\n        else {\n            return sentinel{ _iterable.end() };\n        }\n    }\n\n#else\n\n    template<class I = value_type>\n    LZ_NODISCARD constexpr enable_if_t<!is_sentinel<I, sentinel_t<Iterable>>::value, iterator> end() const {\n        return iterator{ _iterable.end() };\n    }\n\n    template<class I = value_type>\n    LZ_NODISCARD constexpr enable_if_t<is_sentinel<I, sentinel_t<Iterable>>::value, sentinel> end() const {\n        return sentinel{ _iterable.end() };\n    }\n\n#endif // LZ_HAS_CXX_17\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_AS_ITERATOR_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/c_string.hpp",
    "content": "#pragma once\n\n#ifndef LZ_C_STRING_ITERABLE_HPP\n#define LZ_C_STRING_ITERABLE_HPP\n\n#include <Lz/detail/iterators/c_string.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class C>\nclass c_string_iterable : public lazy_view {\n    C* _begin{ nullptr };\n\npublic:\n    using iterator = c_string_iterator<C>;\n    using sentinel = typename c_string_iterator<C>::sentinel;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n    constexpr c_string_iterable() noexcept = default;\n\n    explicit constexpr c_string_iterable(C* begin) noexcept : _begin{ begin } {\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const noexcept {\n        return iterator{ _begin };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_C_STRING_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/cached_size.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CACHED_SIZE_ITERABLE_HPP\n#define LZ_CACHED_SIZE_ITERABLE_HPP\n\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/procs/eager_size.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable>\nclass cached_size_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    size_t _size{};\n\npublic:\n    using iterator = iter_t<Iterable>;\n    using const_iterator = iterator;\n    using sentinel = sentinel_t<Iterable>;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr cached_size_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr cached_size_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    explicit constexpr cached_size_iterable(I&& iterable) :\n        _iterable{ std::forward<I>(iterable) },\n        _size{ static_cast<size_t>(lz::eager_size(_iterable)) } {\n    }\n\n    LZ_NODISCARD constexpr size_t size() const noexcept {\n        return _size;\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return _iterable.begin();\n    }\n\n    LZ_NODISCARD constexpr sentinel end() const {\n        return _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/cartesian_product.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CARTESIAN_PRODUCT_ITERABLE_HPP\n#define LZ_CARTESIAN_PRODUCT_ITERABLE_HPP\n\n#include <Lz/detail/iterators/cartesian_product.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n#include <numeric>\n\nnamespace lz {\nnamespace detail {\ntemplate<class... Iterables>\nclass cartesian_product_iterable : public lazy_view {\n    std::tuple<maybe_owned<Iterables>...> _iterables{};\n\n    template<size_t... Is>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 size_t size(index_sequence<Is...>) const {\n        const size_t sizes[] = { static_cast<size_t>(lz::size(std::get<Is>(_iterables)))... };\n        return std::accumulate(detail::begin(sizes), detail::end(sizes), size_t{ 1 }, std::multiplies<size_t>{});\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    template<std::ptrdiff_t I, class Iterators>\n    constexpr void init_iterators(Iterators& it, bool& first_at_end) const {\n        if constexpr (I >= 0) {\n            const bool this_at_end = std::get<I>(it) == std::get<I>(_iterables).end();\n            if (this_at_end && !first_at_end) {\n                std::get<0>(it) = std::get<0>(_iterables).end();\n                first_at_end = true;\n                return;\n            }\n            init_iterators<I - 1>(it, first_at_end);\n        }\n    }\n\n#else\n\n    template<std::ptrdiff_t I, class Iterators>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<(I >= 0)> init_iterators(Iterators& it, bool& first_at_end) const {\n        const bool this_at_end = std::get<I>(it) == std::get<I>(_iterables).end();\n        if (this_at_end && !first_at_end) {\n            std::get<0>(it) = std::get<0>(_iterables).end();\n            first_at_end = true;\n            return;\n        }\n        init_iterators<I - 1>(it, first_at_end);\n    }\n\n    template<std::ptrdiff_t I, class Iterators>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<(I < 0)> init_iterators(Iterators&, bool&) const noexcept {\n    }\n\n#endif\n\n    template<class Iterable2, size_t... Is>\n    static cartesian_product_iterable<remove_ref_t<Iterable2>, Iterables...>\n    concat_iterables(Iterable2&& iterable2, cartesian_product_iterable<Iterables...>&& cartesian, index_sequence<Is...>) {\n        return { std::forward<Iterable2>(iterable2), std::move(std::get<Is>(cartesian._iterables))... };\n    }\n\n    template<class Iterable2, size_t... Is>\n    static cartesian_product_iterable<remove_ref_t<Iterable2>, Iterables...>\n    concat_iterables(Iterable2&& iterable2, const cartesian_product_iterable<Iterables...>& cartesian, index_sequence<Is...>) {\n        return { std::forward<Iterable2>(iterable2), std::get<Is>(cartesian._iterables)... };\n    }\n\n    static constexpr size_t tuple_size = sizeof...(Iterables);\n    using is = make_index_sequence<tuple_size>;\n\npublic:\n    using iterator = cartesian_product_iterator<std::tuple<maybe_owned<Iterables>...>>;\n    using sentinel = typename iterator::sentinel;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || disjunction<has_sentinel<Iterables>...>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr cartesian_product_iterable()\n        requires(std::default_initializable<maybe_owned<Iterables>> && ...)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterables), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr cartesian_product_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class... Is>\n    LZ_CONSTEXPR_CXX_14 cartesian_product_iterable(Is&&... iterables) : _iterables{ std::forward<Is>(iterables)... } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterables> && ...)\n    {\n        return size(is{});\n    }\n\n#else\n\n    template<bool S = conjunction<is_sized<Iterables>...>::value>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<S, size_t> size() const {\n        return size(is{});\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        auto it = begin_tuple(_iterables);\n        auto end = end_tuple(_iterables);\n        auto first_at_end = std::get<0>(it) == std::get<0>(end);\n        init_iterators<static_cast<std::ptrdiff_t>(tuple_size) - 1>(it, first_at_end);\n        return { _iterables, it };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            auto rest_it = begin_tuple(_iterables);\n            auto end = end_tuple(_iterables);\n            std::get<0>(rest_it) = std::get<0>(end);\n            return iterator{ _iterables, rest_it };\n        }\n        else {\n            return sentinel{};\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        auto rest_it = begin_tuple(_iterables);\n        auto end = end_tuple(_iterables);\n        std::get<0>(rest_it) = std::get<0>(end);\n        return iterator{ _iterables, rest_it };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, sentinel> end() const noexcept {\n        return {};\n    }\n\n#endif\n\n    template<class Iterable2>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 friend cartesian_product_iterable<remove_ref_t<Iterable2>, Iterables...>\n    operator|(Iterable2&& iterable2, cartesian_product_iterable<Iterables...>&& cartesian) {\n        return concat_iterables(std::forward<Iterable2>(iterable2), std::move(cartesian), is{});\n    }\n\n    template<class Iterable2>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 friend cartesian_product_iterable<remove_ref_t<Iterable2>, Iterables...>\n    operator|(Iterable2&& iterable2, const cartesian_product_iterable<Iterables...>& cartesian) {\n        return concat_iterables(std::forward<Iterable2>(iterable2), cartesian, is{});\n    }\n};\n\n} // namespace detail\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/chunk_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CHUNK_IF_ITERABLE_HPP\n#define LZ_CHUNK_IF_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/chunk_if.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class ValueType, class Iterable, class UnaryPredicate>\nclass chunk_if_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    func_container<UnaryPredicate> _predicate{};\n\npublic:\n    using iterator = chunk_if_iterator<ValueType, iter_t<Iterable>, sentinel_t<Iterable>, func_container<UnaryPredicate>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr chunk_if_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<UnaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable),\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<UnaryPredicate>::value>>\n    constexpr chunk_if_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                           std::is_nothrow_default_constructible<UnaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr chunk_if_iterable(I&& iterable, UnaryPredicate predicate) :\n        _iterable{ iterable },\n        _predicate{ std::move(predicate) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable.begin(), _iterable.end(), _predicate, _iterable.end() == _iterable.begin() };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/chunks.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CHUNKS_ITERABLE_HPP\n#define LZ_CHUNKS_ITERABLE_HPP\n\n#include <Lz/detail/iterators/chunks.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable>\nclass chunks_iterable : public lazy_view {\n\n    using inner = iter_t<Iterable>;\n\npublic:\n    using iterator = chunks_iterator<maybe_owned<Iterable>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n    using sentinel = typename iterator::sentinel;\n\nprivate:\n    maybe_owned<Iterable> _iterable{};\n    typename iterator::difference_type _chunk_size{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr chunks_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr chunks_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 chunks_iterable(I&& iterable, const typename iterator::difference_type chunk_size) :\n        _iterable{ std::forward<I>(iterable) },\n        _chunk_size{ chunk_size } {\n        LZ_ASSERT(chunk_size > 0, \"Chunk size must be greater than 0\");\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>((lz::size(_iterable) + (static_cast<size_t>(_chunk_size) - 1)) /\n                                   static_cast<size_t>(_chunk_size));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_sized<I>::value, size_t> size() const {\n        return static_cast<size_t>((lz::size(_iterable) + (static_cast<size_t>(_chunk_size) - 1)) /\n                                   static_cast<size_t>(_chunk_size));\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto begin() const {\n        // Also counts for ra\n        if constexpr (is_bidi_tag_v<typename iterator::iterator_category>) {\n            return iterator{ _iterable, _iterable.begin(), _chunk_size };\n        }\n        else {\n            return iterator{ _iterable.begin(), _iterable.end(), _chunk_size };\n        }\n    }\n\n#else\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_bidi_tag<I>::value, iterator> begin() const {\n        return { _iterable.begin(), _iterable.end(), _chunk_size };\n    }\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_bidi_tag<I>::value, iterator> begin() const {\n        return { _iterable, _iterable.begin(), _chunk_size };\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        // Also counts for ra\n        if constexpr (is_bidi_tag_v<typename iterator::iterator_category> && !is_sentinel_v<inner, sentinel_t<Iterable>>) {\n            return iterator{ _iterable, _iterable.end(), _chunk_size };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD\n        LZ_CONSTEXPR_CXX_14 enable_if_t<is_bidi_tag<I>::value && !is_sentinel<inner, sentinel_t<Iterable>>::value, iterator>\n        end() const {\n        return { _iterable, _iterable.end(), _chunk_size };\n    }\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD constexpr enable_if_t<!is_bidi_tag<I>::value || is_sentinel<inner, sentinel_t<Iterable>>::value,\n                                       default_sentinel_t>\n    end() const {\n        return {};\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/common.hpp",
    "content": "#pragma once\n\n#ifndef LZ_COMMON_ITERABLE_HPP\n#define LZ_COMMON_ITERABLE_HPP\n\n#include <Lz/detail/iterators/common.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable>\nclass common_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n\npublic:\n    using iterator = common_iterator<iter_t<Iterable>, sentinel_t<Iterable>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr common_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr common_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    explicit constexpr common_iterable(I&& iterable) : _iterable{ std::forward<I>(iterable) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    LZ_NODISCARD constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return lz::size(_iterable);\n    }\n\n#else\n\n    template<bool Sized = is_sized<Iterable>::value>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<Sized, size_t> size() const {\n        return lz::size(_iterable);\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return iterator{ _iterable.begin() };\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator end() const {\n        return iterator{ _iterable.end() };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/concatenate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CONCATENATE_ITERABLE_HPP\n#define LZ_CONCATENATE_ITERABLE_HPP\n\n#include <Lz/detail/iterators/concatenate.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class... Iterables>\nclass concatenate_iterable : public lazy_view {\n    using iterables = std::tuple<maybe_owned<Iterables>...>;\n    iterables _iterables{};\n\n    template<size_t... I>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 size_t size(index_sequence<I...>) const {\n        const size_t sizes[] = { static_cast<size_t>(lz::size(std::get<I>(_iterables)))... };\n        return std::accumulate(detail::begin(sizes), detail::end(sizes), size_t{ 0 });\n    }\n\n    template<class Iterable2, size_t... Is>\n    static concatenate_iterable<remove_ref_t<Iterable2>, Iterables...>\n    concat_iterables(Iterable2&& iterable2, concatenate_iterable<Iterables...> cat, index_sequence<Is...>) {\n        return { std::forward<Iterable2>(iterable2), std::move(std::get<Is>(cat._iterables))... };\n    }\n\n    using is = make_index_sequence<sizeof...(Iterables)>;\n\npublic:\n    using iterator = concatenate_iterator<iterables, std::tuple<iter_t<Iterables>...>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || disjunction<has_sentinel<Iterables>...>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr concatenate_iterable()\n        requires(std::default_initializable<maybe_owned<Iterables>> && ...)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterables), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr concatenate_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class... Is>\n    LZ_CONSTEXPR_CXX_14 concatenate_iterable(Is&&... its) : _iterables{ std::forward<Is>(its)... } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterables> && ...)\n    {\n        return size(is{});\n    }\n\n#else\n\n    template<class T = conjunction<is_sized<Iterables>...>>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<T::value, size_t> size() const {\n        return size(is{});\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterables, begin_tuple(_iterables) };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterables, end_tuple(_iterables) };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterables, end_tuple(_iterables) };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, default_sentinel_t> end() const {\n        return {};\n    }\n\n    // clang-format on\n#endif // LZ_HAS_CXX_17\n\n    template<class Iterable>\n    friend concatenate_iterable<remove_ref_t<Iterable>, Iterables...>\n    operator|(Iterable&& iterable, concatenate_iterable<Iterables...> concatenate) {\n        return concat_iterables(std::forward<Iterable>(iterable), std::move(concatenate), is{});\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_CONCATENATE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/drop.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DROP_ITERABLE_HPP\n#define LZ_DROP_ITERABLE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/procs/next_fast.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable>\nclass drop_iterable : public lazy_view {\npublic:\n    using iterator = iter_t<Iterable>;\n    using sentinel = sentinel_t<Iterable>;\n    using const_iterator = iterator;\n    using value_type = val_iterable_t<Iterable>;\n\nprivate:\n    using diff = diff_type<iterator>;\n\n    maybe_owned<Iterable> _iterable{};\n    diff _n{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr drop_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr drop_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr drop_iterable(I&& iterable, const diff n) : _iterable{ std::forward<I>(iterable) }, _n{ n } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        const auto size = lz::size(_iterable);\n        return size > static_cast<size_t>(_n) ? size - static_cast<size_t>(_n) : 0;\n    }\n\n#else\n\n    template<bool Sized = is_sized<Iterable>::value>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<Sized, size_t> size() const {\n        const auto size = lz::size(_iterable);\n        return size > static_cast<size_t>(_n) ? size - static_cast<size_t>(_n) : 0;\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return next_fast_safe(_iterable, _n);\n    }\n\n    LZ_NODISCARD constexpr sentinel end() const {\n        return _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/drop_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DROP_WHILE_ITERABLE_HPP\n#define LZ_DROP_WHILE_ITERABLE_HPP\n\n#include <Lz/algorithm/find_if_not.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable>\nclass drop_while_iterable : public lazy_view {\n    iter_t<Iterable> _begin{};\n    sentinel_t<Iterable> _end{};\n\npublic:\n    using iterator = iter_t<Iterable>;\n    using sentinel = sentinel_t<Iterable>;\n    using const_iterator = iterator;\n    using value_type = val_iterable_t<Iterable>;\n\nprivate:\n    static constexpr bool return_sentinel = !is_bidi_tag<iter_cat_t<iterator>>::value || has_sentinel<Iterable>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr drop_while_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<sentinel_t<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_begin), class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                             std::is_default_constructible<sentinel_t<Iterable>>::value>>\n    constexpr drop_while_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<sentinel_t<Iterable>>::value) {\n    }\n\n#endif\n\n    template<class I, class UnaryPredicate>\n    constexpr drop_while_iterable(I&& iterable, UnaryPredicate unary_predicate) :\n        _begin{ lz::find_if_not(iterable, std::move(unary_predicate)) },\n        _end{ detail::end(iterable) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return _begin;\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(is_ra_tag_v<iter_cat_t<iterator>>)\n    {\n        return static_cast<size_t>(_end - _begin);\n    }\n\n#else\n\n    template<class I = iter_cat_t<iterator>>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_ra_tag<I>::value, size_t> size() const {\n        return static_cast<size_t>(_end - _begin);\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return _end;\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, sentinel> end() const {\n        return _end;\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD constexpr enable_if_t<R, default_sentinel_t> end() const {\n        return lz::default_sentinel;\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/duplicates.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DUPLICATES_ITERABLE_HPP\n#define LZ_DUPLICATES_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/duplicates.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class BinaryPredicate>\nclass duplicates_iterable : public lazy_view {\npublic:\n    using iterator = duplicates_iterator<maybe_owned<Iterable>, func_container<BinaryPredicate>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n    using it = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n\nprivate:\n    maybe_owned<Iterable> _iterable{};\n    func_container<BinaryPredicate> _compare{};\n\n    static constexpr bool return_sentinel = !is_bidi_tag<iter_cat_t<iterator>>::value || has_sentinel<Iterable>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr duplicates_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                                std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr duplicates_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr duplicates_iterable(I&& iterable, BinaryPredicate compare) :\n        _iterable{ std::forward<I>(iterable) },\n        _compare{ std::move(compare) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _iterable.begin(), _compare };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _iterable.end(), _compare };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable, _iterable.end(), _compare };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD constexpr enable_if_t<R, default_sentinel_t> end() const {\n        return {};\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/enumerate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ENUMERATE_ITERABLE_HPP\n#define LZ_ENUMERATE_ITERABLE_HPP\n\n#include <Lz/detail/iterators/enumerate.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class IntType>\nclass enumerate_iterable : public lazy_view {\n    using iter = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n\n    maybe_owned<Iterable> _iterable{};\n    IntType _start{};\n\npublic:\n    using iterator = enumerate_iterator<maybe_owned<Iterable>, IntType>;\n    using const_iterator = iterator;\n    using sentinel = typename iterator::sentinel;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel = !is_bidi_tag<iter_cat_t<iterator>>::value || has_sentinel<Iterable>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr enumerate_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr enumerate_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr enumerate_iterable(I&& iterable, const IntType start) : _iterable{ std::forward<I>(iterable) }, _start{ start } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_sized<I>::value, size_t> size() const {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr iterator begin() const {\n        if constexpr (is_bidi_tag_v<typename iterator::iterator_category>) {\n            return { _iterable, _iterable.begin(), _start };\n        }\n        else {\n            return { _iterable.begin(), _start };\n        }\n    }\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _iterable.end(), _start + static_cast<IntType>(lz::eager_size(_iterable)) };\n        }\n        else if constexpr (is_bidi_tag_v<typename iterator::iterator_category>) {\n            return sentinel{ _start };\n        }\n        else {\n            return sentinel{ _iterable.end() };\n        }\n    }\n\n#else\n    template<class T = is_bidi_tag<typename iterator::iterator_category>>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<T::value, iterator> begin() const {\n        return { _iterable, _iterable.begin(), _start };\n    }\n\n    template<class T = is_bidi_tag<typename iterator::iterator_category>>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!T::value, iterator> begin() const {\n        return { _iterable.begin(), _start };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable, _iterable.end(), _start + static_cast<IntType>(lz::eager_size(_iterable)) };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R && is_bidi_tag<typename iterator::iterator_category>::value, sentinel>\n    end() const {\n        return sentinel{ _start };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R && !is_bidi_tag<typename iterator::iterator_category>::value, sentinel> end() const {\n        return sentinel{ _iterable.end() };\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/except.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCEPT_ITERABLE_HPP\n#define LZ_EXCEPT_ITERABLE_HPP\n\n#include <Lz/cached_size.hpp>\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/except.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/conditional.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable1, class Iterable2, class BinaryPredicate>\nclass except_iterable : public lazy_view {\n    using iterable2_type = conditional_t<is_sized<Iterable2>::value, maybe_owned<Iterable2>, cached_size_iterable<Iterable2>>;\n\n    maybe_owned<Iterable1> _iterable1{};\n    iterable2_type _iterable2{};\n    func_container<BinaryPredicate> _binary_predicate{};\n\npublic:\n    using iterator = except_iterator<maybe_owned<Iterable1>, iterable2_type, func_container<BinaryPredicate>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    using iter = iter_t<Iterable1>;\n    static constexpr bool return_sentinel = !is_bidi<iter>::value || has_sentinel<Iterable1>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr except_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable1>> && std::default_initializable<iterable2_type> &&\n                 std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable1), class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                                 std::is_default_constructible<iterable2_type>::value &&\n                                                                 std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr except_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<iterable2_type>::value &&\n                                         std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I1, class I2>\n    constexpr except_iterable(I1&& iterable1, I2&& iterable2, BinaryPredicate binary_predicate) :\n        _iterable1{ std::forward<I1>(iterable1) },\n        _iterable2{ std::forward<I2>(iterable2) },\n        _binary_predicate{ std::move(binary_predicate) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable1, _iterable1.begin(), _iterable2, _binary_predicate };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable1, _iterable1.end(), _iterable2, _binary_predicate };\n        }\n        else {\n            return default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable1, _iterable1.end(), _iterable2, _binary_predicate };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, default_sentinel_t> end() const noexcept {\n        return default_sentinel;\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/exclude.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCLUDE_ITERABLE_HPP\n#define LZ_EXCLUDE_ITERABLE_HPP\n\n#include <Lz/detail/iterators/exclude.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable>\nclass exclude_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    diff_iterable_t<Iterable> _from{};\n    diff_iterable_t<Iterable> _to{};\n\n    using it = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n\npublic:\n    using iterator = exclude_iterator<maybe_owned<Iterable>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n    using sentinel = typename iterator::sentinel;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<it, sent>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr exclude_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr exclude_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr exclude_iterable(I&& iterable, const diff_iterable_t<Iterable> from, const diff_iterable_t<Iterable> to) :\n        _iterable{ std::forward<I>(iterable) },\n        _from{ from },\n        _to{ to } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>(lz::size(_iterable) - static_cast<size_t>(_to - _from));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_sized<I>::value, size_t> size() const {\n        return static_cast<size_t>(lz::size(_iterable) - static_cast<size_t>(_to - _from));\n    }\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _iterable.begin(), _from, _to, 0 };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _iterable.end(), _from, _to,\n                             static_cast<typename iterator::difference_type>(lz::eager_size(_iterable)) };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable, _iterable.end(), _from, _to,\n                 static_cast<typename iterator::difference_type>(lz::eager_size(_iterable)) };\n    }\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, sentinel> end() const {\n        return lz::default_sentinel;\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_EXCLUDE_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/exclusive_scan.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCLUSIVE_SCAN_ITERABLE_HPP\n#define LZ_EXCLUSIVE_SCAN_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/exclusive_scan.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class T, class BinaryOp>\nclass exclusive_scan_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    T _init{};\n    func_container<BinaryOp> _binary_op{};\n\npublic:\n    using iterator = exclusive_scan_iterator<iter_t<Iterable>, sentinel_t<Iterable>, T, func_container<BinaryOp>>;\n    using const_iterator = iterator;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr exclusive_scan_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<T> &&\n                 std::default_initializable<BinaryOp>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable),\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<T>::value &&\n                                 std::is_default_constructible<BinaryOp>::value>>\n    constexpr exclusive_scan_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                                 std::is_nothrow_default_constructible<T>::value &&\n                                                 std::is_nothrow_default_constructible<BinaryOp>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr exclusive_scan_iterable(I&& iterable, T init, BinaryOp binary_op) :\n        _iterable{ std::forward<I>(iterable) },\n        _init{ std::move(init) },\n        _binary_op{ std::move(binary_op) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        const auto size = static_cast<size_t>(lz::size(_iterable));\n        return size == 0 ? 0 : size + 1;\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_sized<I>::value, size_t> size() const {\n        const auto size = static_cast<size_t>(lz::size(_iterable));\n        return size == 0 ? 0 : size + 1;\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const&{\n        return { _iterable.begin(), _iterable.end(), _init, _binary_op };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_EXCLUSIVE_SCAN_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/filter.hpp",
    "content": "#pragma once\n\n#ifndef LZ_FILTER_ITERABLE_HPP\n#define LZ_FILTER_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/filter.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class UnaryPredicate>\nclass filter_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    func_container<UnaryPredicate> _predicate{};\n\n    using it = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n\npublic:\n    using iterator = filter_iterator<maybe_owned<Iterable>, func_container<UnaryPredicate>>;\n    using const_iterator = iterator;\n    using sentinel = typename iterator::sentinel;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<it, sent>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr filter_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<UnaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable),\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<UnaryPredicate>::value>>\n    constexpr filter_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<UnaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr filter_iterable(I&& iterable, UnaryPredicate predicate) :\n        _iterable{ std::forward<I>(iterable) },\n        _predicate{ std::move(predicate) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _iterable.begin(), _predicate };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _iterable.end(), _predicate };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable, _iterable.end(), _predicate };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD constexpr enable_if_t<R, default_sentinel_t> end() const noexcept {\n        return {};\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // end LZ_FILTER_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/flatten.hpp",
    "content": "#pragma once\n\n#ifndef LZ_FLATTEN_ITERABLE_HPP\n#define LZ_FLATTEN_ITERABLE_HPP\n\n#include <Lz/algorithm/accumulate.hpp>\n#include <Lz/detail/iterators/flatten.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/conditional.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class T>\n[[nodiscard]] constexpr bool all_sized_fn() {\n    if constexpr (is_iterable_v<T>) {\n        return is_sized_v<T> && all_sized_fn<val_iterable_t<T>>();\n    }\n    else {\n        return true;\n    }\n}\n\ntemplate<class Iterable>\nusing is_all_sized = std::bool_constant<all_sized_fn<Iterable>()>;\n\n#else\n\ntemplate<bool SizedIterable>\nstruct all_sized_impl {\n    template<class>\n    using type = std::integral_constant<bool, SizedIterable>;\n};\n\ntemplate<>\nstruct all_sized_impl<true> {\n    template<class Iterable>\n    using inner = val_iterable_t<Iterable>;\n\n    template<class Iterable>\n    using is_inner_sized = is_sized<inner<Iterable>>;\n\n    template<class Iterable>\n    using type = conditional_t<\n        // If the inner is another iterable\n        is_iterable<inner<Iterable>>::value,\n        // Check whether the inner is also sized\n        typename all_sized_impl<is_inner_sized<Iterable>::value>::template type<inner<Iterable>>,\n        // Otherwise return true\n        std::true_type>;\n};\n\ntemplate<class Iterable>\nusing is_all_sized = typename all_sized_impl<is_sized<Iterable>::value && is_iterable<Iterable>::value>::template type<Iterable>;\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<size_t I, class Iterable>\n[[nodiscard]] constexpr size_t size_all(Iterable&& iterable) {\n    if constexpr (I == 1) {\n        return static_cast<size_t>(lz::size(iterable));\n    }\n    else {\n        return lz::accumulate(iterable, size_t{ 0 },\n                              [](size_t sum, ref_iterable_t<Iterable> val) { return sum + size_all<I - 1>(val); });\n    }\n}\n\n#else\n\ntemplate<size_t I, class Iterable>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<I == 1, size_t> size_all(Iterable&& iterable) {\n    return static_cast<size_t>(lz::size(iterable));\n}\n\ntemplate<size_t I, class Iterable>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<(I > 1), size_t> size_all(Iterable&& iterable) {\n    return lz::accumulate(iterable, size_t{ 0 },\n                          [](size_t sum, ref_iterable_t<Iterable> val) { return sum + size_all<I - 1>(val); });\n}\n\n#endif\n\ntemplate<class Iterable, size_t Dims>\nclass flatten_iterable : public lazy_view {\n    using inner_iter = iter_t<Iterable>;\n    using inner_sentinel = sentinel_t<Iterable>;\n\n    maybe_owned<Iterable> _iterable{};\n\npublic:\n    using iterator = flatten_iterator<Iterable, Dims>;\n    using const_iterator = iterator;\n    using value_type = typename flatten_iterator<Iterable, 0>::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<inner_iter, inner_sentinel>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr flatten_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr flatten_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    explicit LZ_CONSTEXPR_CXX_14 flatten_iterable(I&& iterable) : _iterable{ std::forward<I>(iterable) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(is_all_sized<Iterable>::value)\n    {\n        return size_all<Dims + 1>(_iterable);\n    }\n\n#else\n\n    template<class T = is_all_sized<Iterable>>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<T::value, size_t> size() const {\n        return size_all<Dims + 1>(_iterable);\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _iterable.begin() };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _iterable.end() };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD constexpr enable_if_t<!R, iterator> end() const {\n        return { _iterable, _iterable.end() };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD constexpr enable_if_t<R, default_sentinel_t> end() const noexcept {\n        return {};\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_FLATTEN_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/generate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GENERATE_ITERABLE_HPP\n#define LZ_GENERATE_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/generate.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class, bool /* is inf */>\nclass generate_iterable;\n\ntemplate<class GeneratorFunc>\nclass generate_iterable<GeneratorFunc, false> : public lazy_view {\n    func_container<GeneratorFunc> _func{};\n    ptrdiff_t _amount{};\n\npublic:\n    using iterator = generate_iterator<func_container<GeneratorFunc>, false>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr generate_iterable()\n        requires(std::default_initializable<GeneratorFunc>)\n    = default;\n\n#else\n\n    template<class G = GeneratorFunc, class = enable_if_t<std::is_default_constructible<G>::value>>\n    constexpr generate_iterable() noexcept(std::is_nothrow_default_constructible<G>::value) {\n    }\n\n#endif\n\n    constexpr generate_iterable(GeneratorFunc func, const ptrdiff_t amount) : _func{ std::move(func) }, _amount{ amount } {\n    }\n\n    LZ_NODISCARD constexpr size_t size() const noexcept {\n        return static_cast<size_t>(_amount);\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return { _func, _amount };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n\ntemplate<class GeneratorFunc>\nclass generate_iterable<GeneratorFunc, true> : public lazy_view {\n    func_container<GeneratorFunc> _func;\n\npublic:\n    using iterator = generate_iterator<func_container<GeneratorFunc>, true>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr generate_iterable()\n        requires(std::default_initializable<GeneratorFunc>)\n    = default;\n\n#else\n\n    template<class G = GeneratorFunc, class = enable_if_t<std::is_default_constructible<G>::value>>\n    constexpr generate_iterable() noexcept(std::is_nothrow_default_constructible<G>::value) {\n    }\n\n#endif\n\n    explicit constexpr generate_iterable(GeneratorFunc func) : _func{ std::move(func) } {\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return iterator{ _func };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/generate_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GENERATE_WHILE_ITERABLE_HPP\n#define LZ_GENERATE_WHILE_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/generate_while.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class GeneratorFunc>\nclass generate_while_iterable : public lazy_view {\n    using fn_return_type = decltype(std::declval<GeneratorFunc>()());\n    using T = typename std::tuple_element<1, fn_return_type>::type;\n\n    fn_return_type _init{ T{}, true };\n    func_container<GeneratorFunc> _func{};\n\npublic:\n    using iterator = generate_while_iterator<func_container<GeneratorFunc>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr generate_while_iterable()\n        requires(std::default_initializable<GeneratorFunc> && std::default_initializable<fn_return_type>)\n    = default;\n\n#else\n\n    template<class G = GeneratorFunc,\n             class = enable_if_t<std::is_default_constructible<G>::value && std::is_default_constructible<fn_return_type>::value>>\n    constexpr generate_while_iterable() noexcept(std::is_nothrow_default_constructible<G>::value &&\n                                                 std::is_nothrow_default_constructible<fn_return_type>::value) {\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 generate_while_iterable(GeneratorFunc func) : _init{ func() }, _func{ std::move(func) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _func, _init };\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/group_by.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GROUP_BY_ITERABLE_HPP\n#define LZ_GROUP_BY_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/group_by.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class BinaryPredicate>\nclass group_by_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    func_container<BinaryPredicate> _binary_predicate{};\n\npublic:\n    using iterator = group_by_iterator<maybe_owned<Iterable>, func_container<BinaryPredicate>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<iter_t<Iterable>, sentinel_t<Iterable>>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr group_by_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                                std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr group_by_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                           std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 group_by_iterable(I&& iterable, BinaryPredicate binary_predicate) :\n        _iterable{ std::forward<I>(iterable) },\n        _binary_predicate{ std::move(binary_predicate) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _iterable.begin(), _binary_predicate };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _iterable.end(), _binary_predicate };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable, _iterable.end(), _binary_predicate };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, default_sentinel_t> end() const {\n        return {};\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_GROUP_BY_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/inclusive_scan.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INCLUSIVE_SCAN_ITERABLE_HPP\n#define LZ_INCLUSIVE_SCAN_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/inclusive_scan.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class T, class BinaryOp>\nclass inclusive_scan_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    T _init{};\n    func_container<BinaryOp> _binary_op{};\n\npublic:\n    using iterator = inclusive_scan_iterator<iter_t<Iterable>, sentinel_t<Iterable>, T, func_container<BinaryOp>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr inclusive_scan_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<T> &&\n                 std::default_initializable<BinaryOp>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable),\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<T>::value &&\n                                 std::is_default_constructible<BinaryOp>::value>>\n    constexpr inclusive_scan_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                                 std::is_nothrow_default_constructible<T>::value &&\n                                                 std::is_nothrow_default_constructible<BinaryOp>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr inclusive_scan_iterable(I&& iterable, T init, BinaryOp binary_op) :\n        _iterable{ std::forward<I>(iterable) },\n        _init{ std::move(init) },\n        _binary_op{ std::move(binary_op) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    LZ_NODISCARD constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_sized<I>::value, size_t> size() const {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        auto begin = _iterable.begin();\n        auto end = _iterable.end();\n\n        if (begin != end) {\n            auto new_init = _binary_op(_init, *begin);\n            return { begin, end, new_init, _binary_op };\n        }\n        return { _iterable.begin(), _iterable.end(), _init, _binary_op };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_INCLUSIVE_SCAN_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/interleave.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INTERLEAVED_ITERABLE_HPP\n#define LZ_INTERLEAVED_ITERABLE_HPP\n\n#include <Lz/detail/iterators/interleave.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class... Iterables>\nclass interleave_iterable : public lazy_view {\n    std::tuple<maybe_owned<Iterables>...> _iterables{};\n\n    using iterators = std::tuple<iter_t<Iterables>...>;\n    using sentinels = std::tuple<sentinel_t<Iterables>...>;\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 size_t size(index_sequence<I...>) const {\n        return min_variadic(static_cast<size_t>(std::get<I>(_iterables).size())...) * sizeof...(Iterables);\n    }\n\n    using is = make_index_sequence<sizeof...(Iterables)>;\n\n    template<class Iterable2, size_t... Is>\n    static interleave_iterable<remove_ref_t<Iterable2>, Iterables...>\n    concat_iterables(Iterable2&& iterable2, interleave_iterable<Iterables...>&& interleaved, index_sequence<Is...>) {\n        return { std::forward<Iterable2>(iterable2), std::move(std::get<Is>(interleaved._iterables))... };\n    }\n\npublic:\n    using iterator = interleave_iterator<iterators, sentinels>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    using this_sentinel = typename iterator::sentinel;\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || disjunction<has_sentinel<Iterables>...>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr interleave_iterable()\n        requires(std::default_initializable<maybe_owned<Iterables>> && ...)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterables), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr interleave_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class... Is>\n    constexpr interleave_iterable(Is&&... iterables) : _iterables{ std::forward<Is>(iterables)... } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterables> && ...)\n    {\n        return size(is{});\n    }\n\n#else\n\n    template<class T = conjunction<is_sized<Iterables>...>>\n    LZ_NODISCARD constexpr enable_if_t<T::value, size_t> size() const {\n        return size(is{});\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { begin_tuple(_iterables) };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ smallest_end_tuple(_iterables, is{}) };\n        }\n        else {\n            return this_sentinel{ end_tuple(_iterables) };\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { smallest_end_tuple(_iterables, is{}) };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, this_sentinel> end() const {\n        return this_sentinel{ end_tuple(_iterables) };\n    }\n\n#endif\n\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 friend interleave_iterable<remove_ref_t<Iterable>, Iterables...>\n    operator|(Iterable&& iterable, interleave_iterable<Iterables...> interleaved) {\n        return concat_iterables(std::forward<Iterable>(iterable), std::move(interleaved), is{});\n    }\n};\n\n} // namespace detail\n} // namespace lz\n#endif // LZ_INTERLEAVED_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/intersection.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INTERSECTION_ITERABLE_HPP\n#define LZ_INTERSECTION_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/intersection.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class Iterable2, class BinaryPredicate>\nclass intersection_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    maybe_owned<Iterable2> _iterable2{};\n    func_container<BinaryPredicate> _compare{};\n\npublic:\n    using iterator = intersection_iterator<maybe_owned<Iterable>, maybe_owned<Iterable2>, func_container<BinaryPredicate>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel = !is_bidi_tag<typename iterator::iterator_category>::value ||\n                                            has_sentinel<Iterable>::value || has_sentinel<Iterable2>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr intersection_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<maybe_owned<Iterable2>> &&\n                 std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                                std::is_default_constructible<maybe_owned<Iterable2>>::value &&\n                                                                std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr intersection_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                               std::is_nothrow_default_constructible<maybe_owned<Iterable2>>::value &&\n                                               std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I, class I2>\n    constexpr intersection_iterable(I&& iterable, I2&& iterable2, BinaryPredicate compare) :\n        _iterable{ std::forward<I>(iterable) },\n        _iterable2{ std::forward<I2>(iterable2) },\n        _compare{ std::move(compare) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _iterable2, _iterable.begin(), _iterable2.begin(), _compare };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _iterable2, _iterable.end(), _iterable2.end(), _compare };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable, _iterable2, _iterable.end(), _iterable2.end(), _compare };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD constexpr enable_if_t<R, default_sentinel_t> end() const {\n        return {};\n    }\n\n#endif\n};\n}\n} // namespace lz\n\n#endif // LZ_INTERSECTION_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/join_where.hpp",
    "content": "#pragma once\n\n#ifndef LZ_JOIN_WHERE_ITERABLE_HPP\n#define LZ_JOIN_WHERE_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/join_where.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class IterableA, class IterableB, class SelectorA, class SelectorB, class ResultSelector>\nclass join_where_iterable : public lazy_view {\n    maybe_owned<IterableA> _iterable_a{};\n    maybe_owned<IterableB> _iterable_b{};\n    func_container<SelectorA> _a{};\n    func_container<SelectorB> _b{};\n    func_container<ResultSelector> _result_selector{};\n\npublic:\n    using iterator = join_where_iterator<maybe_owned<IterableA>, iter_t<IterableB>, sentinel_t<IterableB>,\n                                         func_container<SelectorA>, func_container<SelectorB>, func_container<ResultSelector>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !has_sentinel<IterableA>::value || !is_bidi_tag<typename iterator::iterator_category>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr join_where_iterable()\n        requires(std::default_initializable<IterableA> && std::default_initializable<IterableB> &&\n                 std::default_initializable<SelectorA> && std::default_initializable<SelectorB> &&\n                 std::default_initializable<ResultSelector>)\n    = default;\n\n#else\n\n    template<\n        class I = decltype(_iterable_a),\n        class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<IterableB>::value &&\n                            std::is_default_constructible<SelectorA>::value && std::is_default_constructible<SelectorB>::value &&\n                            std::is_default_constructible<ResultSelector>::value>>\n    constexpr join_where_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<maybe_owned<IterableB>>::value &&\n                                             std::is_nothrow_default_constructible<SelectorA>::value &&\n                                             std::is_nothrow_default_constructible<SelectorB>::value &&\n                                             std::is_nothrow_default_constructible<ResultSelector>::value) {\n    }\n\n#endif\n\n    template<class I, class I2>\n    constexpr join_where_iterable(I&& iterable, I2&& iterable2, SelectorA a, SelectorB b, ResultSelector result_selector) :\n        _iterable_a{ std::forward<I>(iterable) },\n        _iterable_b{ std::forward<I2>(iterable2) },\n        _a{ std::move(a) },\n        _b{ std::move(b) },\n        _result_selector{ std::move(result_selector) } {\n    }\n\n    constexpr iterator begin() const {\n        return { _iterable_a, _iterable_a.begin(), _iterable_b.begin(), _iterable_b.end(), _a, _b, _result_selector };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_JOIN_WHERE_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/loop.hpp",
    "content": "#pragma once\n\n#ifndef LZ_LOOP_ITERALBE_HPP\n#define LZ_LOOP_ITERALBE_HPP\n\n#include <Lz/detail/iterators/loop.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class, bool /* is inf */>\nclass loop_iterable;\n\ntemplate<class Iterable>\nclass loop_iterable<Iterable, false /* is inf loop */> : public lazy_view {\npublic:\n    using iterator = loop_iterator<maybe_owned<Iterable>, false>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<iter_t<Iterable>, sentinel_t<Iterable>>::value;\n\n    maybe_owned<Iterable> _iterable{};\n    typename iterator::difference_type _amount{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr loop_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr loop_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr loop_iterable(I&& iterable, const typename iterator::difference_type amount) :\n        _iterable{ std::forward<I>(iterable) },\n        _amount{ amount } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>(_amount) * static_cast<size_t>(lz::size(_iterable));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_sized<I>::value, size_t> size() const {\n        return static_cast<size_t>(_amount) * static_cast<size_t>(lz::size(_iterable));\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        auto first = _iterable.begin();\n        if (_amount == 0 || first == _iterable.end()) {\n            first = _iterable.end();\n            return { _iterable, first, 0 };\n        }\n        return { _iterable, first, _amount - 1 };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _iterable.end(), 0 };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return iterator{ _iterable, _iterable.end(), 0 };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD constexpr enable_if_t<R, default_sentinel_t> end() const {\n        return {};\n    }\n\n#endif\n};\n\ntemplate<class Iterable>\nclass loop_iterable<Iterable, true /* is inf loop */> : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n\npublic:\n    using iterator = loop_iterator<maybe_owned<Iterable>, true>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr loop_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr loop_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    explicit constexpr loop_iterable(I&& iterable) : _iterable{ std::forward<I>(iterable) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _iterable.begin() };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const {\n        return {};\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_LOOP_ITERALBE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/map.hpp",
    "content": "#pragma once\n\n#ifndef LZ_MAP_ITERABLE\n#define LZ_MAP_ITERABLE\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/map.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class UnaryOp>\nclass map_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    func_container<UnaryOp> _unary_op{};\n\n    using iter = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n\npublic:\n    using iterator = map_iterator<iter, sent, func_container<UnaryOp>>;\n    using sentinel = sent;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<iter, sent>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr map_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<UnaryOp>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable),\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<UnaryOp>::value>>\n    constexpr map_iterable() noexcept(std::is_nothrow_default_constructible<maybe_owned<Iterable>>::value &&\n                                      std::is_nothrow_default_constructible<UnaryOp>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr map_iterable(I&& iterable, UnaryOp unary_op) :\n        _iterable{ std::forward<I>(iterable) },\n        _unary_op{ std::move(unary_op) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_sized<I>::value, size_t> size() const noexcept {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable.begin(), _unary_op };\n    }\n\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable.end(), _unary_op };\n        }\n        else {\n            return _iterable.end();\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable.end(), _unary_op };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, sentinel> end() const {\n        return _iterable.end();\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/pairwise.hpp",
    "content": "#pragma once\n\n#ifndef LZ_PAIRWISE_ITERABLE_HPP\n#define LZ_PAIRWISE_ITERABLE_HPP\n\n#include <Lz/detail/iterators/pairwise.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/detail/traits/conditional.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/procs/size.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable>\nclass pairwise_iterable : public lazy_view {\npublic:\n    using iterator = conditional_t<!is_ra<iter_t<Iterable>>::value, bidi_pairwise_iterator<maybe_owned<Iterable>>,\n                                   ra_pairwise_iterator<maybe_owned<Iterable>>>;\n    using value_type = typename iterator::value_type;\n    using const_iterator = iterator;\n\nprivate:\n    static constexpr auto return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || has_sentinel<Iterable>::value;\n\n    using diff = typename iterator::difference_type;\n    using it_cat = typename iterator::iterator_category;\n\n    maybe_owned<Iterable> _iterable{};\n    diff _pair_size{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr pairwise_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr pairwise_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 pairwise_iterable(I&& iterable, const diff pair_size) :\n        _iterable{ std::forward<I>(iterable) },\n        _pair_size{ pair_size } {\n        LZ_ASSERT(_pair_size != 0, \"Size must be greater than zero\");\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        const auto it_size = lz::size(_iterable);\n        return it_size < static_cast<size_t>(_pair_size) ? 0 : it_size - static_cast<size_t>(_pair_size) + 1;\n    }\n\n#else\n\n    template<bool S = is_sized<Iterable>::value>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<S, size_t> size() const {\n        const auto it_size = lz::size(_iterable);\n        return it_size < static_cast<size_t>(_pair_size) ? 0 : it_size - static_cast<size_t>(_pair_size) + 1;\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr iterator begin() const {\n        if constexpr (is_ra_tag_v<it_cat>) {\n            return { _iterable, _iterable.begin(), _pair_size };\n        }\n        else if constexpr (is_sized_v<Iterable> && !has_sentinel_v<Iterable>) {\n            if (lz::ssize(_iterable) < _pair_size) {\n                return { _iterable, _iterable.end(), _pair_size };\n            }\n            return { _iterable, _iterable.begin(), _pair_size };\n        }\n        else if constexpr (is_bidi_tag_v<it_cat> && !has_sentinel_v<Iterable>) {\n            diff count = 0;\n            auto it = _iterable.end();\n            for (; count < _pair_size && it != _iterable.begin(); --it, ++count) {\n            }\n            if (count < _pair_size) {\n                return { _iterable, _iterable.end(), _pair_size };\n            }\n            return { _iterable, _iterable.begin(), _pair_size };\n        }\n        else {\n            diff count = 0;\n            auto it = _iterable.begin();\n            for (; count < _pair_size && it != _iterable.end(); ++it, ++count) {\n            }\n            if (it == _iterable.end() && count < _pair_size) {\n                return { _iterable, it, _pair_size };\n            }\n            return { _iterable, _iterable.begin(), _pair_size };\n        }\n    }\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (return_sentinel) {\n            return default_sentinel;\n        }\n        else if constexpr (is_ra_tag_v<it_cat>) {\n            const auto end_offset = detail::min_variadic2(_pair_size - 1, _iterable.end() - _iterable.begin());\n            return iterator{ _iterable, _iterable.end() - end_offset, _pair_size };\n        }\n        else {\n            return iterator{ _iterable, _iterable.end(), _pair_size };\n        }\n    }\n\n#else\n\n    template<class Cat = it_cat>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_ra_tag<Cat>::value, iterator> begin() const {\n        return { _iterable, _iterable.begin(), _pair_size };\n    }\n\n    // clang-format off\n\n    template<class I = Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14\n    enable_if_t<!is_ra_tag<it_cat>::value && is_sized<I>::value && !has_sentinel<I>::value, iterator>\n    begin() const {\n        if (lz::ssize(_iterable) < _pair_size) {\n            return { _iterable, _iterable.end(), _pair_size };\n        }\n        return { _iterable, _iterable.begin(), _pair_size };\n    }\n\n    template<class Cat = it_cat>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14\n    enable_if_t<\n        !is_ra_tag<Cat>::value && !is_sized<Iterable>::value && !has_sentinel<Iterable>::value && is_bidi_tag<Cat>::value , iterator>\n    begin() const {\n        diff count = 0;\n        auto it = _iterable.end();\n        for (; count < _pair_size && it != _iterable.begin(); --it, ++count) {\n        }\n        if (count < _pair_size) {\n            return { _iterable, _iterable.end(), _pair_size };\n        }\n        return { _iterable, it, _pair_size };\n    }\n\n    // clang-format on !ra && !sized && (has_sentinel || !bidi)\n\n    template<class Cat = it_cat>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<\n        !is_bidi_tag<Cat>::value || (!is_ra_tag<Cat>::value && has_sentinel<Iterable>::value), iterator>\n    begin() const {\n        diff count = 0;\n        auto it = _iterable.begin();\n        for (; count < _pair_size && it != _iterable.end(); ++it, ++count) {\n        }\n        if (it == _iterable.end() && count < _pair_size) {\n            return { _iterable, it, _pair_size };\n        }\n        return { _iterable, _iterable.begin(), _pair_size };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, default_sentinel_t> end() const {\n        return default_sentinel;\n    }\n\n    template<class Cat = it_cat>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_ra_tag<Cat>::value && !return_sentinel, iterator> end() const {\n        const auto end_offset = detail::min_variadic2(_pair_size - 1, _iterable.end() - _iterable.begin());\n        return iterator{ _iterable, _iterable.end() - end_offset, _pair_size };\n    }\n\n    template<class Cat = it_cat>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_ra_tag<Cat>::value && !return_sentinel, iterator> end() const {\n        return iterator{ _iterable, _iterable.end(), _pair_size };\n    }\n\n#endif\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/random.hpp",
    "content": "#pragma once\n\n#ifndef LZ_RANDOM_ITERABLE_HPP\n#define LZ_RANDOM_ITERABLE_HPP\n\n#include <Lz/detail/iterators/random.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Arithmetic, class Distribution, class Generator, bool UseSentinel>\nclass random_iterable : public lazy_view {\n    Distribution _distribution{};\n    Generator* _generator{ nullptr };\n    ptrdiff_t _current{};\n\npublic:\n    constexpr random_iterable(const random_iterable&) = default;\n    LZ_CONSTEXPR_CXX_14 random_iterable& operator=(const random_iterable&) = default;\n\n    using iterator = random_iterator<Arithmetic, Distribution, Generator, UseSentinel>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr random_iterable()\n        requires(std::default_initializable<Distribution>)\n    = default;\n\n#else\n\n    template<class D = Distribution, class = enable_if_t<std::is_default_constructible<D>::value>>\n    constexpr random_iterable() noexcept(std::is_nothrow_default_constructible<D>::value) {\n    }\n\n#endif\n\n    constexpr random_iterable(const Distribution& distribution, Generator& generator, const ptrdiff_t current) :\n        _distribution{ distribution },\n        _generator{ &generator },\n        _current{ current } {\n    }\n\n    LZ_NODISCARD constexpr size_t size() const noexcept{\n        return static_cast<size_t>(_current);\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return { _distribution, *_generator, _current };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!UseSentinel) {\n            return iterator{ _distribution, *_generator, 0 };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool B = UseSentinel>\n    LZ_NODISCARD constexpr enable_if_t<!B, iterator> end() const {\n        return { _distribution, *_generator, 0 };\n    }\n\n    template<bool B = UseSentinel>\n    LZ_NODISCARD constexpr enable_if_t<B, default_sentinel_t> end() const {\n        return {};\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_RANDOM_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/range.hpp",
    "content": "#pragma once\n\n#ifndef LZ_RANGE_ITERABLE_HPP\n#define LZ_RANGE_ITERABLE_HPP\n\n#include <Lz/detail/iterators/range.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Arithmetic, bool /* step wise */>\nclass range_iterable;\n\ntemplate<class Arithmetic>\nclass range_iterable<Arithmetic, true /* step wise */> : public lazy_view {\n    Arithmetic _start{};\n    Arithmetic _end{};\n    Arithmetic _step{};\n\npublic:\n    using iterator = range_iterator<Arithmetic, true>;\n    using const_iterator = iterator;\n    using reverse_iterator = std::reverse_iterator<iterator>;\n    using value_type = typename iterator::value_type;\n\n    constexpr range_iterable() noexcept = default;\n\n#if defined(LZ_USE_DEBUG_ASSERTIONS) && defined(LZ_HAS_CXX_17)\n\n    LZ_CONSTEXPR_CXX_14 range_iterable(const Arithmetic start, const Arithmetic end, const Arithmetic step) noexcept :\n        _start{ start },\n        _end{ end },\n        _step{ step } {\n        if constexpr (!std::is_floating_point_v<Arithmetic>) {\n            LZ_ASSERT(step > std::numeric_limits<Arithmetic>::min(), \"Step must be larger than the minimum representable value\");\n            // The behavior (of std::abs) is undefined if the result cannot be represented by the return type\n            LZ_ASSERT(std::abs(_step) > 0, \"Step cannot be zero\");\n        }\n    }\n\n#elif defined(LZ_USE_DEBUG_ASSERTIONS)\n\n    template<class A = Arithmetic, enable_if_t<!std::is_floating_point<A>::value, int> = 0>\n    LZ_CONSTEXPR_CXX_14 range_iterable(const Arithmetic start, const Arithmetic end, const Arithmetic step) noexcept :\n        _start{ start },\n        _end{ end },\n        _step{ step } {\n        LZ_ASSERT(step > std::numeric_limits<Arithmetic>::min(), \"Step must be larger than the minimum representable value\");\n        // The behavior (of std::abs) is undefined if the result cannot be represented by the return type\n        LZ_ASSERT(std::abs(_step) > 0, \"Step cannot be zero\");\n    }\n\n    template<class A = Arithmetic, enable_if_t<std::is_floating_point<A>::value, int> = 0>\n    LZ_CONSTEXPR_CXX_14 range_iterable(const Arithmetic start, const Arithmetic end, const Arithmetic step) noexcept :\n        _start{ start },\n        _end{ end },\n        _step{ step } {\n    }\n\n#else\n\n    LZ_CONSTEXPR_CXX_14 range_iterable(const Arithmetic start, const Arithmetic end, const Arithmetic step) noexcept :\n        _start{ start },\n        _end{ end },\n        _step{ step } {\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr size_t size() const noexcept {\n        if constexpr (std::is_floating_point_v<Arithmetic>) {\n            const auto total_length = (_end - _start) / static_cast<Arithmetic>(_step);\n            const auto int_part = static_cast<size_t>(total_length);\n            return (total_length > static_cast<Arithmetic>(int_part)) ? int_part + 1 : int_part;\n        }\n        else {\n            const auto diff = _end - _start;\n            return static_cast<size_t>(std::abs((diff + (_step > 0 ? _step - 1 : _step + 1)) / _step));\n        }\n    }\n\n#else\n\n    template<class A = Arithmetic>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_floating_point<A>::value, size_t> size() const noexcept {\n        const auto total_length = (_end - _start) / static_cast<Arithmetic>(_step);\n        const auto int_part = static_cast<size_t>(total_length);\n        return (total_length > static_cast<A>(int_part)) ? int_part + 1 : int_part;\n    }\n\n    template<class A = Arithmetic>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!std::is_floating_point<A>::value, size_t> size() const noexcept {\n        const auto diff = _end - _start;\n        return static_cast<size_t>(std::abs((diff + (_step > 0 ? _step - 1 : _step + 1)) / _step));\n    }\n\n#endif\n\n    LZ_NODISCARD constexpr iterator begin() const noexcept {\n        return { _start, _step };\n    }\n\n    LZ_NODISCARD constexpr iterator end() const noexcept {\n        return { _start + static_cast<Arithmetic>(size()) * _step, _step };\n    }\n};\n\ntemplate<class Arithmetic>\nclass range_iterable<Arithmetic, false /* step wise */> : public lazy_view {\n    Arithmetic _start{};\n    Arithmetic _end{};\n\npublic:\n    using iterator = range_iterator<Arithmetic, false>;\n    using const_iterator = iterator;\n    using reverse_iterator = std::reverse_iterator<iterator>;\n    using value_type = typename iterator::value_type;\n\n    constexpr range_iterable() noexcept = default;\n\n    LZ_CONSTEXPR_CXX_14 range_iterable(const Arithmetic start, const Arithmetic end) noexcept : _start{ start }, _end{ end } {\n        LZ_ASSERT(start <= end, \"Start must be less than or equal to end\");\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 size_t size() const noexcept {\n        if constexpr (std::is_floating_point_v<Arithmetic>) {\n            const auto total_length = (_end - _start);\n            const auto int_part = static_cast<size_t>(total_length);\n            return (total_length > static_cast<Arithmetic>(int_part)) ? int_part + 1 : int_part;\n        }\n        else {\n            return static_cast<size_t>(_end - _start);\n        }\n    }\n\n#else\n\n    template<class A = Arithmetic>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_floating_point<A>::value, size_t> size() const noexcept {\n        const auto total_length = (_end - _start);\n        const auto int_part = static_cast<size_t>(total_length);\n        return (total_length > static_cast<Arithmetic>(int_part)) ? int_part + 1 : int_part;\n    }\n\n    template<class A = Arithmetic>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!std::is_floating_point<A>::value, size_t> size() const noexcept {\n        return static_cast<size_t>(_end - _start);\n    }\n\n#endif\n\n    LZ_NODISCARD constexpr iterator begin() const noexcept {\n        return iterator{ _start };\n    }\n\n    LZ_NODISCARD constexpr iterator end() const noexcept {\n        return iterator{ _start + static_cast<Arithmetic>(size()) };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_RANGE_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/regex_split.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REGEX_SPLIT_ITERABLE_HPP\n#define LZ_REGEX_SPLIT_ITERABLE_HPP\n\n#include <Lz/detail/iterators/regex_split.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class RegexTokenIter, class RegexTokenSentinel>\nclass regex_split_iterable : public lazy_view {\n    RegexTokenIter _begin{};\n    RegexTokenSentinel _end{};\n\npublic:\n    using iterator = regex_split_iterator<RegexTokenIter, RegexTokenSentinel>;\n    using const_iterator = iterator;\n    using value_type = typename RegexTokenIter::value_type;\n    using sentinel = typename iterator::sentinel;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr regex_split_iterable()\n        requires(std::default_initializable<RegexTokenIter> && std::default_initializable<RegexTokenSentinel>)\n    = default;\n\n#else\n\n    template<class I = RegexTokenIter, class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                           std::is_default_constructible<RegexTokenSentinel>::value>>\n    constexpr regex_split_iterable() noexcept(std::is_nothrow_default_constructible<RegexTokenIter>::value &&\n                                              std::is_nothrow_default_constructible<RegexTokenSentinel>::value) {\n    }\n\n#endif\n\n    constexpr regex_split_iterable(RegexTokenIter begin, RegexTokenSentinel end) :\n        _begin{ std::move(begin) },\n        _end{ std::move(end) } {\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return { _begin, _end };\n    }\n\n    LZ_NODISCARD constexpr sentinel end() const {\n        return sentinel{ _end };\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/repeat.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REPEAT_ITERABLE_HPP\n#define LZ_REPEAT_ITERABLE_HPP\n\n#include <Lz/detail/iterators/repeat.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\n// todo add extra tests for references\nnamespace lz {\nnamespace detail {\n\ntemplate<class, bool /* is inf */>\nclass repeat_iterable;\n\ntemplate<class T>\nclass repeat_iterable<T, false> : public lazy_view {\n    T _value{};\n    ptrdiff_t _amount{};\n\npublic:\n    using iterator = repeat_iterator<T, false>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr repeat_iterable()\n        requires(std::default_initializable<T>)\n    = default;\n\n#else\n\n    template<class U = T, class = enable_if_t<std::is_default_constructible<U>::value>>\n    constexpr repeat_iterable() noexcept(std::is_nothrow_default_constructible<U>::value) {\n    }\n\n#endif\n\n    constexpr repeat_iterable(T value, const ptrdiff_t amount) : _value{ std::forward<T>(value) }, _amount{ amount } {\n    }\n\n    LZ_NODISCARD constexpr size_t size() const noexcept {\n        return static_cast<size_t>(_amount);\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return { _value, _amount };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n\ntemplate<class T>\nclass repeat_iterable<T, true> : public lazy_view {\n    T _value{};\n\npublic:\n    using iterator = repeat_iterator<T, true>;\n    using const_iterator = iterator;\n    using value_type = T;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr repeat_iterable()\n        requires(std::default_initializable<T>)\n    = default;\n\n#else\n\n    template<class U = T, class = enable_if_t<std::is_default_constructible<U>::value>>\n    constexpr repeat_iterable() noexcept(std::is_nothrow_default_constructible<U>::value) {\n    }\n\n#endif\n\n    explicit constexpr repeat_iterable(T value) : _value{ std::forward<T>(value) } {\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return iterator{ _value };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_REPEAT_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/reverse.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REVERSE_ITERABLE_HPP\n#define LZ_REVERSE_ITERABLE_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterators/cached_reverse.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/conditional.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, bool Cached>\nclass reverse_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n\n    using inner_iter = iter_t<Iterable>;\n    using inner_sent = sentinel_t<Iterable>;\n\npublic:\n    using iterator = conditional_t<Cached, cached_reverse_iterator<inner_iter>, std::reverse_iterator<inner_iter>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr reverse_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr reverse_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n    static_assert((has_sentinel<Iterable>::value && is_ra<iterator>::value) || !has_sentinel<Iterable>::value,\n                  \"Cannot reverse non-random access iterable with a sentinel. Try to use lz::common first.\");\n\n    template<class I>\n    explicit constexpr reverse_iterable(I&& iterable) : _iterable{ std::forward<I>(iterable) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_sized<I>::value, size_t> size() const {\n        return static_cast<size_t>(lz::size(_iterable));\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr iterator begin() const {\n        if constexpr (Cached) {\n            if constexpr (has_sentinel_v<Iterable>) {\n                auto last = _iterable.begin() + (_iterable.end() - _iterable.begin());\n                return { last, _iterable.begin(), last };\n            }\n            else {\n                return { _iterable.end(), _iterable.begin(), _iterable.end() };\n            }\n        }\n        else if constexpr (has_sentinel_v<Iterable>) {\n            return iterator{ _iterable.begin() + (_iterable.end() - _iterable.begin()) };\n        }\n        else {\n            return iterator{ _iterable.end() };\n        }\n    }\n\n    [[nodiscard]] constexpr iterator end() const {\n        if constexpr (Cached) {\n            if constexpr (has_sentinel_v<Iterable>) {\n                auto last = _iterable.begin() + (_iterable.end() - _iterable.begin());\n                return { _iterable.begin(), _iterable.begin(), last };\n            }\n            else {\n                return { _iterable.begin(), _iterable.begin(), _iterable.end() };\n            }\n        }\n        else {\n            return iterator{ _iterable.begin() };\n        }\n    }\n\n#else\n\n    template<bool C = Cached>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<C && has_sentinel<Iterable>::value, iterator> begin() const {\n        auto last = _iterable.begin() + (_iterable.end() - _iterable.begin());\n        return { last, _iterable.begin(), last };\n    }\n\n    template<bool C = Cached>\n    LZ_NODISCARD constexpr enable_if_t<C && !has_sentinel<Iterable>::value, iterator> begin() const {\n        return { _iterable.end(), _iterable.begin(), _iterable.end() };\n    }\n\n    template<bool C = Cached>\n    LZ_NODISCARD constexpr enable_if_t<!C && has_sentinel<Iterable>::value, iterator> begin() const {\n        return { _iterable.begin() + (_iterable.end() - _iterable.begin()) };\n    }\n\n    template<bool C = Cached>\n    LZ_NODISCARD constexpr enable_if_t<!C && !has_sentinel<Iterable>::value, iterator> begin() const {\n        return iterator{ _iterable.end() };\n    }\n\n    // Using constexpr 17 here because cxx 17 has constexpr reverse_iterator\n    template<bool C = Cached>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_17 enable_if_t<!C, iterator> end() const {\n        return iterator{ _iterable.begin() };\n    }\n\n    template<bool C = Cached>\n    LZ_NODISCARD constexpr enable_if_t<C, iterator> end() const {\n        return { _iterable.begin(), _iterable.begin(), _iterable.end() };\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_REVERSE_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/rotate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ROTATE_ITERABLE_HPP\n#define LZ_ROTATE_ITERABLE_HPP\n\n#include <Lz/detail/procs/next_fast.hpp>\n#include <Lz/detail/iterators/rotate.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable>\nclass rotate_iterable : public lazy_view {\n    using inner_iter = iter_t<Iterable>;\n    using diff_t = diff_type<inner_iter>;\n\n    maybe_owned<Iterable> _iterable{};\n    inner_iter _start_iter{};\n    diff_t _start_index{};\n\npublic:\n    using iterator = rotate_iterator<maybe_owned<Iterable>>;\n    using sentinel = typename iterator::sentinel;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<inner_iter, sentinel_t<Iterable>>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr rotate_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<inner_iter>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable),\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<inner_iter>::value>>\n    constexpr rotate_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<inner_iter>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 rotate_iterable(I&& iterable, const diff_t start) :\n        _iterable{ std::forward<I>(iterable) },\n        _start_iter{ next_fast_safe(_iterable, start) },\n        _start_index{ start } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        const auto s = lz::size(_iterable);\n        if (s == 0) {\n            return 0;\n        }\n        return static_cast<size_t>(_start_index) == s ? 0 : s;\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_sized<I>::value, size_t> size() const {\n        const auto s = lz::size(_iterable);\n        if (s == 0) {\n            return 0;\n        }\n        return static_cast<size_t>(_start_index) == s ? 0 : s;\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _start_iter, _start_iter == _iterable.end() ? _start_index : 0 };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _start_iter, _start_iter == _iterable.end() ? _start_index : lz::eager_ssize(_iterable) };\n        }\n        else {\n            return sentinel{ _start_iter };\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable, _start_iter, _start_iter == _iterable.end() ? _start_index : lz::eager_ssize(_iterable) };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, sentinel> end() const {\n        return sentinel{ _start_iter };\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_ROTATE_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/split.hpp",
    "content": "#pragma once\n\n#ifndef LZ_SPLIT_ITERABLE_HPP\n#define LZ_SPLIT_ITERABLE_HPP\n\n#include <Lz/basic_iterable.hpp>\n#include <Lz/detail/iterators/split.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class ValueType, class Iterable, class Iterable2, class = void>\nclass split_iterable;\n\ntemplate<class ValueType, class Iterable, class Iterable2>\nclass split_iterable<ValueType, Iterable, Iterable2, enable_if_t<is_iterable<Iterable2>::value>> : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    maybe_owned<Iterable2> _delimiter{};\n\npublic:\n    using const_iterator =\n        split_iterator<ValueType, iter_t<Iterable>, sentinel_t<Iterable>, iter_t<Iterable2>, sentinel_t<Iterable2>>;\n    using iterator = const_iterator;\n    using value_type = basic_iterable<iterator>;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr split_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<maybe_owned<Iterable2>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                                std::is_default_constructible<maybe_owned<Iterable2>>::value>>\n    constexpr split_iterable() noexcept(std::is_nothrow_default_constructible<maybe_owned<Iterable>>::value &&\n                                        std::is_nothrow_default_constructible<maybe_owned<Iterable2>>::value) {\n    }\n\n#endif\n\n    template<class I, class I2>\n    constexpr split_iterable(I&& iterable, I2&& delimiter) :\n        _iterable{ std::forward<I>(iterable) },\n        _delimiter{ std::forward<I2>(delimiter) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable.begin(), _iterable.end(), _delimiter.begin(), _delimiter.end() };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n\ntemplate<class ValueType, class Iterable, class T>\nclass split_iterable<ValueType, Iterable, T, enable_if_t<!is_iterable<T>::value>> : public lazy_view {\n    maybe_owned<Iterable> _iterable;\n    T _delimiter{};\n\npublic:\n    using const_iterator = split_single_iterator<ValueType, iter_t<Iterable>, sentinel_t<Iterable>, T>;\n    using iterator = const_iterator;\n    using value_type = basic_iterable<iterator>;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr split_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<T>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable),\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<T>::value>>\n    constexpr split_iterable() noexcept(std::is_nothrow_default_constructible<maybe_owned<Iterable>>::value &&\n                                        std::is_nothrow_default_constructible<T>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr split_iterable(I&& iterable, T delimiter) :\n        _iterable{ std::forward<I>(iterable) },\n        _delimiter{ std::move(delimiter) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable.begin(), _iterable.end(), _delimiter };\n    }\n\n    LZ_NODISCARD constexpr default_sentinel_t end() const noexcept {\n        return {};\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_SPLIT_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/take.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_ITERABLE_HPP\n#define LZ_TAKE_ITERABLE_HPP\n\n#include <Lz/detail/iterators/take.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/detail/procs/next_fast.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/traits/lazy_view.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class = void>\nclass take_iterable;\n\ntemplate<class Iterator>\nclass take_iterable<Iterator, enable_if_t<!is_iterable<Iterator>::value>> : public lazy_view {\npublic:\n    using iterator = n_take_iterator<Iterator>;\n    using const_iterator = Iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    Iterator _iterator{};\n    typename iterator::difference_type _n{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr take_iterable()\n        requires(std::default_initializable<Iterator>)\n    = default;\n\n#else\n\n    template<class I = Iterator, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr take_iterable() {\n    }\n\n#endif\n\n    constexpr take_iterable(Iterator it, const typename iterator::difference_type n) : _iterator{ std::move(it) }, _n{ n } {\n    }\n\n    LZ_NODISCARD constexpr size_t size() const noexcept {\n        return static_cast<size_t>(_n);\n    }\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return { _iterator, _n };\n    }\n\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (is_bidi_tag_v<typename iterator::iterator_category>) {\n            return iterator{ std::next(_iterator, _n), 0 };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD constexpr enable_if_t<is_bidi_tag<I>::value, iterator> end() const {\n        return { std::next(_iterator, _n), 0 };\n    }\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD constexpr enable_if_t<!is_bidi_tag<I>::value, default_sentinel_t> end() const {\n        return {};\n    }\n\n#endif\n};\n\ntemplate<class Iterable>\nclass take_iterable<Iterable, enable_if_t<is_iterable<Iterable>::value>> : public lazy_view {\npublic:\n    using iterator = iter_t<Iterable>;\n    using const_iterator = iterator;\n    using value_type = val_t<iterator>;\n\nprivate:\n    maybe_owned<Iterable> _iterable{};\n    diff_type<iterator> _n{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr take_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr take_iterable() {\n    }\n\n#endif\n\n    template<class I>\n    constexpr take_iterable(I&& iterable, const diff_type<iterator> n) : _iterable{ std::forward<I>(iterable) }, _n{ n } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return detail::min_variadic2(static_cast<size_t>(_n), static_cast<size_t>(lz::size(_iterable)));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_sized<I>::value, size_t> size() const {\n        return detail::min_variadic2(static_cast<size_t>(_n), static_cast<size_t>(lz::size(_iterable)));\n    }\n\n#endif\n\n    LZ_NODISCARD constexpr iterator begin() const {\n        return _iterable.begin();\n    }\n\n    LZ_NODISCARD constexpr iterator end() const {\n        return next_fast_safe(_iterable, static_cast<diff_type<iterator>>(_n));\n    }\n};\n} // namespace detail\n} // namespace lz\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/take_every.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_EVERY_ITERABLE_HPP\n#define LZ_TAKE_EVERY_ITERABLE_HPP\n\n#include <Lz/detail/iterators/take_every.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/detail/procs/next_fast.hpp>\n#include <Lz/procs/eager_size.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\n\n// next_fast_safe: check whether it's faster to go forward or backward when incrementing n steps\n// Check whether n is within bounds. Only relevant for sized (requesting size is then O(1)).\n// Only bidirectional iterators supported currently\n\n#ifdef LZ_HAS_CXX_17\n\n#else\n\n#endif\n\ntemplate<class Iterable>\nclass take_every_iterable : public lazy_view {\n    using iter = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n\npublic:\n    using iterator = take_every_iterator<maybe_owned<Iterable>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    using diff = typename iterator::difference_type;\n\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<iter_t<Iterable>, sent>::value;\n\n    maybe_owned<Iterable> _iterable{};\n    diff _offset{};\n    diff _start{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr take_every_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr take_every_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 take_every_iterable(I&& iterable, const diff offset, const diff start) :\n        _iterable{ std::forward<I>(iterable) },\n        _offset{ offset },\n        _start{ start } {\n        LZ_ASSERT(_offset != 0, \"Cannot increment by 0\");\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        const auto cur_size = static_cast<size_t>(lz::size(_iterable));\n        if (static_cast<diff>(cur_size) - _start <= 0) {\n            return 0;\n        }\n\n        const auto actual_size = detail::max_variadic2(diff{ 1 }, static_cast<diff>(cur_size) - _start);\n        const auto rem = actual_size % _offset;\n        const auto quot = actual_size / _offset;\n\n        return static_cast<size_t>(quot) + static_cast<size_t>(rem == 0 ? 0 : 1);\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_sized<I>::value, size_t> size() const {\n        const auto cur_size = static_cast<size_t>(lz::size(_iterable));\n        if (static_cast<diff>(cur_size) - _start <= 0) {\n            return 0;\n        }\n\n        const auto actual_size = detail::max_variadic2(diff{ 1 }, static_cast<diff>(cur_size) - _start);\n        const auto rem = actual_size % _offset;\n        const auto quot = actual_size / _offset;\n\n        return static_cast<size_t>(quot) + static_cast<size_t>(rem == 0 ? 0 : 1);\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr iterator begin() const {\n        auto start_pos = next_fast_safe(_iterable, _start);\n        if constexpr (is_ra_tag_v<typename iterator::iterator_category>) {\n            return { _iterable, start_pos, _offset };\n        }\n        else if constexpr (is_bidi_tag_v<typename iterator::iterator_category>) {\n            return { start_pos, _iterable.end(), _offset, _start };\n        }\n        else {\n            return { start_pos, _iterable.end(), _offset };\n        }\n    }\n\n#else\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_bidi_tag<I>::value, iterator> begin() const {\n        auto start_pos = next_fast_safe(_iterable, _start);\n        return { start_pos, _iterable.end(), _offset };\n    }\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_same<I, std::bidirectional_iterator_tag>::value, iterator>\n    begin() const {\n        auto start_pos = next_fast_safe(_iterable, _start);\n        return { start_pos, _iterable.end(), _offset, _start };\n    }\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_ra_tag<I>::value, iterator> begin() const {\n        // random access iterator can go both ways O(1), no need to use next_fast_safe\n        auto start_pos = next_fast_safe(_iterable, _start);\n        return { _iterable, start_pos, _offset };\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (return_sentinel) {\n            return lz::default_sentinel;\n        }\n        else if constexpr (!is_ra_tag_v<typename iterator::iterator_category>) {\n            return iterator{ _iterable.end(), _iterable.end(), _offset, lz::eager_ssize(_iterable) - _start };\n        }\n        else {\n            return iterator{ _iterable, _iterable.end(), _offset };\n        }\n    }\n\n#else\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_ra_tag<I>::value && !is_sentinel<iter, sent>::value, iterator> end() const {\n        return { _iterable, _iterable.end(), _offset };\n    }\n\n    template<class I = typename iterator::iterator_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14\n        // std::is_same is not the same as is_bidi_tag, explicitly check for bidirectional iterator tag\n        enable_if_t<std::is_same<I, std::bidirectional_iterator_tag>::value && !is_sentinel<iter, sent>::value, iterator>\n        end() const {\n        return { _iterable.end(), _iterable.end(), _offset, lz::eager_ssize(_iterable) - _start };\n    }\n\n    template<bool R = return_sentinel> // checks for not bidirectional or if is sentinel\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, default_sentinel_t> end() const noexcept {\n        return {};\n    }\n\n#endif\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/take_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_WHILE_ITERABLE_HPP\n#define LZ_TAKE_WHILE_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/take_while.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class UnaryPredicate>\nclass take_while_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    func_container<UnaryPredicate> _unary_predicate{};\n\n    using it = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n\npublic:\n    using iterator = take_while_iterator<maybe_owned<Iterable>, func_container<UnaryPredicate>>;\n    using sentinel = typename iterator::sentinel;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<it, sent>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr take_while_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<UnaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable),\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<UnaryPredicate>::value>>\n    constexpr take_while_iterable() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<UnaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr take_while_iterable(I&& iterable, UnaryPredicate unary_predicate) :\n        _iterable{ std::forward<I>(iterable) },\n        _unary_predicate{ std::move(unary_predicate) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _unary_predicate, std::true_type{} };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _unary_predicate, std::false_type {} };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { _iterable, _unary_predicate, std::false_type{} };\n    }\n\n    template<bool R = return_sentinel>\n    constexpr enable_if_t<R, default_sentinel_t> end() const noexcept {\n        return default_sentinel;\n    }\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterables/unique.hpp",
    "content": "#pragma once\n\n#ifndef LZ_UNIQUE_ITERABLE_HPP\n#define LZ_UNIQUE_ITERABLE_HPP\n\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterators/unique.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class BinaryPredicate>\nclass unique_iterable : public lazy_view {\n    maybe_owned<Iterable> _iterable{};\n    func_container<BinaryPredicate> _predicate{};\n\n    using it = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n\npublic:\n    using iterator = unique_iterator<maybe_owned<Iterable>, func_container<BinaryPredicate>>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || is_sentinel<it, sent>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr unique_iterable()\n        requires(std::default_initializable<maybe_owned<Iterable>> && std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterable), class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                                std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr unique_iterable() noexcept(std::is_nothrow_default_constructible<maybe_owned<Iterable>>::value &&\n                                         std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr unique_iterable(I&& iterable, BinaryPredicate compare) :\n        _iterable{ std::forward<I>(iterable) },\n        _predicate{ std::move(compare) } {\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { _iterable, _iterable.begin(), _predicate };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ _iterable, _iterable.end(), _predicate };\n        }\n        else {\n            return lz::default_sentinel;\n        }\n    }\n\n#else\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return iterator{ _iterable, _iterable.end(), _predicate };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD constexpr enable_if_t<R, default_sentinel_t> end() const noexcept {\n        return default_sentinel;\n    }\n\n#endif\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_UNIQUE_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/zip.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ZIP_ITERABLE_HPP\n#define LZ_ZIP_ITERABLE_HPP\n\n#include <Lz/detail/iterators/zip.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\n#ifdef LZ_HAS_CONCEPTS\n#include <Lz/traits/concepts.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\ntemplate<class... Iterables>\nclass zip_iterable : public lazy_view {\n    using iter_tuple = std::tuple<iter_t<Iterables>...>;\n    using sentinel_tuple = std::tuple<sentinel_t<Iterables>...>;\n\n    std::tuple<maybe_owned<Iterables>...> _iterables{};\n\n    template<size_t... I>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 size_t size(index_sequence<I...>) const {\n        return min_variadic(static_cast<size_t>(lz::size(std::get<I>(_iterables)))...);\n    }\n\n    template<class Iterable2, size_t... Is>\n    static zip_iterable<remove_ref_t<Iterable2>, Iterables...>\n    concat_iterables(Iterable2&& iterable2, zip_iterable<Iterables...> zipper, index_sequence<Is...>) {\n        return { std::forward<Iterable2>(iterable2), std::move(std::get<Is>(zipper._iterables))... };\n    }\n\npublic:\n    using iterator = zip_iterator<maybe_owned<Iterables>...>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\nprivate:\n    using seq = make_index_sequence<sizeof...(Iterables)>;\n    using sentinel = typename iterator::sentinel;\n\n    static constexpr bool return_sentinel =\n        !is_bidi_tag<typename iterator::iterator_category>::value || disjunction<has_sentinel<Iterables>...>::value;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr zip_iterable()\n        requires(std::default_initializable<maybe_owned<Iterables>> && ...)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterables), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr zip_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class... Is>\n    LZ_CONSTEXPR_CXX_14 zip_iterable(Is&&... iterables) : _iterables{ std::forward<Is>(iterables)... } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<maybe_owned<Iterables>> && ...)\n    {\n        return size(seq{});\n    }\n\n#else\n\n    template<bool S = conjunction<is_sized<Iterables>...>::value>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<S, size_t> size() const {\n        return size(seq{});\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr iterator begin() const {\n        return { begin_tuple(_iterables) };\n    }\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (!return_sentinel) {\n            return iterator{ smallest_end_tuple(_iterables, seq{}) };\n        }\n        else {\n            return typename iterator::sentinel{ end_tuple(_iterables) };\n        }\n    }\n\n#else\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iterator begin() const {\n        return { begin_tuple(_iterables) };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!R, iterator> end() const {\n        return { smallest_end_tuple(_iterables, seq{}) };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, sentinel> end() const {\n        return sentinel{ end_tuple(_iterables) };\n    }\n\n#endif\n\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 friend zip_iterable<remove_ref_t<Iterable>, Iterables...>\n    operator|(Iterable&& iterable, zip_iterable<Iterables...> zipper) {\n        return concat_iterables(std::forward<Iterable>(iterable), std::move(zipper), seq{});\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_ZIP_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterables/zip_longest.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ZIP_LONGEST_ITERABLE_HPP\n#define LZ_ZIP_LONGEST_ITERABLE_HPP\n\n#include <Lz/detail/iterators/zip_longest.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class... Iterables>\nclass zip_longest_iterable : public lazy_view {\n    std::tuple<maybe_owned<Iterables>...> _iterables{};\n\n    using iterators = std::tuple<iter_t<Iterables>...>;\n    using sentinels = std::tuple<sentinel_t<Iterables>...>;\n\n    static constexpr bool bidi = is_bidi_tag<iter_tuple_iter_cat_t<iterators>>::value;\n\n    static constexpr bool return_sentinel = !bidi || disjunction<has_sentinel<Iterables>...>::value;\n\n    using iter_category = iter_tuple_iter_cat_t<iterators>;\n\npublic:\n    using iterator = zip_longest_iterator<iter_category, maybe_owned<Iterables>...>;\n    using const_iterator = iterator;\n    using value_type = typename iterator::value_type;\n\n    using is = make_index_sequence<sizeof...(Iterables)>;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr zip_longest_iterable()\n        requires(std::default_initializable<maybe_owned<Iterables>> && ...)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterables), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr zip_longest_iterable() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<size_t... I>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 size_t size(index_sequence<I...>) const {\n        return max_variadic(static_cast<size_t>(lz::size(std::get<I>(_iterables)))...);\n    }\n\n    template<class Iterable2, size_t... Is>\n    static zip_longest_iterable<remove_ref_t<Iterable2>, Iterables...>\n    concat_iterables(Iterable2&& iterable2, zip_longest_iterable<Iterables...> zipper, index_sequence<Is...>) {\n        return { std::forward<Iterable2>(iterable2), std::move(std::get<Is>(zipper._iterables))... };\n    }\n\npublic:\n    template<class... I>\n    LZ_CONSTEXPR_CXX_14 zip_longest_iterable(I&&... iterables) : _iterables{ std::forward<I>(iterables)... } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 size_t size() const\n        requires(sized<Iterables> && ...)\n    {\n        return size(is{});\n    }\n\n#else\n\n    template<bool S = conjunction<is_sized<Iterables>...>::value>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<S, size_t> size() const {\n        return size(is{});\n    }\n\n#endif\n\n#ifdef LZ_HAS_CXX_17\n\n    [[nodiscard]] constexpr iterator begin() const {\n        if constexpr (!is_bidi_tag_v<iter_category>) {\n            return { begin_tuple(_iterables), end_tuple(_iterables) };\n        }\n        else {\n            using diff = typename iterator::difference_type;\n            return iterator{ _iterables, begin_tuple(_iterables), tuple_of<diff>(is{}) };\n        }\n    }\n\n    [[nodiscard]] constexpr auto end() const {\n        if constexpr (return_sentinel) {\n            return lz::default_sentinel;\n        }\n        else {\n            using diff = typename iterator::difference_type;\n            return iterator{ _iterables, end_tuple(_iterables), iterables_size_tuple<diff>(_iterables, is{}) };\n        }\n    }\n\n#else\n\n    template<class C = iter_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_bidi_tag<C>::value, iterator> begin() const {\n        return { begin_tuple(_iterables), end_tuple(_iterables) };\n    }\n\n    template<class C = iter_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<is_bidi_tag<C>::value, iterator> begin() const {\n        using diff = typename iterator::difference_type;\n        return { _iterables, begin_tuple(_iterables), tuple_of<diff>(is{}) };\n    }\n\n    template<class C = iter_category>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!return_sentinel && is_bidi_tag<C>::value, iterator> end() const {\n        using diff = typename iterator::difference_type;\n        return iterator{ _iterables, end_tuple(_iterables), iterables_size_tuple<diff>(_iterables, is{}) };\n    }\n\n    template<bool R = return_sentinel>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<R, default_sentinel_t> end() const noexcept {\n        return {};\n    }\n\n#endif\n\n    template<class Iterable>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 friend zip_longest_iterable<remove_ref_t<Iterable>, Iterables...>\n    operator|(Iterable&& iterable, zip_longest_iterable<Iterables...> zipper) {\n        return concat_iterables(std::forward<Iterable>(iterable), std::move(zipper), is{});\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_ZIP_LONGEST_ITERABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterator.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_ITERATOR_HPP\n#define LZ_DETAIL_ITERATOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/sentinel_operators.hpp>\n#include <iterator>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Derived, class, class, class, class, class S = Derived>\nstruct iterator;\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class S>\nstruct iterator<Derived, Reference, Pointer, DifferenceType, std::input_iterator_tag, S> {\n    using iterator_category = std::input_iterator_tag;\n    using sentinel = S;\n\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Weffc++\"\n#endif\n\n    LZ_CONSTEXPR_CXX_14 Derived& operator++() {\n        static_cast<Derived&>(*this).increment();\n        return static_cast<Derived&>(*this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 Derived operator++(int) {\n        Derived copy = static_cast<Derived&>(*this);\n        static_cast<Derived&>(*this).increment();\n        return copy;\n    }\n\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n\n    LZ_NODISCARD constexpr Reference operator*() const {\n        return static_cast<const Derived&>(*this).dereference();\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 Reference operator*() {\n        return static_cast<Derived&>(*this).dereference();\n    }\n\n    LZ_NODISCARD constexpr Pointer operator->() const {\n        return static_cast<const Derived&>(*this).arrow();\n    }\n\n    LZ_NODISCARD constexpr bool operator==(const sentinel& s) const {\n        return static_cast<const Derived&>(*this).eq(s);\n    }\n\n    LZ_NODISCARD constexpr bool operator!=(const sentinel& s) const {\n        return !(*this == s);\n    }\n\n    LZ_NODISCARD friend constexpr bool operator==(const Derived& a, const Derived& d) {\n        return a.eq(d);\n    }\n\n    LZ_NODISCARD friend constexpr bool operator!=(const Derived& a, const Derived& d) {\n        return !(a == d);\n    }\n};\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class S>\nstruct iterator<Derived, Reference, Pointer, DifferenceType, std::forward_iterator_tag, S>\n    : public iterator<Derived, Reference, Pointer, DifferenceType, std::input_iterator_tag, S> {\n    using iterator_category = std::forward_iterator_tag;\n};\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class S>\nstruct iterator<Derived, Reference, Pointer, DifferenceType, std::bidirectional_iterator_tag, S>\n    : public iterator<Derived, Reference, Pointer, DifferenceType, std::forward_iterator_tag, S> {\n    using iterator_category = std::bidirectional_iterator_tag;\n\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Weffc++\"\n#endif\n\n    LZ_CONSTEXPR_CXX_14 Derived& operator--() {\n        static_cast<Derived&>(*this).decrement();\n        return static_cast<Derived&>(*this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 Derived operator--(int) {\n        Derived copy = static_cast<Derived&>(*this);\n        static_cast<Derived&>(*this).decrement();\n        return copy;\n    }\n\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n};\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class S>\nstruct iterator<Derived, Reference, Pointer, DifferenceType, std::random_access_iterator_tag, S>\n    : public iterator<Derived, Reference, Pointer, DifferenceType, std::bidirectional_iterator_tag, S> {\n    using iterator_category = std::random_access_iterator_tag;\n\n    LZ_CONSTEXPR_CXX_14 Derived& operator+=(const DifferenceType n) {\n        static_cast<Derived&>(*this).plus_is(n);\n        return static_cast<Derived&>(*this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 Derived operator+(const DifferenceType n) const {\n        auto tmp = static_cast<const Derived&>(*this);\n        tmp += n;\n        return tmp;\n    }\n\n    LZ_NODISCARD constexpr friend DifferenceType operator-(const Derived& a, const Derived& d) {\n        return a.difference(d);\n    }\n\n    LZ_NODISCARD constexpr DifferenceType operator-(const S& s) const {\n        return static_cast<const Derived&>(*this).difference(s);\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 Derived operator-(const DifferenceType n) const {\n        Derived temp = static_cast<const Derived&>(*this);\n        temp -= n;\n        return temp;\n    }\n\n    LZ_CONSTEXPR_CXX_14 Derived& operator-=(const DifferenceType n) {\n        *this += (-n);\n        return *static_cast<Derived*>(this);\n    }\n\n    LZ_NODISCARD constexpr Reference operator[](DifferenceType n) const {\n        return *(*this + n);\n    }\n\n    LZ_NODISCARD constexpr friend bool operator<(const Derived& a, const Derived& d) {\n        return d - a > 0;\n    }\n\n    LZ_NODISCARD constexpr friend bool operator>(const Derived& a, const Derived& d) {\n        return d < a;\n    }\n\n    LZ_NODISCARD constexpr friend bool operator<=(const Derived& a, const Derived& d) {\n        return !(d < a);\n    }\n\n    LZ_NODISCARD constexpr friend bool operator>=(const Derived& a, const Derived& d) {\n        return !(a < d);\n    }\n\n    LZ_NODISCARD constexpr bool operator<(const S& s) const {\n        return (*this - s) < 0;\n    }\n\n    LZ_NODISCARD constexpr bool operator>(const S& s) const {\n        return s < *this;\n    }\n\n    LZ_NODISCARD constexpr bool operator<=(const S& s) const {\n        return !(s < *this);\n    }\n\n    LZ_NODISCARD constexpr bool operator>=(const S& s) const {\n        return !(*this < s);\n    }\n};\n\n#ifdef LZ_HAS_CXX_20\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class S>\nstruct iterator<Derived, Reference, Pointer, DifferenceType, std::contiguous_iterator_tag, S>\n    : public iterator<Derived, Reference, Pointer, DifferenceType, std::random_access_iterator_tag, S> {\n    using iterator_category = std::contiguous_iterator_tag;\n};\n\n#endif\n} // namespace detail\n} // namespace lz\n#endif // LZ_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/any_iterable/any_iterator_impl.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ANY_VIEW_ITERATOR_IMPL_HPP\n#define LZ_ANY_VIEW_ITERATOR_IMPL_HPP\n\n#include <Lz/detail/iterators/any_iterable/iterator_base.hpp>\n#include <Lz/detail/iterators/common.hpp>\n#include <Lz/detail/traits/conditional.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/unique_ptr.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iter, class S, class T, class Reference, class IterCat, class DiffType>\nclass any_iterator_impl;\n\ntemplate<class Iter, class S, class T, class Reference, class DiffType>\nclass any_iterator_impl<Iter, S, T, Reference, std::forward_iterator_tag, DiffType> final\n    : public iterator_base<Reference, std::forward_iterator_tag, DiffType> {\n\n    static constexpr bool is_sent = is_sentinel<S, Iter>::value;\n    using iter_type = conditional_t<!is_sent, Iter, common_iterator<Iter, S>>;\n    iter_type _iter{};\n\n    using any_iter_base = iterator_base<Reference, std::forward_iterator_tag, DiffType>;\n\npublic:\n    using value_type = T;\n    using reference = Reference;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = DiffType;\n    using iterator_category = std::forward_iterator_tag;\n\n    constexpr any_iterator_impl(const any_iterator_impl&) = default;\n    LZ_CONSTEXPR_CXX_14 any_iterator_impl& operator=(const any_iterator_impl&) = default;\n\n#ifdef LZ_HAS_CXX_20\n\n    constexpr any_iterator_impl()\n        requires(std::default_initializable<iter_type>)\n    = default;\n\n#else\n\n    template<class I = iter_type, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr any_iterator_impl() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    constexpr any_iterator_impl(Iter iter) : _iter{ std::move(iter) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr any_iterator_impl(S s)\n        requires(is_sent)\n        : _iter{ std::move(s) } {\n    }\n\n    constexpr any_iterator_impl(common_iterator<Iter, S> iter)\n        requires(is_sent)\n        : _iter{ std::move(iter) } {\n    }\n\n#else\n\n    template<bool I = is_sent, class = enable_if_t<I>>\n    constexpr any_iterator_impl(S s) : _iter{ std::move(s) } {\n    }\n\n    template<bool I = is_sent, class = enable_if_t<I>>\n    constexpr any_iterator_impl(common_iterator<Iter, S> iter) : _iter{ std::move(iter) } {\n    }\n\n#endif\n\n    ~any_iterator_impl() override = default;\n\n    reference dereference() override {\n        return *_iter;\n    }\n\n    reference dereference() const override {\n        return *_iter;\n    }\n\n    pointer arrow() override {\n        return pointer{ dereference() };\n    }\n\n    pointer arrow() const override {\n        return pointer{ dereference() };\n    }\n\n    void increment() override {\n        ++_iter;\n    }\n\n    bool eq(const any_iter_base& other) const override {\n        return _iter == static_cast<const any_iterator_impl&>(other)._iter;\n    }\n\n    detail::unique_ptr<any_iter_base> clone() const override {\n        return detail::make_unique<any_iterator_impl>(_iter);\n    }\n};\n\ntemplate<class Iter, class S, class T, class Reference, class DiffType>\nclass any_iterator_impl<Iter, S, T, Reference, std::bidirectional_iterator_tag, DiffType> final\n    : public iterator_base<Reference, std::bidirectional_iterator_tag, DiffType> {\n\n    static constexpr bool is_sent = is_sentinel<S, Iter>::value;\n    using iter_type = conditional_t<!is_sent, Iter, common_iterator<Iter, S>>;\n    iter_type _iter{};\n\n    using any_iter_base = iterator_base<Reference, std::bidirectional_iterator_tag, DiffType>;\n\npublic:\n    using value_type = T;\n    using reference = Reference;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = DiffType;\n    using iterator_category = std::bidirectional_iterator_tag;\n\n    constexpr any_iterator_impl(const any_iterator_impl&) = default;\n    LZ_CONSTEXPR_CXX_14 any_iterator_impl& operator=(const any_iterator_impl&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr any_iterator_impl()\n        requires(std::default_initializable<iter_type>)\n    = default;\n\n#else\n\n    template<class I = iter_type, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr any_iterator_impl() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    constexpr any_iterator_impl(Iter iter) : _iter{ std::move(iter) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr any_iterator_impl(S s)\n        requires(is_sent)\n        : _iter{ std::move(s) } {\n    }\n\n    constexpr any_iterator_impl(common_iterator<Iter, S> iter)\n        requires(is_sent)\n        : _iter{ std::move(iter) } {\n    }\n\n#else\n\n    template<bool I = is_sent, class = enable_if_t<I>>\n    constexpr any_iterator_impl(S iter) : _iter{ std::move(iter) } {\n    }\n\n    template<bool I = is_sent, class = enable_if_t<I>>\n    constexpr any_iterator_impl(common_iterator<Iter, S> iter) : _iter{ std::move(iter) } {\n    }\n\n#endif\n\n    ~any_iterator_impl() override = default;\n\n    reference dereference() override {\n        return *_iter;\n    }\n\n    reference dereference() const override {\n        return *_iter;\n    }\n\n    pointer arrow() override {\n        return pointer{ dereference() };\n    }\n\n    pointer arrow() const override {\n        return pointer{ dereference() };\n    }\n\n    void increment() override {\n        ++_iter;\n    }\n\n    void decrement() override {\n        --_iter;\n    }\n\n    bool eq(const any_iter_base& other) const override {\n        return _iter == static_cast<const any_iterator_impl&>(other)._iter;\n    }\n\n    detail::unique_ptr<any_iter_base> clone() const override {\n        return detail::make_unique<any_iterator_impl>(_iter);\n    }\n};\n\ntemplate<class Iter, class S, class T, class Reference, class DiffType>\nclass any_iterator_impl<Iter, S, T, Reference, std::random_access_iterator_tag, DiffType> final\n    : public iterator_base<Reference, std::random_access_iterator_tag, DiffType> {\n\n    static constexpr bool is_sent = is_sentinel<S, Iter>::value;\n    using iter_type = conditional_t<!is_sent, Iter, common_iterator<Iter, S>>;\n    iter_type _iter{};\n\n    using any_iter_base = iterator_base<Reference, std::random_access_iterator_tag, DiffType>;\n\npublic:\n    using value_type = T;\n    using reference = Reference;\n    using pointer = fake_ptr_proxy<Reference>;\n    using difference_type = DiffType;\n    using iterator_category = std::random_access_iterator_tag;\n\n    constexpr any_iterator_impl(const any_iterator_impl&) = default;\n    LZ_CONSTEXPR_CXX_14 any_iterator_impl& operator=(const any_iterator_impl&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr any_iterator_impl()\n        requires(std::default_initializable<iter_type>)\n    = default;\n\n#else\n\n    template<class I = iter_type, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr any_iterator_impl() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    constexpr any_iterator_impl(Iter iter) : _iter{ std::move(iter) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr any_iterator_impl(S s)\n        requires(is_sent)\n        : _iter{ std::move(s) } {\n    }\n\n    constexpr any_iterator_impl(common_iterator<Iter, S> iter)\n        requires(is_sent)\n        : _iter{ std::move(iter) } {\n    }\n\n#else\n\n    template<bool I = is_sent, class = enable_if_t<I>>\n    constexpr any_iterator_impl(S iter) : _iter{ std::move(iter) } {\n    }\n\n    template<bool I = is_sent, class = enable_if_t<I>>\n    constexpr any_iterator_impl(common_iterator<Iter, S> iter) : _iter{ std::move(iter) } {\n    }\n\n#endif\n\n    ~any_iterator_impl() override = default;\n\n    reference dereference() override {\n        return *_iter;\n    }\n\n    reference dereference() const override {\n        return *_iter;\n    }\n\n    pointer arrow() override {\n        return pointer{ dereference() };\n    }\n\n    pointer arrow() const override {\n        return pointer{ dereference() };\n    }\n\n    void increment() override {\n        ++_iter;\n    }\n\n    void decrement() override {\n        --_iter;\n    }\n\n    bool eq(const any_iter_base& other) const override {\n        return _iter == static_cast<const any_iterator_impl&>(other)._iter;\n    }\n\n    void plus_is(DiffType n) override {\n        _iter += n;\n    }\n\n    DiffType difference(const any_iter_base& other) const override {\n        return _iter - static_cast<const any_iterator_impl&>(other)._iter;\n    }\n\n    detail::unique_ptr<any_iter_base> clone() const override {\n        return detail::make_unique<any_iterator_impl>(_iter);\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/any_iterable/iterator_base.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ANY_VIEW_ITERATOR_BASE_HPP\n#define LZ_ANY_VIEW_ITERATOR_BASE_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/unique_ptr.hpp>\n#include <iterator>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Reference, class IterCat, class DiffType>\nstruct iterator_base;\n\ntemplate<class Reference, class DiffType>\nstruct iterator_base<Reference, std::forward_iterator_tag, DiffType> {\n    virtual ~iterator_base() = default;\n\n    virtual Reference dereference() = 0;\n\n    virtual Reference dereference() const = 0;\n\n    virtual fake_ptr_proxy<Reference> arrow() = 0;\n\n    virtual fake_ptr_proxy<Reference> arrow() const = 0;\n\n    virtual void increment() = 0;\n\n    virtual bool eq(const iterator_base& other) const = 0;\n\n    virtual detail::unique_ptr<iterator_base> clone() const = 0;\n};\n\ntemplate<class Reference, class DiffType>\nstruct iterator_base<Reference, std::bidirectional_iterator_tag, DiffType> {\n    virtual ~iterator_base() = default;\n\n    virtual Reference dereference() = 0;\n\n    virtual Reference dereference() const = 0;\n\n    virtual fake_ptr_proxy<Reference> arrow() = 0;\n\n    virtual fake_ptr_proxy<Reference> arrow() const = 0;\n\n    virtual void increment() = 0;\n\n    virtual void decrement() = 0;\n\n    virtual bool eq(const iterator_base& other) const = 0;\n\n    virtual detail::unique_ptr<iterator_base> clone() const = 0;\n};\n\ntemplate<class Reference, class DiffType>\nstruct iterator_base<Reference, std::random_access_iterator_tag, DiffType> {\n    virtual ~iterator_base() = default;\n\n    virtual Reference dereference() = 0;\n\n    virtual Reference dereference() const = 0;\n\n    virtual fake_ptr_proxy<Reference> arrow() = 0;\n\n    virtual fake_ptr_proxy<Reference> arrow() const = 0;\n\n    virtual void increment() = 0;\n\n    virtual void decrement() = 0;\n\n    virtual void plus_is(DiffType n) = 0;\n\n    virtual DiffType difference(const iterator_base& other) const = 0;\n\n    virtual bool eq(const iterator_base& other) const = 0;\n\n    virtual detail::unique_ptr<iterator_base> clone() const = 0;\n};\n} // namespace detail\n} // namespace lz\n#endif // LZ_ANY_VIEW_ITERATOR_BASE_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/as_iterator.hpp",
    "content": "#pragma once\n\n#ifndef LZ_AS_ITERATOR_ITERATOR_HPP\n#define LZ_AS_ITERATOR_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nclass as_iterator_sentinel {\n    T value{};\n\n    template<class, class, class>\n    friend class as_iterator_iterator;\n\n    template<class, class>\n    friend class as_iterator_iterable;\n\n    explicit constexpr as_iterator_sentinel(T v) noexcept(std::is_nothrow_move_constructible<T>::value) : value{ std::move(v) } {\n    }\n\npublic:\n#ifdef LZ_HAS_CXX_20\n    constexpr as_iterator_sentinel()\n        requires(std::default_initializable<T>)\n    = default;\n#else\n    template<class I = T, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr as_iterator_sentinel() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n#endif\n};\n\ntemplate<class Iterator, class S, class IterCat>\nclass as_iterator_iterator : public iterator<as_iterator_iterator<Iterator, S, IterCat>, Iterator, fake_ptr_proxy<Iterator>,\n                                             diff_type<Iterator>, IterCat, as_iterator_sentinel<S>> {\n    Iterator _iterator{};\n\npublic:\n    using value_type = Iterator;\n    using reference = Iterator;\n    using pointer = fake_ptr_proxy<Iterator>;\n    using difference_type = typename std::iterator_traits<Iterator>::difference_type;\n\n    constexpr as_iterator_iterator(const as_iterator_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 as_iterator_iterator& operator=(const as_iterator_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr as_iterator_iterator()\n        requires(std::default_initializable<Iterator>)\n    = default;\n\n#else\n\n    template<class I = Iterator, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr as_iterator_iterator() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    explicit constexpr as_iterator_iterator(Iterator it) : _iterator{ std::move(it) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 as_iterator_iterator& operator=(const as_iterator_sentinel<S>& other) {\n        _iterator = other.value;\n        return *this;\n    }\n\n    constexpr reference dereference() const {\n        return _iterator;\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<Iterator>{ _iterator };\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        ++_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        --_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const as_iterator_iterator& other) const {\n        return _iterator - other._iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type& n) {\n        _iterator += n;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const as_iterator_iterator& other) const {\n        return _iterator == other._iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const as_iterator_sentinel<S>& other) const {\n        return _iterator == other.value;\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/c_string.hpp",
    "content": "#pragma once\n\n#ifndef LZ_C_STRING_ITERATOR_HPP\n#define LZ_C_STRING_ITERATOR_HPP\n\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class C>\nclass c_string_iterator\n    : public iterator<c_string_iterator<C>, C&, C*, std::ptrdiff_t, std::forward_iterator_tag, default_sentinel_t> {\n\n    C* _it{ nullptr };\n\npublic:\n    using value_type = typename std::remove_const<C>::type;\n    using difference_type = std::ptrdiff_t;\n    using pointer = C*;\n    using reference = C&;\n\n    constexpr c_string_iterator() noexcept = default;\n    constexpr c_string_iterator(const c_string_iterator&) noexcept = default;\n    LZ_CONSTEXPR_CXX_14 c_string_iterator& operator=(const c_string_iterator&) noexcept = default;\n\n    explicit constexpr c_string_iterator(C* it) noexcept : _it{ it } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 c_string_iterator& operator=(default_sentinel_t) noexcept {\n        _it = nullptr;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const noexcept {\n        LZ_ASSERT_DEREFERENCABLE(_it != nullptr);\n        return *_it;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const noexcept {\n        LZ_ASSERT_DEREFERENCABLE(_it != nullptr);\n        return _it;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() noexcept {\n        LZ_ASSERT_INCREMENTABLE(_it != nullptr);\n        LZ_ASSERT_INCREMENTABLE(*_it != '\\0');\n        ++_it;\n    }\n\n    constexpr bool eq(const c_string_iterator& other) const noexcept {\n        return other._it == nullptr ? _it == nullptr || *_it == '\\0' : _it == other._it;\n    }\n\n    constexpr bool eq(default_sentinel_t) const noexcept {\n        return _it == nullptr || *_it == '\\0';\n    }\n\n    constexpr explicit operator bool() const noexcept {\n        return _it != nullptr && *_it != '\\0';\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_C_STRING_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/cached_reverse.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CACHED_REVERSE_ITERATOR_HPP\n#define LZ_CACHED_REVERSE_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterator>\nclass cached_reverse_iterator\n    : public iterator<cached_reverse_iterator<Iterator>, ref_t<Iterator>, fake_ptr_proxy<ref_t<Iterator>>, diff_type<Iterator>,\n                      iter_cat_t<Iterator>, default_sentinel_t> {\n    using traits = std::iterator_traits<Iterator>;\n\n    Iterator _iterator{};\n    Iterator _prev_it{};\n    Iterator _begin{};\n\npublic:\n    using value_type = typename traits::value_type;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr cached_reverse_iterator(const cached_reverse_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 cached_reverse_iterator& operator=(const cached_reverse_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr cached_reverse_iterator()\n        requires(std::default_initializable<Iterator>)\n    = default;\n\n#else\n\n    template<class I = Iterator, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr cached_reverse_iterator() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class S>\n    LZ_CONSTEXPR_CXX_14 cached_reverse_iterator(Iterator it, Iterator begin, S end) :\n        _iterator{ std::move(it) },\n        _prev_it{ _iterator },\n        _begin{ std::move(begin) } {\n        if (_iterator == end && _begin != end) {\n            --_prev_it;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 cached_reverse_iterator& operator=(default_sentinel_t) {\n        _iterator = _begin;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_prev_it;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        _iterator = _prev_it;\n        if (_prev_it != _begin) {\n            --_prev_it;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        _prev_it = _iterator;\n        ++_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type n) {\n        _iterator -= n;\n        if (_iterator != _begin) {\n            _prev_it = (_iterator - 1);\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const cached_reverse_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(other._begin == _begin);\n        return static_cast<difference_type>(other._iterator - _iterator);\n    }\n\n    constexpr difference_type difference(default_sentinel_t) const {\n        return static_cast<difference_type>(_begin - _iterator);\n    }\n\n    constexpr bool eq(const cached_reverse_iterator& other) const {\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _begin;\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_CACHED_REVERSE_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/cartesian_product.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CARTESIAN_PRODUCT_ITERATOR_HPP\n#define LZ_CARTESIAN_PRODUCT_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/procs/decompose.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class... Iterables>\nstruct tuple_iter_types;\n\ntemplate<class... Iterables>\nstruct tuple_iter_types<std::tuple<Iterables...>> {\n    using type = std::tuple<iter_t<Iterables>...>;\n};\n\ntemplate<class Iterables>\nusing tuple_iter_types_t = typename tuple_iter_types<Iterables>::type;\n\ntemplate<class Iterables>\nclass cartesian_product_iterator\n    : public iterator<cartesian_product_iterator<Iterables>,\n                      iter_tuple_ref_type_t<tuple_iter_types_t<Iterables>>,\n                      fake_ptr_proxy<iter_tuple_ref_type_t<tuple_iter_types_t<Iterables>>>,\n                      iter_tuple_diff_type_t<tuple_iter_types_t<Iterables>>,\n                      iter_tuple_iter_cat_t<tuple_iter_types_t<Iterables>>, default_sentinel_t> {\n\n    using iterators = tuple_iter_types_t<Iterables>;\n    static constexpr size_t tup_size = tuple_size<iterators>::value;\n\npublic:\n    using value_type = iter_tuple_value_type_t<iterators>;\n    using reference = iter_tuple_ref_type_t<iterators>;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = iter_tuple_diff_type_t<iterators>;\n\nprivate:\n    iterators _iterators{};\n    Iterables _iterables{};\n\n#ifdef LZ_HAS_CXX_17\n\n    template<size_t I>\n    constexpr void next() {\n        \n        ++std::get<I>(_iterators);\n        if constexpr (I > 0) {\n            if (std::get<I>(_iterators) == std::get<I>(_iterables).end()) {\n                std::get<I>(_iterators) = std::get<I>(_iterables).begin();\n                next<I - 1>();\n            }\n        }\n    }\n\n    template<size_t I>\n    constexpr void previous() {\n        \n        if constexpr (I > 0) {\n            if (std::get<I>(_iterators) == std::get<I>(_iterables).begin()) {\n                std::get<I>(_iterators) = std::get<I>(_iterables).end();\n                previous<I - 1>();\n            }\n        }\n        LZ_ASSERT_DECREMENTABLE(std::get<I>(_iterators) != std::get<I>(_iterables).begin());\n        --std::get<I>(_iterators);\n    }\n\n    template<size_t I>\n    constexpr void operator_plus_impl(const difference_type offset) {\n        \n        if constexpr (I == 0) {\n            LZ_ASSERT_SUB_ADDABLE(offset < 0 ? -offset <= std::get<0>(_iterators) - std::get<0>(_iterables).begin()\n                                             : offset <= std::get<0>(_iterables).end() - std::get<0>(_iterators));\n            std::get<0>(_iterators) += offset;\n        }\n        else {\n            if (offset == 0) {\n                return;\n            }\n\n            const auto size = static_cast<difference_type>(std::get<I>(_iterables).end() - std::get<I>(_iterables).begin());\n            const auto iterator_offset = static_cast<difference_type>(std::get<I>(_iterators) - std::get<I>(_iterables).begin());\n            const auto to_add_this = (iterator_offset + offset) % size;\n            const auto to_add_next = (iterator_offset + offset) / size;\n\n            if (to_add_this < 0) {\n                std::get<I>(_iterators) = std::get<I>(_iterables).begin() + static_cast<difference_type>(to_add_this + size);\n                operator_plus_impl<I - 1>(to_add_next - 1);\n                return;\n            }\n\n            std::get<I>(_iterators) = std::get<I>(_iterables).begin() + static_cast<difference_type>(to_add_this);\n            operator_plus_impl<I - 1>(to_add_next);\n        }\n    }\n\n#else\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 0> next() {\n        \n        ++std::get<I>(_iterators);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<(I > 0)> next() {\n        \n        ++std::get<I>(_iterators);\n        if (std::get<I>(_iterators) == std::get<I>(_iterables).end()) {\n            std::get<I>(_iterators) = std::get<I>(_iterables).begin();\n            next<I - 1>();\n        }\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 0> previous() {\n        \n        LZ_ASSERT_DECREMENTABLE(std::get<0>(_iterators) != std::get<0>(_iterables).begin());\n        --std::get<0>(_iterators);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<(I > 0)> previous() {\n        \n        if (std::get<I>(_iterators) == std::get<I>(_iterables).begin()) {\n            std::get<I>(_iterators) = std::get<I>(_iterables).end();\n            previous<I - 1>();\n        }\n        LZ_ASSERT_DECREMENTABLE(std::get<I>(_iterators) != std::get<I>(_iterables).begin());\n        --std::get<I>(_iterators);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 0> operator_plus_impl(const difference_type offset) {\n        \n        LZ_ASSERT_SUB_ADDABLE(offset < 0 ? -offset <= std::get<0>(_iterators) - std::get<0>(_iterables).begin()\n                                         : offset <= std::get<0>(_iterables).end() - std::get<0>(_iterators));\n        std::get<0>(_iterators) += offset;\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<(I > 0)> operator_plus_impl(const difference_type offset) {\n        \n        if (offset == 0) {\n            return;\n        }\n\n        const auto size = static_cast<difference_type>(std::get<I>(_iterables).end() - std::get<I>(_iterables).begin());\n        const auto iterator_offset = static_cast<difference_type>(std::get<I>(_iterators) - std::get<I>(_iterables).begin());\n        const auto to_add_this = (iterator_offset + offset) % size;\n        const auto to_add_next = (iterator_offset + offset) / size;\n\n        if (to_add_this < 0) {\n            std::get<I>(_iterators) = std::get<I>(_iterables).begin() + static_cast<difference_type>(to_add_this + size);\n            operator_plus_impl<I - 1>(to_add_next - 1);\n            return;\n        }\n\n        std::get<I>(_iterators) = std::get<I>(_iterables).begin() + static_cast<difference_type>(to_add_this);\n        operator_plus_impl<I - 1>(to_add_next);\n    }\n\n#endif // LZ_HAS_CXX_17\n\n    template<size_t... Is>\n    LZ_CONSTEXPR_CXX_14 reference dereference(index_sequence<Is...>) const {\n        \n        return reference{ *std::get<Is>(_iterators)... };\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    template<std::ptrdiff_t I>\n    constexpr void iter_compat(const cartesian_product_iterator& other) const {\n        \n        if constexpr (I >= 0) {\n            LZ_ASSERT_COMPATIBLE(std::get<I>(_iterables).begin() == std::get<I>(other._iterables).begin() &&\n                                 std::get<I>(_iterables).end() == std::get<I>(other._iterables).end());\n            iter_compat<I - 1>(other);\n        }\n    }\n\n    template<size_t I>\n    constexpr difference_type difference(const cartesian_product_iterator& other) const {\n        \n        if constexpr (I > 0) {\n            const auto distance = std::get<I>(_iterators) - std::get<I>(other._iterators);\n            const auto size = std::get<I>(_iterables).end() - std::get<I>(_iterables).begin();\n            return size * difference<I - 1>(other) + distance;\n        }\n        else {\n            return std::get<0>(_iterators) - std::get<0>(other._iterators);\n        }\n    }\n\n    template<size_t I>\n    constexpr difference_type difference() const {\n        \n        if constexpr (I > 0) {\n            const auto distance = std::get<I>(_iterators) - std::get<I>(_iterables).begin();\n            const auto size = std::get<I>(_iterables).end() - std::get<I>(_iterables).begin();\n            return size * difference<I - 1>() + distance;\n        }\n        else {\n            return std::get<I>(_iterators) - std::get<I>(_iterables).end();\n        }\n    }\n\n#else\n\n    template<std::ptrdiff_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<(I >= 0)> iter_compat(const cartesian_product_iterator& other) const {\n        \n        LZ_ASSERT_COMPATIBLE(std::get<I>(_iterables).begin() == std::get<I>(other._iterables).begin() &&\n                             std::get<I>(_iterables).end() == std::get<I>(other._iterables).end());\n        iter_compat<I - 1>(other);\n    }\n\n    template<std::ptrdiff_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<(I < 0)> iter_compat(const cartesian_product_iterator&) const noexcept {\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<(I > 0), difference_type> difference(const cartesian_product_iterator& other) const {\n        \n        const auto distance = std::get<I>(_iterators) - std::get<I>(other._iterators);\n        const auto size = std::get<I>(_iterables).end() - std::get<I>(_iterables).begin();\n        const auto result = size * difference<I - 1>(other) + distance;\n        return result;\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 0, difference_type> difference(const cartesian_product_iterator& other) const {\n        \n        return std::get<0>(_iterators) - std::get<0>(other._iterators);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<(I > 0), difference_type> difference() const {\n        \n        const auto distance = std::get<I>(_iterators) - std::get<I>(_iterables).begin();\n        const auto size = std::get<I>(_iterables).end() - std::get<I>(_iterables).begin();\n        return size * difference<I - 1>() + distance;\n    }\n\n    template<size_t I>\n    constexpr enable_if_t<I == 0, difference_type> difference() const {\n        \n        return std::get<I>(_iterators) - std::get<I>(_iterables).end();\n    }\n\n#endif\n\n    using is = make_index_sequence<tup_size>;\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void assign_sentinels(index_sequence<I...>) {\n        \n        auto rest_it = begin_tuple(_iterables);\n        std::get<0>(rest_it) = std::get<0>(end_tuple(_iterables));\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_iterators) = std::get<I>(rest_it)), ...);\n#else\n        decompose(std::get<I>(_iterators) = std::get<I>(rest_it)...);\n#endif\n    }\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr cartesian_product_iterator()\n        requires(std::default_initializable<iterators> && std::default_initializable<Iterables>)\n    = default;\n\n#else\n\n    template<class I = iterators, class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                      std::is_default_constructible<Iterables>::value>>\n    constexpr cartesian_product_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                                    std::is_nothrow_default_constructible<Iterables>::value) {\n    }\n\n#endif\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 cartesian_product_iterator(I&& iterables, iterators iters) :\n        _iterators{ std::move(iters) },\n        _iterables{ std::forward<I>(iterables) } {\n        static_assert(tup_size > 1, \"Cannot cartesian product one/zero iterables\");\n    }\n\n    LZ_CONSTEXPR_CXX_14 cartesian_product_iterator& operator=(default_sentinel_t) {\n        assign_sentinels(is{});\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return dereference(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        next<tup_size - 1>();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        previous<tup_size - 1>();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type n) {\n        operator_plus_impl<tup_size - 1>(n);\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const cartesian_product_iterator& other) const {\n        iter_compat<static_cast<std::ptrdiff_t>(tup_size) - 1>(other);\n        return _iterators == other._iterators;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        \n        return std::get<0>(_iterators) == std::get<0>(_iterables).end();\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const cartesian_product_iterator& other) const {\n        iter_compat<static_cast<std::ptrdiff_t>(tup_size) - 1>(other);\n        return difference<tup_size - 1>(other);\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(default_sentinel_t) const {\n        return difference<tup_size - 1>();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_CARTESIAN_PRODUCT_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/chunk_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CHUNK_IF_ITERATOR_HPP\n#define LZ_CHUNK_IF_ITERATOR_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class ValueType, class Iterator, class S, class UnaryPredicate>\nclass chunk_if_iterator\n    : public iterator<chunk_if_iterator<ValueType, Iterator, S, UnaryPredicate>, ValueType, fake_ptr_proxy<ValueType>,\n                      diff_type<Iterator>, std::forward_iterator_tag, default_sentinel_t> {\n    using iter_traits = std::iterator_traits<Iterator>;\n\npublic:\n    using value_type = ValueType;\n    using difference_type = typename iter_traits::difference_type;\n    using reference = value_type;\n    using pointer = fake_ptr_proxy<reference>;\n\n    chunk_if_iterator(const chunk_if_iterator&) = default;\n    chunk_if_iterator& operator=(const chunk_if_iterator&) = default;\n\nprivate:\n    Iterator _sub_range_begin{};\n    Iterator _sub_range_end{};\n    bool _ends_with_trailing{ true };\n    S _end{};\n    mutable UnaryPredicate _predicate{};\n\n    LZ_CONSTEXPR_CXX_14 void find_next() {\n        _sub_range_end = detail::find_if(_sub_range_end, _end, _predicate);\n    }\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr chunk_if_iterator()\n        requires(std::default_initializable<Iterator> && std::default_initializable<S> &&\n                 std::default_initializable<UnaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = Iterator,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<S>::value &&\n                                 std::is_default_constructible<UnaryPredicate>::value>>\n    constexpr chunk_if_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                           std::is_nothrow_default_constructible<S>::value &&\n                                           std::is_nothrow_default_constructible<UnaryPredicate>::value) {\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 chunk_if_iterator(Iterator begin, S end, UnaryPredicate predicate, bool is_empty) :\n        _sub_range_begin{ std::move(begin) },\n        _sub_range_end{ _sub_range_begin },\n        _end{ std::move(end) },\n        _predicate{ std::move(predicate) } {\n        if (_sub_range_begin != _end) {\n            find_next();\n        }\n        if (is_empty) {\n            _ends_with_trailing = false;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 chunk_if_iterator& operator=(default_sentinel_t) {\n        _sub_range_begin = _end;\n        _sub_range_end = _end;\n        _ends_with_trailing = false;\n        return *this;\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    constexpr reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        if constexpr (std::is_constructible_v<ValueType, Iterator, Iterator>) {\n            return { _sub_range_begin, _sub_range_end };\n        }\n        else {\n            return { detail::addressof(*_sub_range_begin), static_cast<size_t>(_sub_range_end - _sub_range_begin) };\n        }\n    }\n\n#else\n\n    template<class V = ValueType>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_constructible<V, Iterator, Iterator>::value, reference> dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return { _sub_range_begin, _sub_range_end };\n    }\n\n    // Overload for std::string, [std/lz]::string_view\n    template<class V = ValueType>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!std::is_constructible<V, Iterator, Iterator>::value, reference> dereference() const {\n        static_assert(is_ra<Iterator>::value, \"Iterator must be a random access\");\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return { detail::addressof(*_sub_range_begin), static_cast<size_t>(_sub_range_end - _sub_range_begin) };\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        if (_ends_with_trailing && _sub_range_end == _end) {\n            _sub_range_begin = _sub_range_end;\n            _ends_with_trailing = false;\n            return;\n        }\n\n        auto prev = _sub_range_end++;\n        if (_sub_range_end != _end) {\n            _sub_range_begin = _sub_range_end;\n            find_next();\n            return;\n        }\n\n        if (_predicate(*prev)) {\n            _sub_range_end = _ends_with_trailing ? std::move(prev) : _sub_range_end;\n            _sub_range_begin = _sub_range_end;\n            _ends_with_trailing = false;\n            return;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const chunk_if_iterator& rhs) const {\n        LZ_ASSERT_COMPATIBLE(_end == rhs._end);\n        return _sub_range_begin == rhs._sub_range_begin && _sub_range_end == rhs._sub_range_end &&\n               _ends_with_trailing == rhs._ends_with_trailing;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _sub_range_begin == _end && !_ends_with_trailing;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_CHUNK_IF_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/chunks.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CHUNKS_ITERATOR_HPP\n#define LZ_CHUNKS_ITERATOR_HPP\n\n#include <Lz/basic_iterable.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/procs/eager_size.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class = void>\nclass chunks_iterator;\n\ntemplate<class Iterable>\nclass chunks_iterator<Iterable, enable_if_t<!is_bidi<iter_t<Iterable>>::value>>\n    : public iterator<chunks_iterator<Iterable>, basic_iterable<iter_t<Iterable>>,\n                      fake_ptr_proxy<basic_iterable<iter_t<Iterable>>>, diff_type<iter_t<Iterable>>, iter_cat_t<iter_t<Iterable>>,\n                      default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n    using iter_traits = std::iterator_traits<iter>;\n\npublic:\n    using value_type = basic_iterable<iter>;\n    using reference = value_type;\n    using pointer = fake_ptr_proxy<value_type>;\n    using difference_type = typename iter_traits::difference_type;\n\n    constexpr chunks_iterator(const chunks_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 chunks_iterator& operator=(const chunks_iterator&) = default;\n\nprivate:\n    iter _sub_range_begin{};\n    iter _sub_range_end{};\n    sentinel_t<Iterable> _end{};\n    difference_type _chunk_size{};\n\n    LZ_CONSTEXPR_CXX_14 void next_chunk() {\n        for (difference_type count = 0; count < _chunk_size && _sub_range_end != _end; count++, ++_sub_range_end) {\n        }\n    }\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr chunks_iterator()\n        requires(std::default_initializable<iter> && std::default_initializable<sent>)\n    = default;\n\n#else\n\n    template<class I = iter,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<sent>::value>>\n    constexpr chunks_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<sent>::value) {\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 chunks_iterator(iter i, sent end, const difference_type chunk_size) :\n        _sub_range_begin{ i },\n        _sub_range_end{ std::move(i) },\n        _end{ std::move(end) },\n        _chunk_size{ chunk_size } {\n        next_chunk();\n    }\n\n    LZ_CONSTEXPR_CXX_14 chunks_iterator& operator=(default_sentinel_t) {\n        _sub_range_begin = _end;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return { _sub_range_begin, _sub_range_end };\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        _sub_range_begin = _sub_range_end;\n        next_chunk();\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const chunks_iterator& rhs) const {\n        LZ_ASSERT_COMPATIBLE(_chunk_size == rhs._chunk_size);\n        return _sub_range_begin == rhs._sub_range_begin;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _sub_range_begin == _end;\n    }\n};\n\ntemplate<class Iterable>\nclass chunks_iterator<Iterable, enable_if_t<is_bidi<iter_t<Iterable>>::value && !is_ra<iter_t<Iterable>>::value>>\n    : public iterator<chunks_iterator<Iterable>, basic_iterable<iter_t<Iterable>>,\n                      fake_ptr_proxy<basic_iterable<iter_t<Iterable>>>, diff_type<iter_t<Iterable>>, iter_cat_t<iter_t<Iterable>>,\n                      default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n    using iter_traits = std::iterator_traits<iter>;\n\npublic:\n    using value_type = basic_iterable<iter>;\n    using reference = value_type;\n    using pointer = fake_ptr_proxy<value_type>;\n    using difference_type = typename iter_traits::difference_type;\n\nprivate:\n    iter _sub_range_begin{};\n    iter _sub_range_end{};\n    Iterable _iterable{};\n    difference_type _chunk_size{};\n    difference_type _distance{};\n\n    LZ_CONSTEXPR_CXX_14 void next_chunk() {\n        for (difference_type count = 0; count < _chunk_size && _sub_range_end != _iterable.end();\n             count++, ++_sub_range_end, ++_distance) {\n        }\n    }\n\npublic:\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr chunks_iterator()\n        requires(std::default_initializable<iter> && std::default_initializable<sent> && std::default_initializable<Iterable>)\n    = default;\n\n#else\n\n    template<class I = iter,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<sent>::value>>\n    constexpr chunks_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<sent>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 chunks_iterator(I&& iterable, iter it, const difference_type chunk_size) :\n        _sub_range_begin{ it },\n        _sub_range_end{ std::move(it) },\n        _iterable{ std::forward<I>(iterable) },\n        _chunk_size{ chunk_size },\n        _distance{ it == _iterable.end() ? lz::eager_ssize(_iterable) : 0 } {\n        next_chunk();\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr chunks_iterator& operator=(default_sentinel_t)\n        requires(is_bidi<iter>::value)\n    {\n        _sub_range_begin = _iterable.end();\n        _distance = lz::eager_ssize(_iterable);\n        return *this;\n    }\n\n#else\n    LZ_CONSTEXPR_CXX_14 chunks_iterator& operator=(default_sentinel_t) {\n        _sub_range_begin = _iterable.end();\n        _distance = lz::eager_ssize(_iterable);\n        return *this;\n    }\n\n#endif\n\n    constexpr reference dereference() const {\n        return { _sub_range_begin, _sub_range_end };\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(_sub_range_begin != _iterable.end());\n        _sub_range_begin = _sub_range_end;\n        next_chunk();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_distance != 0);\n        _sub_range_end = _sub_range_begin;\n\n        auto start_pos = _distance % _chunk_size;\n        const auto adjusted_start_pos = start_pos == 0 ? _chunk_size : start_pos;\n\n        for (difference_type count = 0; count < adjusted_start_pos; count++, --_sub_range_begin, --_distance) {\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const chunks_iterator& rhs) const {\n        LZ_ASSERT_COMPATIBLE(_chunk_size == rhs._chunk_size && _iterable.end() == rhs._iterable.end() &&\n                             _iterable.begin() == rhs._iterable.begin());\n        return _sub_range_begin == rhs._sub_range_begin;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _sub_range_begin == _iterable.end();\n    }\n};\n\ntemplate<class Iterable>\nclass chunks_iterator<Iterable, enable_if_t<is_ra<iter_t<Iterable>>::value>>\n    : public iterator<chunks_iterator<Iterable>, basic_iterable<iter_t<Iterable>>,\n                      fake_ptr_proxy<basic_iterable<iter_t<Iterable>>>, diff_type<iter_t<Iterable>>, iter_cat_t<iter_t<Iterable>>,\n                      default_sentinel_t> {\n    using iter = iter_t<Iterable>;\n    using iter_traits = std::iterator_traits<iter>;\n\npublic:\n    using value_type = basic_iterable<iter>;\n    using reference = value_type;\n    using pointer = fake_ptr_proxy<value_type>;\n    using difference_type = typename iter_traits::difference_type;\n\nprivate:\n    iter _sub_range_begin{};\n    Iterable _iterable{};\n    difference_type _chunk_size{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr chunks_iterator()\n        requires(std::default_initializable<iter> && std::default_initializable<Iterable>)\n    = default;\n\n#else\n\n    template<class I = iter,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value>>\n    constexpr chunks_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<Iterable>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 chunks_iterator(I&& iterable, iter it, const difference_type chunk_size) :\n        _sub_range_begin{ std::move(it) },\n        _iterable{ std::forward<I>(iterable) },\n        _chunk_size{ chunk_size } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 chunks_iterator& operator=(default_sentinel_t) {\n        _sub_range_begin = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(_sub_range_begin != _iterable.end());\n        auto sub_range_end = _sub_range_begin + detail::min_variadic2(_chunk_size, _iterable.end() - _sub_range_begin);\n        return { _sub_range_begin, sub_range_end };\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(_sub_range_begin != _iterable.end());\n        _sub_range_begin += (detail::min_variadic2(_chunk_size, _iterable.end() - _sub_range_begin));\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_sub_range_begin != _iterable.begin());\n        const auto remaining = _sub_range_begin - _iterable.begin();\n        const auto offset = remaining % _chunk_size;\n\n        if (offset == 0) {\n            _sub_range_begin -= _chunk_size;\n        }\n        else {\n            _sub_range_begin -= offset;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const chunks_iterator& rhs) const {\n        LZ_ASSERT_COMPATIBLE(_chunk_size == rhs._chunk_size);\n        return _sub_range_begin == rhs._sub_range_begin;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _sub_range_begin == _iterable.end();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        const auto to_add = offset * _chunk_size;\n\n        if (to_add >= 0) {\n            const auto current_distance = _iterable.end() - _sub_range_begin;\n            if (to_add <= current_distance) {\n                _sub_range_begin += to_add;\n                return;\n            }\n            LZ_ASSERT_ADDABLE(to_add - current_distance < _chunk_size);\n            _sub_range_begin = _iterable.end();\n            return;\n        }\n\n        const auto current_distance = _sub_range_begin - _iterable.begin();\n        const auto remainder = current_distance % _chunk_size;\n        if (remainder == 0) {\n            _sub_range_begin += to_add;\n            return;\n        }\n        const auto to_subtract = remainder + (-(offset + 1) * _chunk_size);\n        LZ_ASSERT_SUBTRACTABLE(current_distance >= to_subtract);\n        _sub_range_begin -= to_subtract;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const chunks_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_chunk_size == other._chunk_size && _iterable.begin() == other._iterable.begin() &&\n                             _iterable.end() == other._iterable.end());\n        const auto left = _sub_range_begin - other._sub_range_begin;\n        const auto remainder = left % _chunk_size;\n        const auto quotient = left / _chunk_size;\n        return remainder == 0 ? quotient : quotient + (left < 0 ? -1 : 1);\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(default_sentinel_t) const {\n        const auto total_distance = _iterable.end() - _sub_range_begin;\n        const auto remainder = total_distance % _chunk_size;\n        const auto quotient = total_distance / _chunk_size;\n        return -(remainder == 0 ? quotient : quotient + (total_distance < 0 ? -1 : 1));\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_CHUNKS_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/common.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_COMMON_ITERATOR_HPP\n#define LZ_DETAIL_COMMON_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/detail/variant.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterator, class S>\nclass common_iterator : public iterator<common_iterator<Iterator, S>, ref_t<Iterator>, fake_ptr_proxy<ref_t<Iterator>>,\n                                        diff_type<Iterator>, iter_cat_t<Iterator>, default_sentinel_t> {\n    variant<Iterator, S> _data{};\n\n    using traits = std::iterator_traits<Iterator>;\n\n    static_assert(!std::is_same<S, Iterator>::value,\n                  \"common_iterator should not be used with the same type for Iterator and S. Use Iterator directly.\");\n\npublic:\n    using iterator_category = typename traits::iterator_category;\n    using value_type = typename traits::value_type;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr common_iterator() noexcept = default;\n\n    explicit constexpr common_iterator(const Iterator& iter) : _data{ iter } {\n    }\n\n    explicit constexpr common_iterator(Iterator&& iter) noexcept : _data{ std::move(iter) } {\n    }\n\n    explicit constexpr common_iterator(const S& sent) : _data{ sent } {\n    }\n\n    explicit constexpr common_iterator(S&& sent) noexcept : _data{ std::move(sent) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 common_iterator& operator=(const Iterator& iter) {\n        _data = iter;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 common_iterator& operator=(const S& sent) {\n        _data = sent;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 common_iterator& operator=(Iterator&& iter) noexcept {\n        _data = std::move(iter);\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 common_iterator& operator=(S&& sent) noexcept {\n        _data = std::move(sent);\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT(_data.index() == 0, \"Cannot dereference a sentinel\");\n#ifdef __cpp_lib_variant\n        using std::get;\n#endif\n        return *get<0>(_data);\n    }\n\n    LZ_CONSTEXPR_CXX_14 fake_ptr_proxy<reference> arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT(_data.index() == 0, \"Cannot increment a sentinel\");\n#ifdef __cpp_lib_variant\n        using std::get;\n#endif\n        ++get<0>(_data);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT(_data.index() == 0, \"Cannot decrement a sentinel\");\n#ifdef __cpp_lib_variant\n        using std::get;\n#endif\n        --get<0>(_data);\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const common_iterator& rhs) const {\n#ifdef __cpp_lib_variant\n        using std::get;\n#endif\n        if (_data.index() == rhs._data.index()) {\n            return true;\n        }\n        return _data.index() == 0 ? get<0>(_data).eq(get<1>(rhs._data)) : !get<0>(rhs._data).eq(get<1>(_data));\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/concatenate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_CONCATENATE_ITERATOR_HPP\n#define LZ_CONCATENATE_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n#include <Lz/util/default_sentinel.hpp>\n#include <numeric>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/procs/decompose.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterables, class Iterators>\nclass concatenate_iterator\n    : public iterator<concatenate_iterator<Iterables, Iterators>, iter_tuple_common_ref_t<Iterators>,\n                      fake_ptr_proxy<iter_tuple_common_ref_t<Iterators>>, iter_tuple_diff_type_t<Iterators>,\n                      iter_tuple_iter_cat_t<Iterators>, default_sentinel_t> {\n\n    Iterators _iterators{};\n    Iterables _iterables{};\n\n    using first_tuple_iterator = std::iterator_traits<first_it_t<Iterators>>;\n\n    static constexpr size_t tup_size = tuple_size<Iterators>::value;\n\npublic:\n    using value_type = typename first_tuple_iterator::value_type;\n    using difference_type = iter_tuple_diff_type_t<Iterators>;\n    using reference = iter_tuple_common_ref_t<Iterators>;\n    using pointer = fake_ptr_proxy<reference>;\n\nprivate:\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_20 difference_type minus(index_sequence<I...>, const concatenate_iterator& other) const {\n\n        const difference_type totals[] = { static_cast<difference_type>(std::get<I>(_iterators) -\n                                                                        std::get<I>(other._iterators))... };\n        return std::accumulate(detail::begin(totals), detail::end(totals), difference_type{ 0 });\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_20 difference_type minus(index_sequence<I...>) const {\n\n        const difference_type totals[] = { static_cast<difference_type>(std::get<I>(_iterators) -\n                                                                        std::get<I>(_iterables).end())... };\n        return std::accumulate(detail::begin(totals), detail::end(totals), difference_type{ 0 });\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    template<size_t I>\n    constexpr void min_is_n(const difference_type offset) {\n\n        if constexpr (I != 0) {\n            auto& current = std::get<I>(_iterators);\n            auto begin = std::get<I>(_iterables).begin();\n\n            if (current != begin) {\n                const auto dist = current - begin;\n                if (dist <= offset) {\n                    current = std::move(begin);\n                    min_is_n<I - 1>(dist == 0 ? 1 : offset - dist);\n                }\n                else {\n                    current -= offset;\n                }\n            }\n            else {\n                min_is_n<I - 1>(offset);\n            }\n        }\n        else {\n            LZ_ASSERT_SUBTRACTABLE(offset <= std::get<0>(_iterators) - std::get<0>(_iterables).begin());\n            auto& current = std::get<0>(_iterators);\n            current -= offset;\n        }\n    }\n\n    template<size_t I>\n    constexpr void plus_is_n(const difference_type offset) {\n\n        if constexpr (I != tup_size) {\n            auto& current = std::get<I>(_iterators);\n            const auto current_end = std::get<I>(_iterables).end();\n            const auto dist = current_end - current;\n\n            if (dist > offset) {\n                current += offset;\n            }\n            else {\n                // Moves to end\n                current += dist;\n                plus_is_n<I + 1>(offset - dist);\n            }\n        }\n        else {\n            LZ_ASSERT_INCREMENTABLE(offset == 0);\n        }\n    }\n\n    template<size_t I>\n    constexpr void minus_minus() {\n\n        if constexpr (I != 0) {\n            auto& current = std::get<I>(_iterators);\n            if (current != std::get<I>(_iterables).begin()) {\n                --current;\n            }\n            else {\n                minus_minus<I - 1>();\n            }\n        }\n        else {\n            LZ_ASSERT_DECREMENTABLE(std::get<0>(_iterators) != std::get<0>(_iterables).begin());\n            --std::get<0>(_iterators);\n        }\n    }\n\n    template<size_t I>\n    constexpr reference deref() const {\n\n        if constexpr (I == tup_size - 1) {\n            LZ_ASSERT_DEREFERENCABLE(std::get<I>(_iterators) != std::get<I>(_iterables).end());\n            return *std::get<I>(_iterators);\n        }\n        else {\n            if (std::get<I>(_iterators) != std::get<I>(_iterables).end()) {\n                return *std::get<I>(_iterators);\n            }\n            else {\n                return deref<I + 1>();\n            }\n        }\n    }\n\n    template<size_t I>\n    constexpr void plus_plus() {\n\n        if constexpr (I != tup_size) {\n            if (std::get<I>(_iterators) != std::get<I>(_iterables).end()) {\n                ++std::get<I>(_iterators);\n            }\n            else {\n                plus_plus<I + 1>();\n            }\n        }\n        else {\n            LZ_ASSERT_INCREMENTABLE(std::get<tup_size - 1>(_iterators) != std::get<tup_size - 1>(_iterables).end());\n        }\n    }\n\n    template<size_t I, class EndIter>\n    constexpr bool iter_equal_to(const EndIter& end) const {\n\n        if constexpr (I != tup_size - 1) {\n            const auto has_value = std::get<I>(_iterators) == std::get<I>(end);\n            return has_value ? iter_equal_to<I + 1>(end) : has_value;\n        }\n        else {\n            return std::get<I>(_iterators) == std::get<I>(end);\n        }\n    }\n\n#else\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I != 0> min_is_n(const difference_type offset) {\n\n        auto& current = std::get<I>(_iterators);\n        auto begin = std::get<I>(_iterables).begin();\n\n        if (current != begin) {\n            const auto dist = current - begin;\n            if (dist <= offset) {\n                current = std::move(begin);\n                min_is_n<I - 1>(dist == 0 ? 1 : offset - dist);\n            }\n            else {\n                current -= offset;\n            }\n        }\n        else {\n            min_is_n<I - 1>(offset);\n        }\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 0> min_is_n(const difference_type offset) {\n\n        auto& current = std::get<0>(_iterators);\n        LZ_ASSERT_SUBTRACTABLE(offset <= current - std::get<0>(_iterables).begin());\n        current -= offset;\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I != tup_size> plus_is_n(const difference_type offset) {\n\n        auto& current = std::get<I>(_iterators);\n        const auto& current_end = std::get<I>(_iterables).end();\n        const auto dist = current_end - current;\n\n        if (dist > offset) {\n            current += offset;\n        }\n        else {\n            // Moves to end\n            current += dist;\n            plus_is_n<I + 1>(offset - dist);\n        }\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == tup_size> plus_is_n(const difference_type offset) const noexcept {\n        LZ_ASSERT_ADDABLE(offset == 0);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I != 0> minus_minus() {\n\n        auto& current = std::get<I>(_iterators);\n        if (current != std::get<I>(_iterables).begin()) {\n            --current;\n        }\n        else {\n            minus_minus<I - 1>();\n        }\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 0> minus_minus() {\n\n        LZ_ASSERT_DECREMENTABLE(std::get<0>(_iterables).begin() != std::get<0>(_iterables).end());\n        --std::get<0>(_iterators);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == tup_size - 1, reference> deref() const {\n\n        return *std::get<I>(_iterators);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I != tup_size - 1, reference> deref() const {\n\n        if (std::get<I>(_iterators) != std::get<I>(_iterables).end()) {\n            return *std::get<I>(_iterators);\n        }\n        else {\n            return deref<I + 1>();\n        }\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I != tuple_size<Iterators>::value> plus_plus() {\n\n        if (std::get<I>(_iterators) != std::get<I>(_iterables).end()) {\n            ++std::get<I>(_iterators);\n        }\n        else {\n            plus_plus<I + 1>();\n        }\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == tuple_size<Iterators>::value> plus_plus() const noexcept {\n    }\n\n    template<size_t I, class EndIter>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I != tuple_size<Iterators>::value - 1, bool> iter_equal_to(const EndIter& end) const {\n\n        const auto has_value = std::get<I>(_iterators) == std::get<I>(end);\n        return has_value ? iter_equal_to<I + 1>(end) : has_value;\n    }\n\n    template<size_t I, class EndIter>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == tuple_size<Iterators>::value - 1, bool> iter_equal_to(const EndIter& end) const {\n\n        return std::get<I>(_iterators) == std::get<I>(end);\n    }\n\n#endif // LZ_HAS_CXX_17\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void assign_sentinels(index_sequence<I...>) {\n\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_iterators) = std::get<I>(_iterables).end()), ...);\n#else\n        decompose(std::get<I>(_iterators) = std::get<I>(_iterables).end()...);\n#endif\n    }\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr concatenate_iterator()\n        requires(std::default_initializable<Iterators> && std::default_initializable<Iterables>)\n    = default;\n\n#else\n\n    template<class I = Iterators,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterables>::value>>\n    constexpr concatenate_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                              std::is_nothrow_default_constructible<Iterables>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 concatenate_iterator(I&& iterables, Iterators iterators) :\n        _iterators{ std::move(iterators) },\n        _iterables{ std::forward<I>(iterables) } {\n        static_assert(tup_size > 1, \"Cannot concat one/zero iterables\");\n    }\n\n    LZ_CONSTEXPR_CXX_14 concatenate_iterator& operator=(default_sentinel_t) {\n        assign_sentinels(make_index_sequence<tup_size>());\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return deref<0>();\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        plus_plus<0>();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        minus_minus<tup_size - 1>();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        if (offset < 0) {\n            min_is_n<tup_size - 1>(-offset);\n        }\n        else {\n            plus_is_n<0>(offset);\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_20 difference_type difference(const concatenate_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(begin_tuple(_iterables) == begin_tuple(other._iterables) &&\n                             end_tuple(_iterables) == end_tuple(other._iterables));\n        return minus(make_index_sequence<tup_size>(), other);\n    }\n\n    LZ_CONSTEXPR_CXX_20 difference_type difference(default_sentinel_t) const {\n        return minus(make_index_sequence<tup_size>());\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const concatenate_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(begin_tuple(_iterables) == begin_tuple(other._iterables) &&\n                             end_tuple(_iterables) == end_tuple(other._iterables));\n        return iter_equal_to<0>(other._iterators);\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(default_sentinel_t) const {\n        return iter_equal_to<0>(end_tuple(_iterables));\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_CONCATENATE_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/duplicates.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DUPLICATES_ITERATOR_HPP\n#define LZ_DUPLICATES_ITERATOR_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class BinaryPredicate, class = void>\nclass duplicates_iterator;\n\ntemplate<class Iterable, class BinaryPredicate>\nclass duplicates_iterator<Iterable, BinaryPredicate, enable_if_t<is_ra<iter_t<Iterable>>::value>>\n    : public iterator<duplicates_iterator<Iterable, BinaryPredicate>, std::pair<ref_t<iter_t<Iterable>>, size_t>,\n                      fake_ptr_proxy<std::pair<ref_t<iter_t<Iterable>>, size_t>>, diff_type<iter_t<Iterable>>,\n                      bidi_strongest_cat<iter_cat_t<iter_t<Iterable>>>, default_sentinel_t> {\n\n    using it = iter_t<Iterable>;\n    using traits = std::iterator_traits<it>;\n\npublic:\n    using value_type = std::pair<typename traits::value_type, size_t>;\n    using reference = std::pair<typename traits::reference, size_t>;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n\nprivate:\n    it _last{};\n    it _first{};\n    Iterable _iterable{};\n    mutable BinaryPredicate _compare{};\n\n    LZ_CONSTEXPR_CXX_14 void next() {\n        _last =\n            detail::find_if(_first, _iterable.end(), [this](typename traits::reference val) { return _compare(*_first, val); });\n    }\n\npublic:\n    constexpr duplicates_iterator(const duplicates_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 duplicates_iterator& operator=(const duplicates_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr duplicates_iterator()\n        requires(std::default_initializable<it> && std::default_initializable<Iterable> &&\n                 std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = it,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value &&\n                                 std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr duplicates_iterator() noexcept(std::is_nothrow_default_constructible<it>::value &&\n                                             std::is_nothrow_default_constructible<Iterable>::value &&\n                                             std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 duplicates_iterator(I&& iterable, it i, BinaryPredicate compare) :\n        _last{ std::move(i) },\n        _first{ std::move(_last) }, // last is assigned in next()\n        _iterable{ std::forward<I>(iterable) },\n        _compare{ std::move(compare) } {\n        next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 duplicates_iterator& operator=(default_sentinel_t) {\n        _first = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return { *_first, static_cast<size_t>(_last - _first) };\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<reference>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        _first = std::move(_last);\n        next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_first != _iterable.begin());\n        _last = _first;\n\n        for (--_first; _first != _iterable.begin(); --_first) {\n            auto prev = std::prev(_first);\n            if (_compare(*prev, *_first)) {\n                return;\n            }\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const duplicates_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.begin() == other._iterable.begin() && _iterable.end() == other._iterable.end());\n        return _first == other._first;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(default_sentinel_t) const {\n        return _first == _iterable.end();\n    }\n};\n\ntemplate<class Iterable, class BinaryPredicate>\nclass duplicates_iterator<Iterable, BinaryPredicate, enable_if_t<!is_ra<iter_t<Iterable>>::value>>\n    : public iterator<duplicates_iterator<Iterable, BinaryPredicate>, std::pair<ref_t<iter_t<Iterable>>, size_t>,\n                      fake_ptr_proxy<std::pair<ref_t<iter_t<Iterable>>, size_t>>, diff_type<iter_t<Iterable>>,\n                      iter_cat_t<iter_t<Iterable>>, default_sentinel_t> {\n\n    using it = iter_t<Iterable>;\n    using traits = std::iterator_traits<iter_t<Iterable>>;\n\npublic:\n    using value_type = std::pair<typename traits::value_type, size_t>;\n    using reference = std::pair<typename traits::reference, size_t>;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n\nprivate:\n    it _last{};\n    it _first{};\n    Iterable _iterable{};\n    difference_type _last_distance{};\n    mutable BinaryPredicate _compare{};\n\n    LZ_CONSTEXPR_CXX_14 void next() {\n        _last_distance = 0;\n        _last = detail::find_if(_first, _iterable.end(), [this](typename traits::reference val) {\n            const auto condition = _compare(*_first, val);\n            if (!condition) {\n                ++_last_distance;\n            }\n            return condition;\n        });\n    }\n\npublic:\n    constexpr duplicates_iterator(const duplicates_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 duplicates_iterator& operator=(const duplicates_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr duplicates_iterator()\n        requires(std::default_initializable<it> && std::default_initializable<Iterable> &&\n                 std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = it,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value &&\n                                 std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr duplicates_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<Iterable>::value &&\n                                             std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 duplicates_iterator(I&& iterable, it i, BinaryPredicate compare) :\n        _last{ std::move(i) },\n        _first{ std::move(_last) },\n        _iterable{ std::forward<I>(iterable) },\n        _compare{ std::move(compare) } {\n        next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 duplicates_iterator& operator=(default_sentinel_t) {\n        _first = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return { *_first, _last_distance };\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<reference>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        _first = std::move(_last);\n        next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_first != _iterable.begin());\n        _last_distance = 1;\n        _last = _first;\n\n        for (--_first; _first != _iterable.begin(); --_first, ++_last_distance) {\n            const auto prev = std::prev(_first);\n            if (_compare(*prev, *_first)) {\n                return;\n            }\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const duplicates_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.begin() == other._iterable.begin() && _iterable.end() == other._iterable.end());\n        return _first == other._first;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(default_sentinel_t) const {\n        return _first == _iterable.end();\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/enumerate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ENUMERATE_ITERATOR_HPP\n#define LZ_ENUMERATE_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/procs/eager_size.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nclass enumerate_sentinel {\n    T value{};\n\n    template<class, class, class>\n    friend class enumerate_iterator;\n\n    template<class, class>\n    friend class enumerate_iterable;\n\n    explicit constexpr enumerate_sentinel(T v) noexcept(std::is_nothrow_move_constructible<T>::value) : value{ std::move(v) } {\n    }\n\npublic:\n#ifdef LZ_HAS_CXX_20\n    constexpr enumerate_sentinel()\n        requires(std::default_initializable<T>)\n    = default;\n#else\n    template<class I = T, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr enumerate_sentinel() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n#endif\n};\n\ntemplate<class Iterable, class IntType, class = void>\nclass enumerate_iterator;\n\ntemplate<class Iterable, class IntType>\nclass enumerate_iterator<Iterable, IntType, enable_if_t<is_bidi<iter_t<Iterable>>::value>>\n    : public iterator<enumerate_iterator<Iterable, IntType>, std::pair<IntType, ref_t<iter_t<Iterable>>>,\n                      fake_ptr_proxy<std::pair<IntType, ref_t<iter_t<Iterable>>>>, diff_type<iter_t<Iterable>>,\n                      iter_cat_t<iter_t<Iterable>>, enumerate_sentinel<IntType>> {\n\n    using iter = iter_t<Iterable>;\n    Iterable _iterable{};\n    iter _iterator{};\n    IntType _index{};\n\n    using traits = std::iterator_traits<iter>;\n\npublic:\n    using value_type = std::pair<IntType, typename traits::value_type>;\n    using reference = std::pair<IntType, typename traits::reference>;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n\n    constexpr enumerate_iterator(const enumerate_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 enumerate_iterator& operator=(const enumerate_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr enumerate_iterator()\n        requires(std::default_initializable<iter> && std::default_initializable<Iterable>)\n    = default;\n\n#else\n\n    template<class I = iter,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value>>\n    constexpr enumerate_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                            std::is_nothrow_default_constructible<Iterable>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr enumerate_iterator(I&& iterable, iter it, const IntType start) :\n        _iterable{ std::forward<I>(iterable) },\n        _iterator{ std::move(it) },\n        _index{ start } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 enumerate_iterator& operator=(enumerate_sentinel<IntType> s) {\n        _iterator = _iterable.end();\n        _index = s.value + static_cast<IntType>(lz::eager_size(_iterable));\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        ++_index;\n        ++_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        --_index;\n        --_iterator;\n    }\n\n    constexpr reference dereference() const {\n        return { _index, *_iterator };\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    constexpr bool eq(const enumerate_iterator& other) const {\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(enumerate_sentinel<IntType>) const {\n        return _iterator == _iterable.end();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type n) {\n        _index += static_cast<IntType>(n);\n        _iterator += n;\n    }\n\n    constexpr difference_type difference(const enumerate_iterator& other) const {\n        return _iterator - other._iterator;\n    }\n\n    constexpr difference_type difference(enumerate_sentinel<IntType>) const {\n        return _iterator - _iterable.end();\n    }\n};\n\ntemplate<class Iterable, class IntType>\nclass enumerate_iterator<Iterable, IntType, enable_if_t<!is_bidi<iter_t<Iterable>>::value>>\n    : public iterator<enumerate_iterator<Iterable, IntType>, std::pair<IntType, ref_t<iter_t<Iterable>>>,\n                      fake_ptr_proxy<std::pair<IntType, ref_t<iter_t<Iterable>>>>, diff_type<iter_t<Iterable>>,\n                      iter_cat_t<iter_t<Iterable>>, enumerate_sentinel<sentinel_t<Iterable>>> {\n\n    using iter = iter_t<Iterable>;\n    iter _iterator{};\n    IntType _index{};\n\n    using traits = std::iterator_traits<iter>;\n\npublic:\n    using value_type = std::pair<IntType, typename traits::value_type>;\n    using reference = std::pair<IntType, typename traits::reference>;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n\n    constexpr enumerate_iterator(const enumerate_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 enumerate_iterator& operator=(const enumerate_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr enumerate_iterator()\n        requires(std::default_initializable<iter>)\n    = default;\n\n#else\n\n    template<class I = iter, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr enumerate_iterator() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    constexpr enumerate_iterator(iter it, const IntType start) : _iterator{ std::move(it) }, _index{ start } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 enumerate_iterator& operator=(enumerate_sentinel<sentinel_t<Iterable>> s) {\n        _iterator = std::move(s.value);\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        ++_index;\n        ++_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        --_index;\n        --_iterator;\n    }\n\n    constexpr reference dereference() const {\n        return { _index, *_iterator };\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    constexpr bool eq(const enumerate_iterator& other) const {\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(const enumerate_sentinel<sentinel_t<Iterable>>& other) const {\n        return _iterator == other.value;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/except.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCEPT_ITERATOR_HPP\n#define LZ_EXCEPT_ITERATOR_HPP\n\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/detail/algorithm/lower_bound.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/procs/size.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class Iterable2, class BinaryPredicate>\nclass except_iterator : public iterator<except_iterator<Iterable, Iterable2, BinaryPredicate>, ref_t<iter_t<Iterable>>,\n                                        fake_ptr_proxy<ref_t<iter_t<Iterable>>>, diff_type<iter_t<Iterable>>,\n                                        bidi_strongest_cat<iter_cat_t<iter_t<Iterable>>>, default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n    using iter_traits = std::iterator_traits<iter>;\n\npublic:\n    using value_type = typename iter_traits::value_type;\n    using difference_type = typename iter_traits::difference_type;\n    using reference = typename iter_traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr except_iterator(const except_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 except_iterator& operator=(const except_iterator&) = default;\n\nprivate:\n    Iterable _iterable{};\n    Iterable2 _to_except{};\n    iter _iterator{};\n    mutable BinaryPredicate _predicate{};\n\n    LZ_CONSTEXPR_CXX_14 void find_next() {\n        _iterator = detail::find_if(std::move(_iterator), _iterable.end(), [this](reference value) {\n            const auto it = detail::sized_lower_bound(_to_except.begin(), value, _predicate,\n                                                      static_cast<difference_type>(lz::size(_to_except)));\n            return it == _to_except.end() || _predicate(value, *it);\n        });\n    }\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr except_iterator()\n        requires(std::default_initializable<Iterable> && std::default_initializable<Iterable2> &&\n                 std::default_initializable<BinaryPredicate> && std::default_initializable<iter>)\n    = default;\n\n#else\n\n    template<\n        class I = Iterable,\n        class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable2>::value &&\n                            std::is_default_constructible<BinaryPredicate>::value && std::is_default_constructible<iter>::value>>\n    constexpr except_iterator() noexcept(std::is_nothrow_default_constructible<Iterable2>::value &&\n                                         std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<BinaryPredicate>::value &&\n                                         std::is_nothrow_default_constructible<iter>::value) {\n    }\n\n#endif\n\n    template<class I, class I2>\n    LZ_CONSTEXPR_CXX_14 except_iterator(I&& iterable, iter it, I2&& to_except, BinaryPredicate compare) :\n        _iterable{ std::forward<I>(iterable) },\n        _to_except{ std::forward<I2>(to_except) },\n        _iterator{ std::move(it) },\n        _predicate{ std::move(compare) } {\n        if (_to_except.begin() == _to_except.end()) {\n            return;\n        }\n        find_next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 except_iterator& operator=(default_sentinel_t) {\n        _iterator = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_iterator;\n        find_next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_iterator != _iterable.begin());\n        do {\n            --_iterator;\n            const auto it = detail::sized_lower_bound(_to_except.begin(), *_iterator, _predicate,\n                                                      static_cast<difference_type>(lz::size(_to_except)));\n            if (it == _to_except.end() || _predicate(*_iterator, *it)) {\n                return;\n            }\n        } while (_iterator != _iterable.begin());\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const except_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.end() == other._iterable.end() && _iterable.begin() == other._iterable.begin() &&\n                             _to_except.begin() == other._to_except.begin() && _to_except.end() == other._to_except.end());\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/exclude.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCLUDE_ITERATOR_HPP\n#define LZ_EXCLUDE_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/next_fast.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/procs/eager_size.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable>\nclass exclude_iterator\n    : public iterator<exclude_iterator<Iterable>, ref_t<iter_t<Iterable>>, fake_ptr_proxy<ref_t<iter_t<Iterable>>>,\n                      diff_type<iter_t<Iterable>>, iter_cat_t<iter_t<Iterable>>, default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n    using traits = std::iterator_traits<iter>;\n\npublic:\n    using value_type = typename traits::value_type;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\nprivate:\n    Iterable _iterable{};\n    iter _iterator{};\n    difference_type _index{};\n    difference_type _from{};\n    difference_type _to{};\n\npublic:\n    constexpr exclude_iterator(const exclude_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 exclude_iterator& operator=(const exclude_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr exclude_iterator()\n        requires(std::default_initializable<iter> && std::default_initializable<Iterable>)\n    = default;\n\n#else\n\n    template<class I = iter, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr exclude_iterator() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_17 exclude_iterator(I&& iterable, iter begin, const difference_type from, const difference_type to,\n                                         const difference_type start_index) :\n        _iterable{ std::forward<I>(iterable) },\n        _iterator{ std::move(begin) },\n        _index{ start_index },\n        _from{ from },\n        _to{ to } {\n        LZ_ASSERT(from <= to, \"From must be less than to\");\n        if (_iterator != _iterable.end() && _from == 0 && _index == 0) {\n            _iterator = next_fast_safe(_iterable, _to);\n            _index = _to;\n        }\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    LZ_CONSTEXPR_CXX_14 exclude_iterator& operator=(default_sentinel_t) {\n        _iterator = _iterable.end();\n        if constexpr (is_bidi_v<iter>) {\n            _index = static_cast<difference_type>(lz::eager_size(_iterable));\n        }\n        return *this;\n    }\n\n#else\n\n    template<class I = iter>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<is_bidi<I>::value, exclude_iterator&> operator=(default_sentinel_t) {\n        _iterator = _iterable.end();\n        _index = static_cast<difference_type>(lz::eager_size(_iterable));\n        return *this;\n    }\n\n    template<class I = iter>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!is_bidi<I>::value, exclude_iterator&> operator=(default_sentinel_t) {\n        _iterator = _iterable.end();\n        return *this;\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_iterator;\n        ++_index;\n        if (_index == _from) {\n            _iterator = std::next(std::move(_iterator), _to - _from);\n            _index = _to;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_iterator != _iterable.begin());\n        if (_index == _to) {\n            _iterator = std::prev(std::move(_iterator), _to - _from);\n            _index = _from;\n        }\n        --_iterator;\n        --_index;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const exclude_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_from == other._from && _to == other._to && _iterable.begin() == other._iterable.begin() &&\n                             _iterable.end() == other._iterable.end());\n        const auto diff = _index - other._index;\n\n        if (_iterator > other._iterator) {\n            return _index < _to || other._index >= _from ? diff : diff - (_to - _from);\n        }\n        return _index >= _from || other._index < _to ? diff : diff + (_to - _from);\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(default_sentinel_t) const {\n        return _iterator - _iterable.end() + (_index >= _from ? 0 : _to - _from);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        // Avoid subtraction-based comparison to prevent -Wstrict-overflow; compare directly instead.\n        LZ_ASSERT_ADDABLE((_iterable.end() - _iterable.begin()) >= (_to - _from));\n        if (offset == 0) {\n            return;\n        }\n\n        _iterator += offset;\n        _index += offset;\n        const auto is_positive_offset = offset > 0;\n\n        if (((!is_positive_offset || _index < _from) && (is_positive_offset || _index >= _to)) || (_from == 0)) {\n            return;\n        }\n\n        const auto to_skip = _to - _from;\n        _iterator += is_positive_offset ? to_skip : -to_skip;\n        _index += is_positive_offset ? to_skip : -to_skip;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const exclude_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_from == other._from && _to == other._to && _iterable.begin() == other._iterable.begin() &&\n                             _iterable.end() == other._iterable.end());\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_EXCLUDE_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/exclusive_scan.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCLUSIVE_SCAN_ITERATOR_HPP\n#define LZ_EXCLUSIVE_SCAN_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterator, class S, class T, class BinaryOp>\nclass exclusive_scan_iterator : public iterator<exclusive_scan_iterator<Iterator, S, T, BinaryOp>, T&, fake_ptr_proxy<T&>,\n                                                diff_type<Iterator>, std::forward_iterator_tag, default_sentinel_t> {\n    Iterator _iterator{};\n    mutable T _reducer{};\n    S _end{};\n    bool _reached_end{ true };\n    mutable BinaryOp _binary_op{};\n\n    using traits = std::iterator_traits<Iterator>;\n\npublic:\n    using reference = T&;\n    using value_type = remove_cvref_t<reference>;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n\n    constexpr exclusive_scan_iterator(const exclusive_scan_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 exclusive_scan_iterator& operator=(const exclusive_scan_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr exclusive_scan_iterator()\n        requires(std::default_initializable<Iterator> && std::default_initializable<S> && std::default_initializable<T> &&\n                 std::default_initializable<BinaryOp>)\n    = default;\n\n#else\n\n    template<class I = Iterator,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<S>::value &&\n                                 std::is_default_constructible<T>::value && std::is_default_constructible<BinaryOp>::value>>\n    constexpr exclusive_scan_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                                 std::is_nothrow_default_constructible<S>::value &&\n                                                 std::is_nothrow_default_constructible<T>::value &&\n                                                 std::is_nothrow_default_constructible<BinaryOp>::value) {\n    }\n\n#endif\n\n    constexpr exclusive_scan_iterator(Iterator it, S end, T init, BinaryOp binary_op) :\n        _iterator{ std::move(it) },\n        _reducer{ std::move(init) },\n        _end{ std::move(end) },\n        _reached_end{ _iterator == _end },\n        _binary_op{ std::move(binary_op) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 exclusive_scan_iterator& operator=(default_sentinel_t) {\n        _iterator = _end;\n        _reached_end = true;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return _reducer;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        if (_iterator == _end) {\n            _reached_end = true;\n            return;\n        }\n        _reducer = _binary_op(std::move(_reducer), *_iterator);\n        ++_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const exclusive_scan_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_end == other._end);\n        return _iterator == other._iterator && _reached_end == other._reached_end;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _end && _reached_end;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/filter.hpp",
    "content": "#pragma once\n\n#ifndef LZ_FILTER_ITERATOR_HPP\n#define LZ_FILTER_ITERATOR_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n#include <algorithm>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class UnaryPredicate>\nclass filter_iterator\n    : public iterator<filter_iterator<Iterable, UnaryPredicate>, ref_t<iter_t<Iterable>>, fake_ptr_proxy<ref_t<iter_t<Iterable>>>,\n                      diff_type<iter_t<Iterable>>, bidi_strongest_cat<iter_cat_t<iter_t<Iterable>>>, default_sentinel_t> {\n\n    using it = iter_t<Iterable>;\n    using traits = std::iterator_traits<it>;\n\npublic:\n    using value_type = typename traits::value_type;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr filter_iterator(const filter_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 filter_iterator& operator=(const filter_iterator&) = default;\n\n    LZ_CONSTEXPR_CXX_14 void find() {\n        _iterator = detail::find_if(std::move(_iterator), _iterable.end(), _predicate);\n    }\n\nprivate:\n    it _iterator{};\n    Iterable _iterable{};\n    mutable UnaryPredicate _predicate{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr filter_iterator()\n        requires(std::default_initializable<Iterable> && std::default_initializable<it> &&\n                 std::default_initializable<UnaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = it,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value &&\n                                 std::is_default_constructible<UnaryPredicate>::value>>\n    constexpr filter_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<Iterable>::value &&\n                                         std::is_nothrow_default_constructible<UnaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 filter_iterator(I&& iterable, it iter, UnaryPredicate up) :\n        _iterator{ std::move(iter) },\n        _iterable{ std::forward<I>(iterable) },\n        _predicate{ std::move(up) } {\n        find();\n    }\n\n    LZ_CONSTEXPR_CXX_14 filter_iterator& operator=(default_sentinel_t) {\n        _iterator = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_iterator;\n        find();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_iterator != _iterable.begin());\n        do {\n            --_iterator;\n        } while (!_predicate(*_iterator) && _iterator != _iterable.begin());\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const filter_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.end() == other._iterable.end() && _iterable.begin() == other._iterable.begin());\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/flatten.hpp",
    "content": "#pragma once\n\n#ifndef LZ_FLATTEN_ITERATOR_HPP\n#define LZ_FLATTEN_ITERATOR_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class T>\n[[nodiscard]] constexpr size_t count_dims_fn() {\n    if constexpr (is_iterable_v<T>) {\n        return 1 + count_dims_fn<ref_iterable_t<T>>();\n    }\n    else {\n        return 0;\n    }\n}\n\n#else\n\ntemplate<bool>\nstruct count_dims_impl;\n\ntemplate<>\nstruct count_dims_impl<false> {\n#ifdef LZ_HAS_CXX_11\n\n    template<class>\n    using type = std::integral_constant<size_t, 0>;\n\n#else\n\n    template<class>\n    static constexpr size_t value = 0;\n\n#endif\n};\n\ntemplate<>\nstruct count_dims_impl<true> {\n    template<class T>\n    using iterable_type = decltype(*detail::begin(std::declval<T>()));\n\n#ifdef LZ_HAS_CXX_11\n\n    template<class T>\n    using type =\n        std::integral_constant<size_t,\n                               1 + count_dims_impl<is_iterable<iterable_type<T>>::value>::template type<iterable_type<T>>::value>;\n\n#else\n\n    template<class T>\n    static constexpr size_t value = 1 + count_dims_impl<is_iterable<iterable_type<T>>::value>::template value<iterable_type<T>>;\n\n#endif\n};\n\n#endif\n\n#ifdef LZ_HAS_CXX_11\n\ntemplate<class T>\nusing count_dims = typename count_dims_impl<is_iterable<T>::value>::template type<T>;\n\n#elif !defined(LZ_HAS_CXX_17)\n\ntemplate<class T>\nusing count_dims = std::integral_constant<size_t, count_dims_impl<is_iterable<T>::value>::template value<T>>;\n\n#else\n\ntemplate<class T>\nusing count_dims = std::integral_constant<size_t, count_dims_fn<T>()>;\n\n#endif\n\n// Improvement of https://stackoverflow.com/a/21076724/8729023\ntemplate<class Iterable>\nclass flatten_wrapper\n    : public iterator<flatten_wrapper<Iterable>, ref_t<iter_t<Iterable>>, fake_ptr_proxy<ref_t<iter_t<Iterable>>>,\n                      diff_type<iter_t<Iterable>>, iter_cat_t<iter_t<Iterable>>, default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n\n    iter _iterator{};\n    maybe_owned<Iterable> _iterable{};\n\n    using traits = std::iterator_traits<iter>;\n\npublic:\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n    using value_type = typename traits::value_type;\n    using difference_type = typename traits::difference_type;\n\n    constexpr flatten_wrapper(const flatten_wrapper&) = default;\n    LZ_CONSTEXPR_CXX_14 flatten_wrapper& operator=(const flatten_wrapper&) = default;\n\n    template<class I>\n    constexpr flatten_wrapper(I&& iterable, iter it) : _iterator{ std::move(it) }, _iterable{ std::forward<I>(iterable) } {\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr flatten_wrapper()\n        requires(std::default_initializable<iter> && std::default_initializable<maybe_owned<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = iter, class = enable_if_t<std::is_default_constructible<I>::value &&\n                                                 std::is_default_constructible<maybe_owned<Iterable>>::value>>\n    constexpr flatten_wrapper() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<maybe_owned<Iterable>>::value) {\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 flatten_wrapper& operator=(default_sentinel_t) {\n        initialize_last();\n        return *this;\n    }\n\n    constexpr bool has_next() const {\n        return _iterator != _iterable.end();\n    }\n\n    constexpr bool has_prev() const {\n        return _iterator != _iterable.begin();\n    }\n\n    constexpr bool has_prev_inner() const {\n        return has_prev();\n    }\n\n    constexpr bool has_next_inner() const {\n        return has_next();\n    }\n\n    constexpr iter iterator() const {\n        return _iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void iterator(iter c) {\n        _iterator = std::move(c);\n    }\n\n    constexpr sentinel_t<Iterable> end() const {\n        return _iterable.end();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void initialize_last() {\n        _iterator = _iterable.end();\n    }\n\n    constexpr difference_type current_to_begin() const {\n        return _iterator - _iterable.begin();\n    }\n\n    constexpr difference_type end_to_current() const {\n        return _iterable.end() - _iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const flatten_wrapper& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.begin() == other._iterable.begin() && _iterable.end() == other._iterable.end());\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _iterable.end();\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_iterator != _iterable.begin());\n        --_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type n) {\n        LZ_ASSERT_SUB_ADDABLE(n < 0 ? -n <= _iterator - _iterable.begin() : n <= _iterable.end() - _iterator);\n        _iterator += n;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const flatten_wrapper& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.begin() == other._iterable.begin() && _iterable.end() == other._iterable.end());\n        return _iterator - other._iterator;\n    }\n\n    constexpr difference_type difference(default_sentinel_t) const {\n        return _iterator - _iterable.end();\n    }\n};\n\ntemplate<class, size_t>\nclass flatten_iterator;\n\ntemplate<class Iterable, size_t N>\nusing inner = flatten_iterator<remove_ref_t<ref_iterable_t<Iterable>>, N - 1>;\n\ntemplate<class Iterable, size_t N>\nusing iter_cat = typename std::common_type<iter_cat_t<inner<Iterable, N>>, iter_cat_t<flatten_wrapper<Iterable>>>::type;\n\ntemplate<class Iterable, size_t N>\nclass flatten_iterator\n    : public iterator<flatten_iterator<Iterable, N>, ref_t<inner<Iterable, N>>, fake_ptr_proxy<ref_t<inner<Iterable, N>>>,\n                      diff_type<inner<Iterable, N>>, iter_cat<Iterable, N>, default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n    using this_inner = inner<Iterable, N>;\n\n    LZ_CONSTEXPR_CXX_14 void find_next_non_empty_inner() {\n        using ref = decltype(*_outer_iter.iterator());\n\n        ++_outer_iter;\n        _outer_iter.iterator(detail::find_if(_outer_iter.iterator(), _outer_iter.end(), [this](ref inner) {\n            _inner_iter = this_inner(inner, inner.begin());\n            return _inner_iter.has_next();\n        }));\n\n        if (_inner_iter.has_next()) {\n            return;\n        }\n        _inner_iter = this_inner{};\n    }\n\n    LZ_CONSTEXPR_CXX_14 void find_prev_non_empty_inner() {\n        while (!_inner_iter.has_prev()) {\n            if (!_outer_iter.has_prev()) {\n                return;\n            }\n            previous_outer();\n        }\n    }\n\npublic:\n    using reference = typename this_inner::reference;\n    using pointer = typename this_inner::pointer;\n    using value_type = typename this_inner::value_type;\n    using difference_type = typename this_inner::difference_type;\n\nprivate:\n    LZ_CONSTEXPR_CXX_14 void advance() {\n        if (_inner_iter.has_next()) {\n            return;\n        }\n\n        find_next_non_empty_inner();\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    constexpr void previous_outer() {\n        --_outer_iter;\n        if constexpr (!is_sentinel_v<iter_t<Iterable>, sentinel_t<Iterable>>) {\n            _inner_iter = this_inner(*_outer_iter, (*_outer_iter).end());\n        }\n        else {\n            _inner_iter = this_inner(*_outer_iter, (*_outer_iter).begin() + ((*_outer_iter).end() - (*_outer_iter).begin()));\n        }\n    }\n\n#else\n\n    template<class I = iter_t<Iterable>>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!is_sentinel<I, sentinel_t<Iterable>>::value> previous_outer() {\n        --_outer_iter;\n        _inner_iter = this_inner(*_outer_iter, (*_outer_iter).end());\n    }\n\n    template<class I = iter_t<Iterable>>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<is_sentinel<I, sentinel_t<Iterable>>::value> previous_outer() {\n        --_outer_iter;\n        _inner_iter = this_inner(*_outer_iter, (*_outer_iter).begin() + ((*_outer_iter).end() - (*_outer_iter).begin()));\n    }\n\n#endif\n\n    flatten_wrapper<Iterable> _outer_iter{};\n    this_inner _inner_iter{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr flatten_iterator()\n        requires(std::default_initializable<flatten_wrapper<Iterable>> && std::default_initializable<this_inner>)\n    = default;\n\n#else\n\n    template<class I = decltype(_outer_iter),\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<this_inner>::value>>\n    constexpr flatten_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                          std::is_nothrow_default_constructible<this_inner>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 flatten_iterator(I&& iterable, iter it) : _outer_iter{ std::forward<I>(iterable), std::move(it) } {\n        if (_outer_iter.has_next()) {\n            _inner_iter = this_inner(*_outer_iter, (*_outer_iter).begin());\n            this->advance();\n        }\n    }\n\n    constexpr flatten_iterator() = default;\n\n    LZ_CONSTEXPR_CXX_14 flatten_iterator& operator=(default_sentinel_t) {\n        _inner_iter = this_inner{};\n        _outer_iter = lz::default_sentinel;\n        return *this;\n    }\n\n    constexpr bool has_next() const {\n        return _outer_iter.has_next();\n    }\n\n    constexpr bool has_prev() const {\n        return _outer_iter.has_prev() || _inner_iter.has_prev();\n    }\n\n    constexpr bool has_prev_inner() const {\n        return _inner_iter.has_prev_inner();\n    }\n\n    constexpr bool has_next_inner() const {\n        return _inner_iter.has_next();\n    }\n\n    constexpr difference_type current_to_begin() const {\n        return _inner_iter.current_to_begin();\n    }\n\n    constexpr difference_type end_to_current() const {\n        return _inner_iter.end_to_current();\n    }\n\n    constexpr bool eq(const flatten_iterator& other) const {\n        return _outer_iter == other._outer_iter && _inner_iter == other._inner_iter;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return !has_next();\n    }\n\n    constexpr reference dereference() const {\n        return *_inner_iter;\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_inner_iter;\n        this->advance();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        if (!_outer_iter.has_next()) {\n            initialize_last();\n            --_inner_iter;\n            return;\n        }\n\n        if (_inner_iter.has_prev()) {\n            --_inner_iter;\n            if (_inner_iter.has_next_inner()) {\n                return;\n            }\n        }\n\n        find_prev_non_empty_inner();\n        if (_inner_iter.has_prev()) {\n            --_inner_iter;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 void initialize_last() {\n        if (_inner_iter.has_prev_inner()) {\n            // inner is not empty, return\n            return;\n        }\n\n        find_prev_non_empty_inner();\n        if (_inner_iter.has_prev_inner()) {\n            // inner is not empty, return\n            return;\n        }\n        // inner is empty, proceed to previous\n        _inner_iter.initialize_last();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(difference_type n) {\n        while (n > 0) {\n            // Check the distance of the inner iterator to the end\n            const auto inner_distance = _inner_iter.end_to_current();\n            if (n < inner_distance) {\n                // n fits, just increment the inner iterator\n                _inner_iter += n;\n                return;\n            }\n            // n doesn't fit, increment the inner iterator to the end\n            _inner_iter += inner_distance;\n            n -= inner_distance;\n            // Check if the inner iterator (possibily recursively) has next\n            if (_inner_iter.has_next()) {\n                continue;\n            }\n            // Inner didnt have any elements left, go to next outer\n            find_next_non_empty_inner();\n            // Check if the find_if found an inner iterator with elements\n            if (_inner_iter.has_next()) {\n                continue;\n            }\n            // Outer iterator has no next, we are done. Set _inner_iter to end/empty\n            _inner_iter = this_inner{};\n            return;\n        }\n\n        while (n < 0) {\n            // Because end may be empty, initialized with = {}, we initialize all iterators with end\n            initialize_last();\n\n            // Get inner distance\n            difference_type inner_distance = _inner_iter.current_to_begin();\n            if (-n <= inner_distance) {\n                // Fits\n                _inner_iter += n;\n                return;\n            }\n\n            n += inner_distance;\n            // Doesn't fit, decrement the inner iterator to the beginning, possibly recursively\n            _inner_iter -= inner_distance;\n\n            if (_inner_iter.has_prev()) {\n                // _inner_iter.outer still has previous elements\n                continue;\n            }\n\n            // Get the previous non empty inner iterator\n            find_prev_non_empty_inner();\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const flatten_iterator& other) const {\n        if (_outer_iter == other._outer_iter) {\n            return _inner_iter - other._inner_iter;\n        }\n\n        if (_outer_iter > other._outer_iter) {\n            // Make sure to make distance negative or positive again\n            return -other.difference(*this);\n        }\n\n        difference_type total = 0;\n        auto outer_iter = _outer_iter;\n\n        if (outer_iter.has_next()) {\n            // If the first outer iterator has next, we need to subtract the distance from the inner iterator\n            total -= this_inner(*outer_iter, (*outer_iter).begin() + ((*outer_iter).end() - (*outer_iter).begin())) - _inner_iter;\n            for (++outer_iter; outer_iter != other._outer_iter; ++outer_iter) {\n                total -= (this_inner(*outer_iter, (*outer_iter).begin() + ((*outer_iter).end() - (*outer_iter).begin())) -\n                          this_inner(*outer_iter, (*outer_iter).begin()));\n            }\n        }\n        if (other._outer_iter.has_next()) {\n            total += this_inner(*other._outer_iter, (*other._outer_iter).begin()) - other._inner_iter;\n        }\n\n        return total;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(default_sentinel_t) const {\n        difference_type total = 0;\n        auto outer_iter = _outer_iter;\n\n        if (outer_iter.has_next()) {\n            total -= this_inner(*outer_iter, (*outer_iter).begin() + ((*outer_iter).end() - (*outer_iter).begin())) - _inner_iter;\n            for (++outer_iter; outer_iter.has_next(); ++outer_iter) {\n                total -= (this_inner(*outer_iter, (*outer_iter).begin() + ((*outer_iter).end() - (*outer_iter).begin())) -\n                          this_inner(*outer_iter, (*outer_iter).begin()));\n            }\n        }\n\n        return total;\n    }\n};\n\ntemplate<class Iterable>\nclass flatten_iterator<Iterable, 0>\n    : public iterator<flatten_iterator<Iterable, 0>, ref_t<flatten_wrapper<Iterable>>,\n                      fake_ptr_proxy<ref_t<flatten_wrapper<Iterable>>>, diff_type<flatten_wrapper<Iterable>>,\n                      iter_cat_t<flatten_wrapper<Iterable>>, default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n    using traits = std::iterator_traits<iter>;\n\n    flatten_wrapper<Iterable> _iterator{};\n\npublic:\n    using pointer = typename traits::pointer;\n    using reference = typename traits::reference;\n    using value_type = typename traits::value_type;\n    using difference_type = typename traits::difference_type;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr flatten_iterator()\n        requires(std::default_initializable<flatten_wrapper<Iterable>>)\n    = default;\n\n#else\n\n    template<class I = decltype(_iterator), class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr flatten_iterator() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr flatten_iterator(I&& iterable, iter it) : _iterator{ std::forward<I>(iterable), std::move(it) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 flatten_iterator& operator=(default_sentinel_t) {\n        _iterator = lz::default_sentinel;\n        return *this;\n    }\n\n    constexpr bool has_prev() const {\n        return _iterator.has_prev();\n    }\n\n    constexpr bool has_next() const {\n        return _iterator.has_next();\n    }\n\n    constexpr bool has_prev_inner() const {\n        return _iterator.has_prev_inner();\n    }\n\n    constexpr bool has_next_inner() const {\n        return _iterator.has_next_inner();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void initialize_last() {\n        _iterator.initialize_last();\n    }\n\n    constexpr difference_type current_to_begin() const {\n        return _iterator.current_to_begin();\n    }\n\n    constexpr difference_type end_to_current() const {\n        return _iterator.end_to_current();\n    }\n\n    constexpr reference dereference() const {\n        return *_iterator;\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    constexpr bool eq(const flatten_iterator& other) const {\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == lz::default_sentinel;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        --_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type n) {\n        _iterator += n;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const flatten_iterator& other) const {\n        return _iterator - other._iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(default_sentinel_t) const {\n        return _iterator - lz::default_sentinel;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_FLATTEN_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/generate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GENERATE_ITERATOR_HPP\n#define LZ_GENERATE_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/func_ret_type.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class GeneratorFunc, bool /* is inf */>\nclass generate_iterator;\n\ntemplate<class GeneratorFunc>\nclass generate_iterator<GeneratorFunc, false>\n    : public iterator<generate_iterator<GeneratorFunc, false>, func_ret_type<GeneratorFunc>,\n                      fake_ptr_proxy<func_ret_type<GeneratorFunc>>, ptrdiff_t, std::forward_iterator_tag, default_sentinel_t> {\n\n    mutable GeneratorFunc _func{};\n    ptrdiff_t _current{};\n\npublic:\n    using reference = func_ret_type<GeneratorFunc>;\n    using value_type = remove_cvref_t<reference>;\n    using difference_type = ptrdiff_t;\n    using pointer = fake_ptr_proxy<reference>;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr generate_iterator()\n        requires(std::default_initializable<GeneratorFunc>)\n    = default;\n\n#else\n\n    template<class G = GeneratorFunc, class = enable_if_t<std::is_default_constructible<G>::value>>\n    constexpr generate_iterator() noexcept(std::is_nothrow_default_constructible<G>::value) {\n    }\n\n#endif\n\n    constexpr generate_iterator(GeneratorFunc generator_func, const ptrdiff_t amount) :\n        _func{ std::move(generator_func) },\n        _current{ amount } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 generate_iterator& operator=(default_sentinel_t) noexcept {\n        _current = 0;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return _func();\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        --_current;\n    }\n\n    constexpr bool eq(const generate_iterator& other) const noexcept {\n        return _current == other._current;\n    }\n\n    constexpr bool eq(default_sentinel_t) const noexcept {\n        return _current == 0;\n    }\n};\n\ntemplate<class GeneratorFunc>\nclass generate_iterator<GeneratorFunc, true>\n    : public iterator<generate_iterator<GeneratorFunc, true>, func_ret_type<GeneratorFunc>,\n                      fake_ptr_proxy<func_ret_type<GeneratorFunc>>, std::ptrdiff_t, std::forward_iterator_tag,\n                      default_sentinel_t> {\n\n    mutable GeneratorFunc _func{};\n\npublic:\n    using reference = func_ret_type<GeneratorFunc>;\n    using value_type = remove_cvref_t<reference>;\n    using difference_type = std::ptrdiff_t;\n    using pointer = fake_ptr_proxy<reference>;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr generate_iterator()\n        requires(std::default_initializable<GeneratorFunc>)\n    = default;\n\n#else\n\n    template<class G = GeneratorFunc, class = enable_if_t<std::is_default_constructible<G>::value>>\n    constexpr generate_iterator() {\n    }\n\n#endif\n\n    explicit constexpr generate_iterator(GeneratorFunc generator_func) : _func{ std::move(generator_func) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 generate_iterator& operator=(default_sentinel_t) noexcept {\n        return *this;\n    }\n\n    constexpr reference dereference() const {\n        return _func();\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() const noexcept {\n    }\n\n    constexpr bool eq(const generate_iterator&) const noexcept {\n        return false;\n    }\n\n    constexpr bool eq(default_sentinel_t) const noexcept {\n        return false;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/generate_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GENERATE_WHILE_ITERATOR_HPP\n#define LZ_GENERATE_WHILE_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/func_ret_type.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\n// 0 is value_type, 1 is bool\ntemplate<class GeneratorFunc>\nclass generate_while_iterator\n    : public iterator<generate_while_iterator<GeneratorFunc>, typename std::tuple_element<0, func_ret_type<GeneratorFunc>>::type,\n                      fake_ptr_proxy<typename std::tuple_element<1, func_ret_type<GeneratorFunc>>::type>, std::ptrdiff_t,\n                      std::input_iterator_tag, default_sentinel_t> {\n\n    using fn_return_type = func_ret_type<GeneratorFunc>;\n    using type = typename std::tuple_element<0, func_ret_type<GeneratorFunc>>::type;\n    using boolean_type = typename std::tuple_element<1, fn_return_type>::type;\n\n    fn_return_type _last_returned{ type{}, static_cast<boolean_type>(true) };\n    mutable GeneratorFunc _func{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr generate_while_iterator()\n        requires(std::default_initializable<GeneratorFunc> && std::default_initializable<fn_return_type>)\n    = default;\n\n#else\n\n    template<class G = GeneratorFunc,\n             class = enable_if_t<std::is_default_constructible<G>::value && std::is_default_constructible<fn_return_type>::value>>\n    constexpr generate_while_iterator() noexcept(std::is_nothrow_default_constructible<G>::value &&\n                                                 std::is_nothrow_default_constructible<fn_return_type>::value) {\n    }\n\n#endif\n\n    using reference = type;\n    using value_type = remove_cvref_t<reference>;\n    using difference_type = std::ptrdiff_t;\n    using pointer = fake_ptr_proxy<reference>;\n\n    LZ_CONSTEXPR_CXX_14 generate_while_iterator(GeneratorFunc generator_func, fn_return_type last_returned) :\n        _last_returned{ std::move(last_returned) },\n        _func{ std::move(generator_func) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 generate_while_iterator& operator=(default_sentinel_t) noexcept {\n        std::get<1>(_last_returned) = false;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return std::get<0>(_last_returned);\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        _last_returned = _func();\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const generate_while_iterator& it) const noexcept {\n        if (std::get<1>(_last_returned) && std::get<1>(it._last_returned)) {\n            return false;\n        }\n        return std::get<1>(_last_returned) == std::get<1>(it._last_returned);\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(default_sentinel_t) const noexcept {\n        return static_cast<bool>(!std::get<1>(_last_returned));\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/group_by.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GROUP_BY_ITERATOR_HPP\n#define LZ_GROUP_BY_ITERATOR_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/basic_iterable.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class BinaryPredicate>\nclass group_by_iterator : public iterator<group_by_iterator<Iterable, BinaryPredicate>,\n                                          std::pair<ref_t<iter_t<Iterable>>, basic_iterable<iter_t<Iterable>>>,\n                                          fake_ptr_proxy<std::pair<ref_t<iter_t<Iterable>>, basic_iterable<iter_t<Iterable>>>>,\n                                          ptrdiff_t, bidi_strongest_cat<iter_cat_t<iter_t<Iterable>>>, default_sentinel_t> {\n    using it = iter_t<Iterable>;\n\n    it _sub_range_end{};\n    it _sub_range_begin{};\n    Iterable _iterable{};\n    mutable BinaryPredicate _comparer{};\n\n    using ref_type = ref_t<it>;\n\n    LZ_CONSTEXPR_CXX_14 void find_next(ref_type last_seen) {\n        _sub_range_end = detail::find_if(std::move(_sub_range_end), _iterable.end(),\n                                         [this, &last_seen](ref_type v) { return !_comparer(v, last_seen); });\n    }\n\n    LZ_CONSTEXPR_CXX_14 void advance() {\n        if (_sub_range_end == _iterable.end()) {\n            return;\n        }\n        ref_type last_seen = *_sub_range_end;\n        ++_sub_range_end;\n        find_next(last_seen);\n    }\n\npublic:\n    using value_type = std::pair<remove_cvref_t<ref_type>, basic_iterable<it>>;\n    using reference = std::pair<ref_type, basic_iterable<it>>;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = std::ptrdiff_t;\n\n    constexpr group_by_iterator(const group_by_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 group_by_iterator& operator=(const group_by_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr group_by_iterator()\n        requires(std::default_initializable<it> && std::default_initializable<Iterable> &&\n                 std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = it,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value &&\n                                 std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr group_by_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                           std::is_nothrow_default_constructible<Iterable>::value &&\n                                           std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 group_by_iterator(I&& iterable, it iter, BinaryPredicate binary_predicate) :\n        _sub_range_end{ iter },\n        _sub_range_begin{ std::move(iter) },\n        _iterable{ std::forward<I>(iterable) },\n        _comparer{ std::move(binary_predicate) } {\n\n        advance();\n    }\n\n    LZ_CONSTEXPR_CXX_14 group_by_iterator& operator=(default_sentinel_t) {\n        _sub_range_begin = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        return { *_sub_range_begin, { _sub_range_begin, _sub_range_end } };\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        _sub_range_begin = _sub_range_end;\n        advance();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_sub_range_begin != _iterable.begin());\n        _sub_range_end = _sub_range_begin;\n        auto prev = --_sub_range_begin;\n\n        ref_type last_seen = *_sub_range_begin;\n        while (_sub_range_begin != _iterable.begin() && _comparer(*_sub_range_begin, last_seen)) {\n            prev = _sub_range_begin;\n            --_sub_range_begin;\n        }\n\n        if (_sub_range_begin == _iterable.begin() && _comparer(*_sub_range_begin, last_seen)) {\n            return;\n        }\n        _sub_range_begin = std::move(prev);\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const group_by_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.end() == other._iterable.end() && _iterable.begin() == other._iterable.begin());\n        return _sub_range_begin == other._sub_range_begin;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _sub_range_begin == _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_GROUP_BY_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/inclusive_scan.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INCLUSIVE_SCAN_ITERATOR_HPP\n#define LZ_INCLUSIVE_SCAN_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterator, class S, class T, class BinaryOp>\nclass inclusive_scan_iterator : public iterator<inclusive_scan_iterator<Iterator, S, T, BinaryOp>, T&, fake_ptr_proxy<T&>,\n                                                diff_type<Iterator>, std::forward_iterator_tag, default_sentinel_t> {\n    Iterator _iterator{};\n    mutable T _reducer{};\n    S _end{};\n    mutable BinaryOp _binary_op{};\n\n    using traits = std::iterator_traits<Iterator>;\n\npublic:\n    using reference = T&;\n    using value_type = T;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n    using iterator_category = std::forward_iterator_tag;\n\n    constexpr inclusive_scan_iterator(const inclusive_scan_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 inclusive_scan_iterator& operator=(const inclusive_scan_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr inclusive_scan_iterator()\n        requires(std::default_initializable<Iterator> && std::default_initializable<T> && std::default_initializable<BinaryOp> &&\n                 std::default_initializable<S>)\n    = default;\n\n#else\n\n    template<class I = Iterator,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<T>::value &&\n                                 std::is_default_constructible<BinaryOp>::value && std::is_default_constructible<S>::value>>\n    constexpr inclusive_scan_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                                 std::is_nothrow_default_constructible<T>::value &&\n                                                 std::is_nothrow_default_constructible<BinaryOp>::value &&\n                                                 std::is_nothrow_default_constructible<S>::value) {\n    }\n\n#endif\n\n    constexpr inclusive_scan_iterator(Iterator it, S end, T init, BinaryOp bin_op) :\n        _iterator{ std::move(it) },\n        _reducer{ std::move(init) },\n        _end{ std::move(end) },\n        _binary_op{ std::move(bin_op) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 inclusive_scan_iterator& operator=(default_sentinel_t) {\n        _iterator = _end;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return _reducer;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_iterator;\n        if (_iterator != _end) {\n            _reducer = _binary_op(std::move(_reducer), *_iterator);\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const inclusive_scan_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_end == other._end);\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _end;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/interleave.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INTERLEAVED_ITERATOR_HPP\n#define LZ_INTERLEAVED_ITERATOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n#include <cstdint>\n#include <limits>\n#include <numeric>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/procs/decompose.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nclass interleave_sentinel {\n    T value{};\n\n    template<class, class>\n    friend class interleave_iterator;\n\n    template<class...>\n    friend class interleave_iterable;\n\n    explicit constexpr interleave_sentinel(T v) noexcept(std::is_nothrow_move_constructible<T>::value) : value{ std::move(v) } {\n    }\n\npublic:\n#ifdef LZ_HAS_CXX_20\n    constexpr interleave_sentinel()\n        requires(std::default_initializable<T>)\n    = default;\n#else\n    template<class I = T, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr interleave_sentinel() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n#endif\n};\n\ntemplate<class Iterators, class Sentinels>\nclass interleave_iterator : public iterator<interleave_iterator<Iterators, Sentinels>, iter_tuple_common_ref_t<Iterators>,\n                                            fake_ptr_proxy<iter_tuple_common_ref_t<Iterators>>, iter_tuple_diff_type_t<Iterators>,\n                                            iter_tuple_iter_cat_t<Iterators>, interleave_sentinel<Sentinels>> {\n\n    using traits = std::iterator_traits<first_it_t<Iterators>>;\n\npublic:\n    using value_type = typename traits::value_type;\n    using difference_type = iter_tuple_diff_type_t<Iterators>;\n    using reference = iter_tuple_common_ref_t<Iterators>;\n    using pointer = fake_ptr_proxy<reference>;\n\nprivate:\n    static_assert(tuple_size<Iterators>::value <= std::numeric_limits<std::uint_least8_t>::max(),\n                  \"interleave_iterator tuple size exceeds uint_least8_t. This is not supported.\");\n\n    static constexpr auto tup_size = static_cast<std::uint_least8_t>(tuple_size<Iterators>::value);\n\n    Iterators _iterators{};\n    // Using uint_least8_t because generally the max number of function parameters is 256\n    std::uint_least8_t _index{};\n\n    using is = make_index_sequence<tup_size>;\n\n#ifdef LZ_HAS_CXX_17\n\n    template<size_t I>\n    constexpr bool eq(const interleave_sentinel<Sentinels>& other) const {\n        if constexpr (I != tup_size - 1) {\n            return std::get<I>(_iterators) == std::get<I>(other.value) ? _index == 0 : eq<I + 1>(other);\n        }\n        else {\n            return std::get<I>(_iterators) == std::get<I>(other.value);\n        }\n    }\n\n    template<size_t I>\n    constexpr bool eq(const interleave_iterator& other) const {\n        if constexpr (I != tup_size - 1) {\n            return std::get<I>(_iterators) == std::get<I>(other._iterators) ? _index == other._index : eq<I + 1>(other);\n        }\n        else {\n            return std::get<I>(_iterators) == std::get<I>(other._iterators) && _index == other._index;\n        }\n    }\n\n#else\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I != tup_size - 1, bool> eq(const interleave_sentinel<Sentinels>& other) const {\n        return std::get<I>(_iterators) == std::get<I>(other.value) ? _index == 0 : eq<I + 1>(other);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == tup_size - 1, bool> eq(const interleave_sentinel<Sentinels>& other) const {\n        return std::get<I>(_iterators) == std::get<I>(other.value);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I != tup_size - 1, bool> eq(const interleave_iterator& other) const {\n        return std::get<I>(_iterators) == std::get<I>(other._iterators) ? _index == other._index : eq<I + 1>(other);\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == tup_size - 1, bool> eq(const interleave_iterator& other) const {\n        return std::get<I>(_iterators) == std::get<I>(other._iterators) && _index == other._index;\n    }\n\n#endif\n\n    template<size_t... Is>\n    LZ_CONSTEXPR_CXX_14 void increment(index_sequence<Is...>) {\n        ++_index;\n        if (_index != tup_size) {\n            return;\n        }\n#ifdef LZ_HAS_CXX_17\n        (++std::get<Is>(_iterators), ...);\n#else\n        decompose(++std::get<Is>(_iterators)...);\n#endif\n        _index = 0;\n    }\n\n    template<size_t... Is>\n    LZ_CONSTEXPR_CXX_14 void decrement(index_sequence<Is...>) {\n        if (_index == 0) {\n#ifdef LZ_HAS_CXX_17\n            (--std::get<Is>(_iterators), ...);\n#else\n            decompose(--std::get<Is>(_iterators)...);\n#endif\n            _index = tup_size - 1;\n            return;\n        }\n        --_index;\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    template<size_t I>\n    constexpr reference dereference() const {\n        if constexpr (I != tup_size - 1) {\n            return _index == I ? *std::get<I>(_iterators) : dereference<I + 1>();\n        }\n        else {\n            return *std::get<I>(_iterators);\n        }\n    }\n\n#else\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I != tup_size - 1, reference> dereference() const {\n        return _index == I ? *std::get<I>(_iterators) : dereference<I + 1>();\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == tup_size - 1, reference> dereference() const noexcept {\n        return *std::get<I>(_iterators);\n    }\n\n#endif\n\n    template<size_t... Is>\n    LZ_CONSTEXPR_CXX_20 difference_type difference(const interleave_iterator& other, index_sequence<Is...>) const {\n\n        const difference_type distances[] = { static_cast<difference_type>(std::get<Is>(_iterators) -\n                                                                           std::get<Is>(other._iterators))... };\n        const auto sum =\n            std::accumulate(detail::begin(distances), detail::end(distances), difference_type{ 0 }, std::plus<difference_type>{});\n        return sum + (static_cast<difference_type>(_index) - static_cast<difference_type>(other._index));\n    }\n\n    template<size_t... Is>\n    LZ_CONSTEXPR_CXX_20 difference_type difference(const interleave_sentinel<Sentinels>& other, index_sequence<Is...>) const {\n\n        const difference_type distances[] = { static_cast<difference_type>(std::get<Is>(_iterators) -\n                                                                           std::get<Is>(other.value))... };\n        const auto sum = max_variadic(distances[Is]...) * static_cast<difference_type>(tup_size);\n        return sum + static_cast<difference_type>(_index);\n    }\n\n    template<size_t... Is>\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type n, index_sequence<Is...>) {\n        if (n == 0) {\n            return;\n        }\n#ifdef LZ_HAS_CXX_17\n        ((std::get<Is>(_iterators) += n), ...);\n#else\n        decompose(std::get<Is>(_iterators) += n...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void assign_sentinels(const interleave_sentinel<Sentinels>& end, index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_iterators) = std::get<I>(end.value)), ...);\n#else\n        decompose(std::get<I>(_iterators) = std::get<I>(end.value)...);\n#endif\n    }\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr interleave_iterator()\n        requires(std::default_initializable<Sentinels>)\n    = default;\n\n#else\n\n    template<class I = Iterators, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr interleave_iterator() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 interleave_iterator(Iterators iterators) : _iterators{ std::move(iterators) } {\n        static_assert(tup_size > 1, \"interleaved_iterator must have at least two iterators\");\n    }\n\n    LZ_CONSTEXPR_CXX_14 interleave_iterator& operator=(const interleave_sentinel<Sentinels>& end) {\n        assign_sentinels(end, is{});\n        _index = 0;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        return dereference<0>();\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        increment(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        decrement(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const interleave_iterator& other) const {\n        return difference(other, is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const interleave_sentinel<Sentinels>& other) const {\n        return difference(other, is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(difference_type n) {\n        if (n == 0) {\n            return;\n        }\n\n        using unsigned_diff = typename std::make_unsigned<difference_type>::type;\n        constexpr auto u_tup_size = static_cast<unsigned_diff>(tup_size);\n        const auto n_plus_index = static_cast<difference_type>(_index) + n;\n\n        if (n > 0) {\n            const auto u_n = static_cast<unsigned_diff>(n);\n            plus_is(n_plus_index / static_cast<difference_type>(tup_size), is{});\n            // clang-format off\n            _index = u_tup_size * (u_n / tup_size) == u_n\n                         ? 0 : static_cast<std::uint_least8_t>(static_cast<unsigned_diff>(n_plus_index) % u_tup_size);\n            // clang-format on\n            return;\n        }\n\n        const auto u_n = static_cast<unsigned_diff>(-n);\n        constexpr auto s_tup_size = static_cast<difference_type>(tup_size);\n        plus_is((n_plus_index - s_tup_size + 1) / s_tup_size, is{});\n        // clang-format off\n        _index = u_tup_size * (u_n / tup_size) == u_n ? \n            0 : static_cast<std::uint_least8_t>((tup_size + n_plus_index % s_tup_size) % tup_size);\n        // clang-format on\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const interleave_iterator& other) const {\n        return eq<0>(other);\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const interleave_sentinel<Sentinels>& last) const {\n        return eq<0>(last);\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_INTERLEAVED_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/intersection.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INTERSECTION_ITERATOR_HPP\n#define LZ_INTERSECTION_ITERATOR_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable1, class Iterable2, class BinaryPredicate>\nclass intersection_iterator\n    : public iterator<intersection_iterator<Iterable1, Iterable2, BinaryPredicate>, ref_t<iter_t<Iterable1>>,\n                      fake_ptr_proxy<ref_t<iter_t<Iterable1>>>, diff_type<iter_t<Iterable1>>,\n                      typename std::common_type<iter_cat_t<iter_t<Iterable1>>, iter_cat_t<iter_t<Iterable2>>,\n                                                std::bidirectional_iterator_tag>::type,\n                      default_sentinel_t> {\n\n    using it1 = iter_t<Iterable1>;\n    using it2 = iter_t<Iterable2>;\n\n    it1 _iterator1{};\n    it2 _iterator2{};\n    Iterable1 _iterable1{};\n    Iterable2 _iterable2{};\n    mutable BinaryPredicate _compare{};\n\n    using iter_traits = std::iterator_traits<it1>;\n\n    void find_next() {\n        using ref_type = typename iter_traits::reference;\n\n        _iterator1 = detail::find_if(std::move(_iterator1), _iterable1.end(), [this](ref_type value) {\n            while (_iterator2 != _iterable2.end()) {\n                if (_compare(value, *_iterator2)) {\n                    return false;\n                }\n                else if (!_compare(*_iterator2, value)) {\n                    return true;\n                }\n                ++_iterator2;\n            }\n            return false;\n        });\n    }\n\npublic:\n    using value_type = typename iter_traits::value_type;\n    using difference_type = typename iter_traits::difference_type;\n    using reference = typename iter_traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr intersection_iterator(const intersection_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 intersection_iterator& operator=(const intersection_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr intersection_iterator()\n        requires(std::default_initializable<it1> && std::default_initializable<it2> && std::default_initializable<Iterable1> &&\n                 std::default_initializable<Iterable2> && std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<\n        class I = it1,\n        class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<it2>::value &&\n                            std::is_default_constructible<Iterable1>::value && std::is_default_constructible<Iterable2>::value &&\n                            std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr intersection_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                               std::is_nothrow_default_constructible<it2>::value &&\n                                               std::is_nothrow_default_constructible<Iterable1>::value &&\n                                               std::is_nothrow_default_constructible<Iterable2>::value &&\n                                               std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I1, class I2>\n    LZ_CONSTEXPR_CXX_14 intersection_iterator(I1&& i1, I2&& i2, it1 it_1, it2 it_2, BinaryPredicate compare) :\n        _iterator1{ std::move(it_1) },\n        _iterator2{ std::move(it_2) },\n        _iterable1{ std::forward<I1>(i1) },\n        _iterable2{ std::forward<I2>(i2) },\n        _compare{ std::move(compare) } {\n        find_next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 intersection_iterator& operator=(default_sentinel_t) {\n        _iterator1 = _iterable1.end();\n        _iterator2 = _iterable2.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator1;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_iterator2;\n        ++_iterator1;\n        find_next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_iterator1 != _iterable1.begin());\n        --_iterator1;\n        --_iterator2;\n\n        while (_iterator2 != _iterable2.begin() || _iterator1 != _iterable1.begin()) {\n            if (_compare(*_iterator1, *_iterator2)) {\n                --_iterator2;\n                continue;\n            }\n            if (!_compare(*_iterator2, *_iterator1)) {\n                return;\n            }\n            --_iterator1;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const intersection_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable1.begin() == other._iterable1.begin() && _iterable1.end() == other._iterable1.end() &&\n                             _iterable2.begin() == other._iterable2.begin() && _iterable2.end() == other._iterable2.end());\n        return _iterator1 == other._iterator1 || _iterator2 == other._iterator2;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator1 == _iterable1.end() || _iterator2 == _iterable2.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_INTERSECTION_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/iterator_wrapper.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ANY_VIEW_HELPERS_HPP\n#define LZ_ANY_VIEW_HELPERS_HPP\n\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/iterators/any_iterable/iterator_base.hpp>\n#include <Lz/detail/unique_ptr.hpp>\n#include <Lz/detail/variant.hpp>\n#include <cstddef>\n#include <cstring> // max_align_t\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\nusing std::in_place_type_t;\n\n#else\n\ntemplate<class T>\nstruct in_place_type_t {\n    explicit in_place_type_t() = default;\n};\n\n#endif\n\ntemplate<class T, class Reference, class IterCat, class DiffType>\nclass iterator_wrapper : public iterator<iterator_wrapper<T, Reference, IterCat, DiffType>, Reference, fake_ptr_proxy<Reference>,\n                                         DiffType, IterCat> {\n\n    using any_iter_base = iterator_base<Reference, IterCat, DiffType>;\n\npublic:\n    static constexpr size_t SBO_SIZE = 64;\n\nprivate:\n    struct alignas(std::max_align_t) storage {\n        char buffer[SBO_SIZE];\n    };\n\n    variant<unique_ptr<any_iter_base>, storage> _storage{};\n\n    void copy_buf_other(const iterator_wrapper& other) noexcept {\n        LZ_ASSERT(other._storage.index() == 1, \"Invalid storage index\");\n        using std::get;\n        _storage.template emplace<1>();\n        std::memcpy(static_cast<void*>(&get<1>(_storage)), static_cast<const void*>(&get<1>(other._storage)), sizeof(storage));\n    }\n\npublic:\n    using value_type = T;\n    using reference = Reference;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = DiffType;\n    using iterator_category = IterCat;\n\n    constexpr iterator_wrapper() noexcept = default;\n\n    template<class Impl, class... Args>\n    iterator_wrapper(in_place_type_t<Impl>, Args&&... args) {\n        static_assert(sizeof(Impl) <= SBO_SIZE, \"Impl too large for SBO\");\n        using std::get;\n        _storage.template emplace<1>();\n        ::new (static_cast<void*>(&get<1>(_storage))) Impl(std::forward<Args>(args)...);\n    }\n\n    iterator_wrapper(const detail::unique_ptr<any_iter_base>& ptr) : _storage{ ptr->clone() } {\n    }\n\n    iterator_wrapper(detail::unique_ptr<any_iter_base>&& ptr) noexcept : _storage{ std::move(ptr) } {\n    }\n\n    iterator_wrapper(const iterator_wrapper& other) {\n        using std::get;\n        if (other._storage.index() == 0) {\n            _storage = get<0>(other._storage)->clone();\n        }\n        else {\n            copy_buf_other(other);\n        }\n    }\n\n    iterator_wrapper(iterator_wrapper&& other) noexcept {\n        using std::get;\n        if (other._storage.index() == 0) {\n            _storage = std::move(get<0>(other._storage));\n        }\n        else {\n            copy_buf_other(other);\n        }\n    }\n\n    iterator_wrapper& operator=(const iterator_wrapper& other) {\n        if (this != &other) {\n            using std::get;\n            if (other._storage.index() == 0) {\n                _storage = get<0>(other._storage)->clone();\n            }\n            else {\n                copy_buf_other(other);\n            }\n        }\n        return *this;\n    }\n\n    iterator_wrapper& operator=(iterator_wrapper&& other) noexcept {\n        if (this != &other) {\n            using std::get;\n            if (other._storage.index() == 0) {\n                _storage = std::move(get<0>(other._storage));\n            }\n            else {\n                copy_buf_other(other);\n            }\n        }\n        return *this;\n    }\n\n    reference dereference() const {\n        using std::get;\n        if (_storage.index() == 0) {\n            return get<0>(_storage)->dereference();\n        }\n        LZ_ASSERT(_storage.index() == 1, \"Invalid storage index\");\n        return reinterpret_cast<const any_iter_base&>(get<1>(_storage)).dereference();\n    }\n\n    reference dereference() {\n        using std::get;\n        if (_storage.index() == 0) {\n            return get<0>(_storage)->dereference();\n        }\n        LZ_ASSERT(_storage.index() == 1, \"Invalid storage index\");\n        return reinterpret_cast<any_iter_base&>(get<1>(_storage)).dereference();\n    }\n\n    pointer arrow() const {\n        using std::get;\n        if (_storage.index() == 0) {\n            return get<0>(_storage)->arrow();\n        }\n        LZ_ASSERT(_storage.index() == 1, \"Invalid storage index\");\n        return reinterpret_cast<const any_iter_base&>(get<1>(_storage)).arrow();\n    }\n\n    pointer arrow() {\n        using std::get;\n        if (_storage.index() == 0) {\n            return get<0>(_storage)->arrow();\n        }\n        LZ_ASSERT(_storage.index() == 1, \"Invalid storage index\");\n        return reinterpret_cast<any_iter_base&>(get<1>(_storage)).arrow();\n    }\n\n    void increment() {\n        using std::get;\n        if (_storage.index() == 0) {\n            get<0>(_storage)->increment();\n            return;\n        }\n        LZ_ASSERT(_storage.index() == 1, \"Invalid storage index\");\n        reinterpret_cast<any_iter_base&>(get<1>(_storage)).increment();\n    }\n\n    void decrement() {\n        using std::get;\n        if (_storage.index() == 0) {\n            get<0>(_storage)->decrement();\n            return;\n        }\n        LZ_ASSERT(_storage.index() == 1, \"Invalid storage index\");\n        return reinterpret_cast<any_iter_base&>(get<1>(_storage)).decrement();\n    }\n\n    bool eq(const iterator_wrapper& other) const {\n        using std::get;\n\n        switch ((_storage.index() << 1) | other._storage.index()) {\n        case 0: // (0, 0)\n            return get<0>(_storage)->eq(*get<0>(other._storage));\n        case 1: // (0, 1)\n            return get<0>(_storage)->eq(reinterpret_cast<const any_iter_base&>(get<1>(other._storage)));\n        case 2: // (1, 0)\n            return reinterpret_cast<const any_iter_base&>(get<1>(_storage)).eq(*get<0>(other._storage));\n        case 3: // (1, 1)\n            return reinterpret_cast<const any_iter_base&>(get<1>(_storage))\n                .eq(reinterpret_cast<const any_iter_base&>(get<1>(other._storage)));\n        default:\n            LZ_ASSERT(_storage.index() < 2 && other._storage.index() < 2, \"Invalid storage index\");\n            return false;\n        }\n    }\n\n    void plus_is(const DiffType n) {\n        using std::get;\n        if (_storage.index() == 0) {\n            return get<0>(_storage)->plus_is(n);\n        }\n        LZ_ASSERT(_storage.index() == 1, \"Invalid storage index\");\n        return reinterpret_cast<any_iter_base&>(get<1>(_storage)).plus_is(n);\n    }\n\n    DiffType difference(const iterator_wrapper& other) const {\n        using std::get;\n\n        switch ((_storage.index() << 1) | other._storage.index()) {\n        case 0: // (0, 0)\n            return get<0>(_storage)->difference(*get<0>(other._storage));\n        case 1: // (0, 1)\n            return get<0>(_storage)->difference(reinterpret_cast<const any_iter_base&>(get<1>(other._storage)));\n        case 2: // (1, 0)\n            return reinterpret_cast<const any_iter_base&>(get<1>(_storage)).difference(*get<0>(other._storage));\n        case 3: // (1, 1)\n            return reinterpret_cast<const any_iter_base&>(get<1>(_storage))\n                .difference(reinterpret_cast<const any_iter_base&>(get<1>(other._storage)));\n        default:\n            LZ_ASSERT(_storage.index() < 2 && other._storage.index() < 2, \"Invalid storage index\");\n            return 0;\n        }\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_ANY_VIEW_HELPERS_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/join_where.hpp",
    "content": "#pragma once\n\n#ifndef LZ_JOIN_WHERE_ITERATOR_HPP\n#define LZ_JOIN_WHERE_ITERATOR_HPP\n\n#include <Lz/algorithm/find_if.hpp>\n#include <Lz/algorithm/lower_bound.hpp>\n#include <Lz/basic_iterable.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/traits/func_ret_type.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class IterableA, class IterB, class SB, class SelectorA, class SelectorB, class ResultSelector>\nclass join_where_iterator\n    : public iterator<join_where_iterator<IterableA, IterB, SB, SelectorA, SelectorB, ResultSelector>,\n                      func_ret_type_iters<ResultSelector, iter_t<IterableA>, IterB>,\n                      fake_ptr_proxy<func_ret_type_iters<ResultSelector, iter_t<IterableA>, IterB>>, std::ptrdiff_t,\n                      bidi_strongest_cat<iter_cat_t<iter_t<IterableA>>>, default_sentinel_t> {\nprivate:\n    using iter_a = iter_t<IterableA>;\n    using traits_a = std::iterator_traits<iter_a>;\n    using traits_b = std::iterator_traits<IterB>;\n    using value_type_a = typename traits_a::value_type;\n    using value_type_b = typename traits_b::value_type;\n    using ref_type_a = typename traits_a::reference;\n\n    using selector_a_ret_val = remove_cvref_t<func_ret_type<SelectorA, ref_type_a>>;\n\n    basic_iterable<IterB, SB> _iterable_b{};\n    iter_a _iter_a{};\n    IterB _begin_b{};\n    IterableA _iterable_a{};\n\n    mutable SelectorA _selector_a{};\n    mutable SelectorB _selector_b{};\n    mutable ResultSelector _result_selector{};\n\n    LZ_CONSTEXPR_CXX_17 void find_next() {\n        _iter_a = detail::find_if(std::move(_iter_a), _iterable_a.end(), [this](ref_t<iter_a> a) {\n            auto&& to_find = _selector_a(a);\n\n            auto pos = lz::lower_bound(_iterable_b, to_find,\n                                       [this](ref_t<IterB> b, const selector_a_ret_val& val) { return _selector_b(b) < val; });\n\n            if (pos != _iterable_b.end() && !(to_find < _selector_b(*pos))) {\n                _iterable_b = lz::basic_iterable<IterB, SB>{ std::move(pos), _iterable_b.end() };\n                return true;\n            }\n            _iterable_b = lz::basic_iterable<IterB, SB>{ _begin_b, _iterable_b.end() };\n            return false;\n        });\n    }\n\npublic:\n    using reference = decltype(_result_selector(*_iter_a, *_iterable_b.begin()));\n    using value_type = remove_cvref_t<reference>;\n    using difference_type = std::ptrdiff_t;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr join_where_iterator(const join_where_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 join_where_iterator& operator=(const join_where_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr join_where_iterator()\n        requires(std::default_initializable<IterableA> && std::default_initializable<iter_a> &&\n                 std::default_initializable<IterB> && std::default_initializable<SB> && std::default_initializable<SelectorA> &&\n                 std::default_initializable<SelectorB> && std::default_initializable<ResultSelector>)\n    = default;\n\n#else\n\n    template<\n        class I = iter_a,\n        class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<IterableA>::value &&\n                            std::is_default_constructible<IterB>::value && std::is_default_constructible<SB>::value &&\n                            std::is_default_constructible<SelectorA>::value && std::is_default_constructible<SelectorB>::value &&\n                            std::is_default_constructible<ResultSelector>::value>>\n    constexpr join_where_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<IterableA>::value &&\n                                             std::is_nothrow_default_constructible<IterB>::value &&\n                                             std::is_nothrow_default_constructible<SB>::value &&\n                                             std::is_nothrow_default_constructible<SelectorA>::value &&\n                                             std::is_nothrow_default_constructible<SelectorB>::value &&\n                                             std::is_nothrow_default_constructible<ResultSelector>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_17 join_where_iterator(I&& iterable, iter_a it_a, IterB it_b, SB end_b, SelectorA a, SelectorB b,\n                                            ResultSelector result_selector) :\n        _iterable_b{ std::move(it_b), std::move(end_b) },\n        _iter_a{ std::move(it_a) },\n        _begin_b{ _iterable_b.begin() },\n        _iterable_a{ std::forward<I>(iterable) },\n        _selector_a{ std::move(a) },\n        _selector_b{ std::move(b) },\n        _result_selector{ std::move(result_selector) } {\n        find_next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 join_where_iterator& operator=(default_sentinel_t) {\n        _iter_a = _iterable_a.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return _result_selector(*_iter_a, *_iterable_b.begin());\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        _iterable_b = lz::basic_iterable<IterB, SB>{ std::next(_iterable_b.begin()), _iterable_b.end() };\n        find_next();\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const join_where_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable_a.end() == other._iterable_a.end());\n        return _iter_a == other._iter_a;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iter_a == _iterable_a.end();\n    }\n};\n\n} // namespace detail\n} // namespace lz\n#endif // LZ_JOIN_WHERE_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/loop.hpp",
    "content": "#pragma once\n\n#ifndef LZ_LOOP_ITERATOR_HPP\n#define LZ_LOOP_ITERATOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, bool /* is inf */>\nclass loop_iterator;\n\ntemplate<class Iterable>\nclass loop_iterator<Iterable, false>\n    : public iterator<loop_iterator<Iterable, false>, ref_t<iter_t<Iterable>>, fake_ptr_proxy<ref_t<iter_t<Iterable>>>,\n                      diff_type<iter_t<Iterable>>, iter_cat_t<iter_t<Iterable>>, default_sentinel_t> {\n    using it = iter_t<Iterable>;\n    using traits = std::iterator_traits<it>;\n\npublic:\n    using reference = typename traits::reference;\n    using value_type = typename traits::value_type;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n\nprivate:\n    it _iterator{};\n    Iterable _iterable{};\n    difference_type _rotations_left{};\n\npublic:\n    constexpr loop_iterator(const loop_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 loop_iterator& operator=(const loop_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr loop_iterator()\n        requires(std::default_initializable<it> && std::default_initializable<Iterable>)\n    = default;\n\n#else\n\n    template<class I = it,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value>>\n    constexpr loop_iterator() noexcept(std::is_nothrow_default_constructible<it>::value &&\n                                       std::is_nothrow_default_constructible<Iterable>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr loop_iterator(I&& iterable, it iter, difference_type amount) :\n        _iterator{ std::move(iter) },\n        _iterable{ std::forward<I>(iterable) },\n        _rotations_left{ amount } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 loop_iterator& operator=(default_sentinel_t) {\n        _rotations_left = 0;\n        _iterator = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_iterator;\n\n        if (_iterator == _iterable.end()) {\n            --_rotations_left;\n            _iterator = _iterable.begin();\n        }\n        if (_rotations_left == -1) {\n            _iterator = _iterable.end();\n            _rotations_left = 0;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_iterable.begin() != _iterable.end());\n        if (_rotations_left == 0 && _iterator == _iterable.begin()) {\n            _iterator = _iterable.end();\n            ++_rotations_left;\n        }\n        --_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        const auto iter_length = _iterable.end() - _iterable.begin();\n        const auto remainder = offset % iter_length;\n        _iterator += offset % iter_length;\n        _rotations_left -= offset / iter_length;\n\n        LZ_ASSERT_ADDABLE(_rotations_left >= -1);\n\n        if (_iterator == _iterable.begin() && _rotations_left == -1) {\n            // We are exactly at end\n            _iterator = _iterable.end();\n            _rotations_left = 0;\n        }\n        else if (_iterator == _iterable.end() && offset < 0 && remainder == 0) {\n            _iterator = _iterable.begin();\n            --_rotations_left;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const loop_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.begin() == other._iterable.begin() && _iterable.end() == other._iterable.end());\n        const auto rotations_left_diff = other._rotations_left - _rotations_left;\n        return (_iterator - other._iterator) + rotations_left_diff * (_iterable.end() - _iterable.begin());\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(default_sentinel_t) const {\n        return -((_iterable.end() - _iterator) + _rotations_left * (_iterable.end() - _iterable.begin()));\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const loop_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.begin() == other._iterable.begin() && _iterable.end() == other._iterable.end());\n        return _rotations_left == other._rotations_left && _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _rotations_left == 0 && _iterator == _iterable.end();\n    }\n};\n\ntemplate<class Iterable>\nclass loop_iterator<Iterable, true>\n    : public iterator<loop_iterator<Iterable, true>, ref_t<iter_t<Iterable>>, fake_ptr_proxy<ref_t<iter_t<Iterable>>>,\n                      diff_type<iter_t<Iterable>>, strongest_cat_t<iter_cat_iterable_t<Iterable>, std::forward_iterator_tag>,\n                      default_sentinel_t> {\n\n    using it = iter_t<Iterable>;\n    using traits = std::iterator_traits<it>;\n\n    it _iterator{};\n    Iterable _iterable{};\n\npublic:\n    using reference = typename traits::reference;\n    using value_type = typename traits::value_type;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n\n    constexpr loop_iterator(const loop_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 loop_iterator& operator=(const loop_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr loop_iterator()\n        requires(std::default_initializable<Iterable> && std::default_initializable<it>)\n    = default;\n\n#else\n\n    template<class I = it,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value>>\n    constexpr loop_iterator() noexcept(std::is_nothrow_default_constructible<it>::value &&\n                                       std::is_nothrow_default_constructible<Iterable>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr loop_iterator(I&& iterable, it iter) : _iterator{ std::move(iter) }, _iterable{ std::forward<I>(iterable) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 loop_iterator& operator=(default_sentinel_t) noexcept {\n        return *this;\n    }\n\n    constexpr reference dereference() const {\n        return *_iterator;\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        ++_iterator;\n        if (_iterator == _iterable.end()) {\n            _iterator = _iterable.begin();\n        }\n    }\n\n    constexpr bool eq(const loop_iterator&) const {\n        return *this == lz::default_sentinel;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterable.begin() == _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_LOOP_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/map.hpp",
    "content": "#pragma once\n\n#ifndef LZ_MAP_ITERATOR_HPP\n#define LZ_MAP_ITERATOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/traits/func_ret_type.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterator, class S, class UnaryOp>\nclass map_iterator\n    : public iterator<map_iterator<Iterator, S, UnaryOp>, func_ret_type_iter<UnaryOp, Iterator>,\n                      fake_ptr_proxy<func_ret_type_iter<UnaryOp, Iterator>>, diff_type<Iterator>, iter_cat_t<Iterator>, S> {\n    Iterator _iterator{};\n    mutable UnaryOp _unary_op{};\n\n    using traits = std::iterator_traits<Iterator>;\n\npublic:\n    using reference = decltype(_unary_op(*_iterator));\n    using value_type = remove_cvref_t<reference>;\n    using iterator_category = typename traits::iterator_category;\n    using difference_type = typename traits::difference_type;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr map_iterator(const map_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 map_iterator& operator=(const map_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr map_iterator()\n        requires(std::default_initializable<Iterator> && std::default_initializable<UnaryOp>)\n    = default;\n\n#else\n\n    template<class I = Iterator,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<UnaryOp>::value>>\n    constexpr map_iterator() noexcept(std::is_nothrow_default_constructible<Iterator>::value &&\n                                      std::is_nothrow_default_constructible<UnaryOp>::value) {\n    }\n\n#endif\n\n    constexpr map_iterator(Iterator it, UnaryOp unary_op) : _iterator{ std::move(it) }, _unary_op{ std::move(unary_op) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 map_iterator& operator=(const S& s) {\n        _iterator = s;\n        return *this;\n    }\n\n    constexpr reference dereference() const {\n        return _unary_op(*_iterator);\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        ++_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        --_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        _iterator += offset;\n    }\n\n    constexpr difference_type difference(const map_iterator& other) const {\n        return _iterator - other._iterator;\n    }\n\n    constexpr difference_type difference(const S& other) const {\n        return _iterator - other;\n    }\n\n    constexpr bool eq(const map_iterator& other) const {\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(const S& s) const {\n        return _iterator == s;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/pairwise.hpp",
    "content": "#pragma once\n\n#ifndef LZ_PAIRWISE_ITERATOR_HPP\n#define LZ_PAIRWISE_ITERATOR_HPP\n\n#include <Lz/basic_iterable.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/detail/procs/next_fast.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n#include <limits>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable>\nclass ra_pairwise_iterator : public iterator<ra_pairwise_iterator<Iterable>, basic_iterable<iter_t<Iterable>>,\n                                             fake_ptr_proxy<basic_iterable<iter_t<Iterable>>>, diff_iterable_t<Iterable>,\n                                             iter_cat_t<iter_t<Iterable>>, default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n    using traits = std::iterator_traits<iter>;\n\npublic:\n    using value_type = basic_iterable<iter>;\n    using reference = value_type;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n\nprivate:\n    iter _sub_begin{};\n    Iterable _iterable{};\n    difference_type _pair_size{};\n\npublic:\n    constexpr ra_pairwise_iterator(const ra_pairwise_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 ra_pairwise_iterator& operator=(const ra_pairwise_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr ra_pairwise_iterator()\n        requires(std::default_initializable<iter> && std::default_initializable<Iterable>)\n    = default;\n\n#else\n\n    template<class I = iter,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value>>\n    constexpr ra_pairwise_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                              std::is_nothrow_default_constructible<Iterable>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 ra_pairwise_iterator(I&& iterable, iter it, const difference_type pair_size) :\n        _sub_begin{ std::move(it) },\n        _iterable{ std::forward<I>(iterable) },\n        _pair_size{ pair_size } {\n        LZ_ASSERT(_pair_size != 0, \"Size must be greater than zero\");\n    }\n\n    LZ_CONSTEXPR_CXX_14 ra_pairwise_iterator& operator=(default_sentinel_t) {\n        const auto size = _iterable.end() - _iterable.begin();\n        _sub_begin = _iterable.begin() + (size - _pair_size + 1);\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return { _sub_begin, _sub_begin + _pair_size };\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_sub_begin;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_sub_begin != _iterable.begin());\n        --_sub_begin;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const ra_pairwise_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.end() == other._iterable.end() && _iterable.begin() == other._iterable.begin() &&\n                             _pair_size == other._pair_size);\n        return _sub_begin == other._sub_begin;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return (_sub_begin + detail::min_variadic2(_pair_size - 1, _iterable.end() - _iterable.begin())) ==\n               detail::end(_iterable);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        LZ_ASSERT_SUB_ADDABLE(offset < 0 ? -offset <= (_sub_begin - _iterable.begin())\n                                         : offset <= (_iterable.end() - _sub_begin) - (_pair_size - 1));\n        _sub_begin += offset;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const ra_pairwise_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.end() == other._iterable.end() && _pair_size == other._pair_size);\n        return _sub_begin - other._sub_begin;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(default_sentinel_t) const {\n        return (_sub_begin - _iterable.end()) + (_pair_size - 1);\n    }\n};\n\ntemplate<class Iterable>\nclass bidi_pairwise_iterator : public iterator<bidi_pairwise_iterator<Iterable>, basic_iterable<iter_t<Iterable>>,\n                                               fake_ptr_proxy<basic_iterable<iter_t<Iterable>>>, diff_iterable_t<Iterable>,\n                                               iter_cat_iterable_t<Iterable>, default_sentinel_t> {\n    using iter = iter_t<Iterable>;\n    using traits = std::iterator_traits<iter>;\n\npublic:\n    using value_type = basic_iterable<iter>;\n    using reference = value_type;\n    using pointer = fake_ptr_proxy<reference>;\n    using difference_type = typename traits::difference_type;\n\nprivate:\n    iter _sub_begin{};\n    iter _sub_end{};\n    Iterable _iterable{};\n    difference_type _pair_size{};\n\npublic:\n    constexpr bidi_pairwise_iterator(const bidi_pairwise_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 bidi_pairwise_iterator& operator=(const bidi_pairwise_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr bidi_pairwise_iterator()\n        requires(std::default_initializable<iter>)\n    = default;\n\n#else\n\n    template<class I = iter,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value>>\n    constexpr bidi_pairwise_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                                std::is_nothrow_default_constructible<Iterable>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 bidi_pairwise_iterator(I&& iterable, iter it, const difference_type pair_size) :\n        _sub_begin{ std::move(it) },\n        _sub_end{ next_fast_safe(iterable, it == iterable.begin() ? pair_size : std::numeric_limits<difference_type>::max()) },\n        _iterable{ std::forward<I>(iterable) },\n        _pair_size{ pair_size } {\n        LZ_ASSERT(_pair_size != 0, \"Size must be greater than zero\");\n    }\n\n    LZ_CONSTEXPR_CXX_14 bidi_pairwise_iterator& operator=(default_sentinel_t) {\n        _sub_begin = _iterable.end();\n        _sub_end = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return { _sub_begin, _sub_end };\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        if (_sub_end == _iterable.end()) {\n            _sub_begin = _iterable.end();\n            return;\n        }\n        ++_sub_begin;\n        ++_sub_end;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_sub_begin != _iterable.begin());\n        if (_sub_begin == _iterable.end()) {\n            _sub_begin = std::prev(_sub_begin, _pair_size);\n            return;\n        }\n        --_sub_begin;\n        --_sub_end;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const bidi_pairwise_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_pair_size == other._pair_size && _iterable.end() == other._iterable.end() &&\n                             _iterable.begin() == other._iterable.begin());\n        return _sub_begin == other._sub_begin;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _sub_begin == _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/random.hpp",
    "content": "#pragma once\n\n#ifndef LZ_RANDOM_ITERATOR_HPP\n#define LZ_RANDOM_ITERATOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/conditional.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Arithmetic, class Distribution, class Generator, bool UseSentinel>\nclass random_iterator\n    : public iterator<\n          random_iterator<Arithmetic, Distribution, Generator, UseSentinel>, Arithmetic, fake_ptr_proxy<Arithmetic>, ptrdiff_t,\n          std::random_access_iterator_tag,\n          conditional_t<UseSentinel, default_sentinel_t, random_iterator<Arithmetic, Distribution, Generator, UseSentinel>>> {\npublic:\n    using value_type = Arithmetic;\n    using difference_type = ptrdiff_t;\n    using pointer = fake_ptr_proxy<Arithmetic>;\n    using reference = value_type;\n    using result_type = value_type;\n\nprivate:\n    mutable Distribution _distribution{};\n    Generator* _generator{ nullptr };\n    ptrdiff_t _current{};\n\npublic:\n    constexpr random_iterator(const random_iterator&) =\n        default;\n    LZ_CONSTEXPR_CXX_14 random_iterator& operator=(const random_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr random_iterator()\n        requires(std::default_initializable<Distribution>)\n    = default;\n\n#else\n\n    template<class D = Distribution, class = enable_if_t<std::is_default_constructible<D>::value>>\n    constexpr random_iterator() noexcept(std::is_nothrow_default_constructible<D>::value) {\n    }\n\n#endif\n\n    constexpr random_iterator(const Distribution& distribution, Generator& generator, const ptrdiff_t current) :\n        _distribution{ distribution },\n        _generator{ detail::addressof(generator) },\n        _current{ current } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 random_iterator& operator=(default_sentinel_t) noexcept {\n        _current = 0;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 value_type dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(_current > 0);\n        LZ_ASSERT(_generator != nullptr, \"Generator is not initialized\");\n        return _distribution(*_generator);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() noexcept {\n        LZ_ASSERT_INCREMENTABLE(_current >= 0);\n        --_current;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() noexcept {\n        ++_current;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 result_type(min)() const noexcept {\n        return (_distribution->min)();\n    }\n\n    LZ_CONSTEXPR_CXX_14 result_type(max)() const noexcept {\n        return (_distribution->max)();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type n) noexcept {\n        _current -= n;\n        LZ_ASSERT_ADDABLE(_current >= 0);\n    }\n\n    constexpr difference_type difference(const random_iterator& other) const noexcept {\n        return other._current - _current;\n    }\n\n    constexpr difference_type difference(default_sentinel_t) const noexcept {\n        return -_current;\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const random_iterator& other) const noexcept {\n        LZ_ASSERT_COMPATIBLE(_generator == other._generator);\n        return _current == other._current;\n    }\n\n    constexpr bool eq(default_sentinel_t) const noexcept {\n        return _current == 0;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/range.hpp",
    "content": "#pragma once\n\n#ifndef LZ_RANGE_ITERATOR_HPP\n#define LZ_RANGE_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/util/default_sentinel.hpp>\n#include <limits>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Floating>\nLZ_CONSTEXPR_CXX_14 bool almost_equal(const Floating a, const Floating b, const Floating epsilon = static_cast<Floating>(1e-6)) {\n    const auto abs_a = a - b;\n    return (abs_a < 0 ? -abs_a : abs_a) < epsilon;\n}\n\ntemplate<class Arithmetic, bool /* step wise */>\nclass range_iterator;\n\ntemplate<class Arithmetic>\nclass range_iterator<Arithmetic, true> : public iterator<range_iterator<Arithmetic, true>, Arithmetic, fake_ptr_proxy<Arithmetic>,\n                                                         std::ptrdiff_t, std::random_access_iterator_tag> {\n    Arithmetic _index{};\n    Arithmetic _step{};\n\npublic:\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = Arithmetic;\n    using difference_type = std::ptrdiff_t;\n    using pointer = fake_ptr_proxy<Arithmetic>;\n    using reference = Arithmetic;\n\n    constexpr range_iterator() noexcept = default;\n\n    constexpr range_iterator(const Arithmetic it, const Arithmetic step) noexcept : _index{ it }, _step{ step } {\n    }\n\n    // Operator= default_sentinel_t not necessary, as it never returns as default_sentinel_t\n    LZ_CONSTEXPR_CXX_14 range_iterator& operator=(default_sentinel_t) = delete;\n\n    constexpr value_type dereference() const noexcept {\n        return _index;\n    }\n\n    constexpr pointer arrow() const noexcept {\n        return fake_ptr_proxy<value_type>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() noexcept {\n        _index += _step;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() noexcept {\n        _index -= _step;\n    }\n\n    difference_type difference(default_sentinel_t) const = delete; // Cannot calculate difference with default_sentinel_t\n\n#ifdef LZ_HAS_CXX_17\n\n    constexpr difference_type difference(const range_iterator& other) const noexcept {\n        if constexpr (std::is_floating_point_v<Arithmetic>) {\n            LZ_ASSERT_COMPATIBLE(almost_equal(_step, other._step, std::numeric_limits<Arithmetic>::epsilon()));\n\n            const auto current_size = (_index - other._index) / _step;\n            const auto int_part = static_cast<difference_type>(current_size);\n            const auto arithmetic_int_part = static_cast<Arithmetic>(int_part);\n            return !almost_equal(current_size, arithmetic_int_part) ? int_part + 1 : int_part;\n        }\n        else {\n            return static_cast<difference_type>(_index - other._index) / static_cast<difference_type>(_step);\n        }\n    }\n\n    constexpr bool eq(const range_iterator& other) const noexcept {\n        if constexpr (std::is_floating_point_v<Arithmetic>) {\n            LZ_ASSERT_COMPATIBLE(almost_equal(_step, other._step, std::numeric_limits<Arithmetic>::epsilon()));\n            return almost_equal(_index, other._index);\n        }\n        else {\n            return _index == other._index;\n        }\n    }\n\n#else\n\n    template<class A = Arithmetic>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_floating_point<A>::value, difference_type>\n    difference(const range_iterator& other) const noexcept {\n        LZ_ASSERT_COMPATIBLE(almost_equal(_step, other._step, std::numeric_limits<Arithmetic>::epsilon()));\n\n        const auto current_size = (_index - other._index) / _step;\n        const auto int_part = static_cast<difference_type>(current_size);\n        return (current_size > static_cast<A>(int_part)) ? int_part + 1 : int_part;\n    }\n\n    template<class A = Arithmetic>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!std::is_floating_point<A>::value, difference_type>\n    difference(const range_iterator& other) const noexcept {\n        LZ_ASSERT_COMPATIBLE(_step == other._step);\n        return static_cast<difference_type>(_index - other._index) / static_cast<difference_type>(_step);\n    }\n\n    template<class A = Arithmetic>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_floating_point<A>::value, bool> eq(const range_iterator& other) const noexcept {\n        LZ_ASSERT_COMPATIBLE(almost_equal(_step, other._step, std::numeric_limits<A>::epsilon()));\n        return almost_equal(_index, other._index);\n    }\n\n    template<class A = Arithmetic>\n    constexpr enable_if_t<!std::is_floating_point<A>::value, bool> eq(const range_iterator& other) const noexcept {\n        return _index == other._index;\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type value) noexcept {\n        _index += static_cast<Arithmetic>(value) * _step;\n    }\n\n    bool eq(default_sentinel_t) const = delete; // Cannot compare with default_sentinel_t\n};\n\ntemplate<class Arithmetic>\nclass range_iterator<Arithmetic, false>\n    : public iterator<range_iterator<Arithmetic, false>, Arithmetic, fake_ptr_proxy<Arithmetic>, std::ptrdiff_t,\n                      std::random_access_iterator_tag> {\n    Arithmetic _index{};\n\npublic:\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = Arithmetic;\n    using difference_type = std::ptrdiff_t;\n    using pointer = fake_ptr_proxy<Arithmetic>;\n    using reference = Arithmetic;\n\n    constexpr range_iterator() noexcept = default;\n\n    explicit constexpr range_iterator(const Arithmetic it) noexcept : _index{ it } {\n    }\n\n    // Operator= default_sentinel_t not necessary, as it never returns as default_sentinel_t\n    LZ_CONSTEXPR_CXX_14 range_iterator& operator=(default_sentinel_t) = delete;\n\n    constexpr value_type dereference() const noexcept {\n        return _index;\n    }\n\n    constexpr pointer arrow() const noexcept {\n        return fake_ptr_proxy<value_type>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() noexcept {\n        ++_index;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() noexcept {\n        --_index;\n    }\n\n    difference_type difference(default_sentinel_t) const = delete; // Cannot calculate difference with default_sentinel_t\n\n#ifdef LZ_HAS_CXX_17\n\n    constexpr difference_type difference(const range_iterator& other) const noexcept {\n        const auto current_size = _index - other._index;\n        if constexpr (std::is_floating_point_v<Arithmetic>) {\n            return static_cast<difference_type>(current_size + (current_size < 0 ? -0.5 : 0.5));\n        }\n        else {\n            return static_cast<difference_type>(current_size);\n        }\n    }\n\n    constexpr bool eq(const range_iterator& other) const noexcept {\n        if constexpr (std::is_floating_point_v<Arithmetic>) {\n            return almost_equal(_index, other._index);\n        }\n        else {\n            return _index == other._index;\n        }\n    }\n\n#else\n\n    template<class A = Arithmetic>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_floating_point<A>::value, difference_type>\n    difference(const range_iterator& other) const noexcept {\n        const auto diff = _index - other._index;\n        return static_cast<difference_type>(diff + (diff < 0 ? -0.5 : 0.5));\n    }\n\n    template<class A = Arithmetic>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!std::is_floating_point<A>::value, difference_type>\n    difference(const range_iterator& other) const noexcept {\n        return static_cast<difference_type>(_index - other._index);\n    }\n\n    template<class A = Arithmetic>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_floating_point<A>::value, bool> eq(const range_iterator& other) const noexcept {\n        return almost_equal(_index, other._index);\n    }\n\n    template<class A = Arithmetic>\n    constexpr enable_if_t<!std::is_floating_point<A>::value, bool> eq(const range_iterator& other) const noexcept {\n        return _index == other._index;\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type value) noexcept {\n        _index += static_cast<Arithmetic>(value);\n    }\n\n    bool eq(default_sentinel_t) const = delete; // Cannot compare with default_sentinel_t\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/regex_split.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REGEX_SPLIT_ITERATOR_HPP\n#define LZ_REGEX_SPLIT_ITERATOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/string_view.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nclass regex_split_sentinel {\n    T value{};\n\n    template<class, class>\n    friend class regex_split_iterator;\n\n    template<class, class>\n    friend class regex_split_iterable;\n\n    explicit constexpr regex_split_sentinel(T v) noexcept(std::is_nothrow_move_constructible<T>::value) : value{ std::move(v) } {\n    }\n\npublic:\n#ifdef LZ_HAS_CXX_20\n    constexpr regex_split_sentinel()\n        requires(std::default_initializable<T>)\n    = default;\n#else\n    template<class I = T, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr regex_split_sentinel() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n#endif\n};\n\ntemplate<class RegexTokenIter>\nusing regex_split_value_type = basic_string_view<typename RegexTokenIter::regex_type::value_type>;\n\ntemplate<class RegexTokenIter>\nusing regex_split_val = typename val_t<RegexTokenIter>::string_type::value_type;\n\ntemplate<class RegexTokenIter, class RegexTokenSentinel>\nclass regex_split_iterator\n    : public iterator<regex_split_iterator<RegexTokenIter, RegexTokenSentinel>,\n                      basic_string_view<regex_split_val<RegexTokenIter>>,\n                      fake_ptr_proxy<basic_string_view<regex_split_val<RegexTokenIter>>>, diff_type<RegexTokenIter>,\n                      std::forward_iterator_tag, regex_split_sentinel<RegexTokenSentinel>> {\npublic:\n    using value_type = basic_string_view<regex_split_val<RegexTokenIter>>;\n    using difference_type = typename RegexTokenIter::difference_type;\n    using pointer = fake_ptr_proxy<value_type>;\n    using reference = value_type;\n\nprivate:\n    RegexTokenIter _current{};\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr regex_split_iterator()\n        requires(std::default_initializable<RegexTokenIter>)\n    = default;\n\n#else\n\n    template<class R = RegexTokenIter, class = enable_if_t<std::is_default_constructible<R>::value>>\n    constexpr regex_split_iterator() noexcept(std::is_nothrow_default_constructible<R>::value) {\n    }\n\n#endif\n\n    regex_split_iterator(RegexTokenIter first, RegexTokenSentinel last) : _current{ std::move(first) } {\n        while (_current != last && _current->length() == 0) {\n            ++_current;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 regex_split_iterator& operator=(regex_split_sentinel<RegexTokenSentinel> end) {\n        _current = std::move(end.value);\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        ++_current;\n    }\n\n    constexpr value_type dereference() const {\n        return value_type(&*_current->first, static_cast<size_t>(_current->length()));\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    constexpr bool eq(const regex_split_iterator& other) const {\n        return _current == other._current;\n    }\n\n    constexpr bool eq(regex_split_sentinel<RegexTokenSentinel> end) const {\n        return _current == end.value;\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/repeat.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REPEAT_ITERATOR_HPP\n#define LZ_REPEAT_ITERATOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class, bool /* is inf */>\nclass repeat_iterator;\n\ntemplate<class T>\nclass repeat_iterator<T, false> : public iterator<repeat_iterator<T, false>, T, fake_ptr_proxy<T>, ptrdiff_t,\n                                                  std::random_access_iterator_tag, default_sentinel_t> {\n    T _to_repeat{};\n    ptrdiff_t _amount{};\n\npublic:\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = remove_cvref_t<T>;\n    using difference_type = ptrdiff_t;\n    using pointer = fake_ptr_proxy<T>;\n    using reference = T;\n\n    constexpr repeat_iterator(const repeat_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 repeat_iterator& operator=(const repeat_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr repeat_iterator()\n        requires(std::default_initializable<T>)\n    = default;\n\n#else\n\n    template<class U = T, class = enable_if_t<std::is_default_constructible<U>::value>>\n    constexpr repeat_iterator() noexcept(std::is_nothrow_default_constructible<T>::value) {\n    }\n\n#endif\n\n    constexpr repeat_iterator(T to_repeat, const ptrdiff_t start) : _to_repeat{ std::forward<T>(to_repeat) }, _amount{ start } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 repeat_iterator& operator=(default_sentinel_t) {\n        _amount = 0;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const noexcept {\n        LZ_ASSERT_DEREFERENCABLE(_amount != 0);\n        return _to_repeat;\n    }\n\n    LZ_CONSTEXPR_CXX_17 pointer arrow() const noexcept {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() noexcept {\n        LZ_ASSERT_INCREMENTABLE(_amount != 0);\n        --_amount;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() noexcept {\n        ++_amount;\n    }\n\n    constexpr bool eq(const repeat_iterator& other) const noexcept {\n        return _amount == other._amount;\n    }\n\n    constexpr bool eq(default_sentinel_t) const noexcept {\n        return _amount == 0;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type value) noexcept {\n        LZ_ASSERT_ADDABLE(value < 0 ? true : _amount >= value);\n        _amount -= value;\n    }\n\n    constexpr difference_type difference(const repeat_iterator& other) const noexcept {\n        return other._amount - _amount;\n    }\n\n    constexpr difference_type difference(default_sentinel_t) const noexcept {\n        return -_amount;\n    }\n};\n\ntemplate<class T>\nclass repeat_iterator<T, true> : public iterator<repeat_iterator<T, true>, T, fake_ptr_proxy<T>, std::ptrdiff_t,\n                                                 std::forward_iterator_tag, default_sentinel_t> {\n    T _to_repeat{};\n\npublic:\n    using iterator_category = std::forward_iterator_tag;\n    using value_type = remove_cvref_t<T>;\n    using difference_type = std::ptrdiff_t;\n    using pointer = fake_ptr_proxy<T>;\n    using reference = T;\n\n    constexpr repeat_iterator(const repeat_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 repeat_iterator& operator=(const repeat_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr repeat_iterator()\n        requires(std::default_initializable<T>)\n    = default;\n\n#else\n\n    template<class U = T, class = enable_if_t<std::is_default_constructible<U>::value>>\n    constexpr repeat_iterator() {\n    }\n\n#endif\n\n    explicit constexpr repeat_iterator(T to_repeat) : _to_repeat{ std::forward<T>(to_repeat) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 repeat_iterator& operator=(default_sentinel_t) {\n        return *this;\n    }\n\n    constexpr reference dereference() const noexcept {\n        return _to_repeat;\n    }\n\n    LZ_CONSTEXPR_CXX_17 pointer arrow() const noexcept {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() const noexcept {\n    }\n\n    constexpr bool eq(const repeat_iterator&) const noexcept {\n        return false;\n    }\n\n    constexpr bool eq(default_sentinel_t) const noexcept {\n        return false;\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/rotate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ROTATE_ITERATOR_HPP\n#define LZ_ROTATE_ITERATOR_HPP\n\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/procs/eager_size.hpp>\n#include <limits>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nclass rotate_sentinel {\n    T value{};\n\n    template<class>\n    friend class rotate_iterator;\n\n    template<class>\n    friend class rotate_iterable;\n\n    explicit constexpr rotate_sentinel(T v) noexcept(std::is_nothrow_move_constructible<T>::value) : value{ std::move(v) } {\n    }\n\npublic:\n#ifdef LZ_HAS_CXX_20\n    constexpr rotate_sentinel()\n        requires(std::default_initializable<T>)\n    = default;\n#else\n    template<class I = T, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr rotate_sentinel() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n#endif\n};\n\ntemplate<class Iterable>\nclass rotate_iterator\n    : public iterator<rotate_iterator<Iterable>, ref_t<iter_t<Iterable>>, ptr_t<iter_t<Iterable>>, diff_type<iter_t<Iterable>>,\n                      iter_cat_t<iter_t<Iterable>>, rotate_sentinel<iter_t<Iterable>>> {\n\n    using iter = iter_t<Iterable>;\n    using traits = std::iterator_traits<iter>;\n\npublic:\n    using reference = typename traits::reference;\n    using value_type = typename traits::value_type;\n    using pointer = typename traits::pointer;\n    using difference_type = typename traits::difference_type;\n\nprivate:\n    iter _iterator{};\n    Iterable _iterable{};\n    difference_type _offset{};\n\npublic:\n    constexpr rotate_iterator(const rotate_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 rotate_iterator& operator=(const rotate_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr rotate_iterator()\n        requires(std::default_initializable<iter> && std::default_initializable<Iterable>)\n    = default;\n\n#else\n\n    template<class I = iter,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value>>\n    constexpr rotate_iterator() noexcept(std::is_nothrow_default_constructible<iter>::value &&\n                                         std::is_nothrow_default_constructible<Iterable>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 rotate_iterator(I&& iterable, iter start, const difference_type offset) :\n        _iterator{ std::move(start) },\n        _iterable{ std::forward<I>(iterable) },\n        _offset{ offset } {\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    constexpr rotate_iterator& operator=(const rotate_sentinel<iter_t<Iterable>>& end) {\n        _iterator = end.value;\n        if constexpr (is_bidi_v<iter>) {\n            _offset = lz::eager_ssize(_iterable);\n        }\n        else {\n            _offset = std::numeric_limits<difference_type>::max();\n        }\n        return *this;\n    }\n#else\n\n    template<class I = iter>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<is_bidi<I>::value, rotate_iterator&> operator=(const rotate_sentinel<iter_t<Iterable>>& end) {\n        _iterator = end.value;\n        _offset = lz::eager_ssize(_iterable);\n        return *this;\n    }\n\n    template<class I = iter>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!is_bidi<I>::value, rotate_iterator&>\n    operator=(const rotate_sentinel<iter_t<Iterable>>& end) {\n        _iterator = end.value;\n        _offset = std::numeric_limits<difference_type>::max();\n        return *this;\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(_iterator != _iterable.end());\n        return *_iterator;\n    }\n\n    constexpr pointer arrow() const {\n        return _iterator.operator->();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(_iterator != _iterable.end());\n        ++_iterator;\n        ++_offset;\n        if (_iterator == _iterable.end()) {\n            _iterator = _iterable.begin();\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        if (_iterable.begin() == _iterable.end()) {\n            return;\n        }\n        if (_iterator == _iterable.begin()) {\n            _iterator = _iterable.end();\n        }\n        LZ_ASSERT_DECREMENTABLE(_offset != 0 && _iterator != _iterable.begin());\n        --_iterator;\n        --_offset;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(difference_type n) {\n        LZ_ASSERT_SUB_ADDABLE((n < 0 ? -n : n) <= (_iterable.end() - _iterable.begin()));\n        if (n == 0) {\n            return;\n        }\n        _offset += n;\n        const auto size = _iterable.end() - _iterable.begin();\n        auto pos = (_iterator - _iterable.begin() + n) % size;\n        if (pos < 0) {\n            pos += size;\n        }\n        _iterator = _iterable.begin() + pos;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const rotate_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.begin() == other._iterable.begin() && _iterable.end() == other._iterable.end());\n        return _offset - other._offset;\n    }\n\n    constexpr difference_type difference(const rotate_sentinel<iter_t<Iterable>>&) const {\n        return (_iterable.begin() - _iterable.end()) + _offset;\n    }\n\n    template<class I = iter>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!is_bidi<I>::value, bool> eq(const rotate_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.begin() == other._iterable.begin() && _iterable.end() == other._iterable.end());\n        return _offset == other._offset || (_iterator == other._iterator && _offset != 0 && other._offset != 0);\n    }\n\n    template<class I = iter>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<is_bidi<I>::value, bool> eq(const rotate_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.begin() == other._iterable.begin() && _iterable.end() == other._iterable.end());\n        return _offset == other._offset;\n    }\n\n    constexpr bool eq(const rotate_sentinel<iter_t<Iterable>>& other) const {\n        return (_offset != 0 || _iterator == _iterable.end()) && _iterator == other.value;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_ROTATE_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/split.hpp",
    "content": "#pragma once\n\n#ifndef LZ_SPLIT_ITERATOR_HPP\n#define LZ_SPLIT_ITERATOR_HPP\n\n#include <Lz/algorithm/find.hpp>\n#include <Lz/algorithm/search.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/operators.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class ValueType, class Iterator, class S, class Iterator2, class S2>\nclass split_iterator\n    : public iterator<split_iterator<ValueType, Iterator, S, Iterator2, S2>, ValueType, fake_ptr_proxy<ValueType>, std::ptrdiff_t,\n                      strongest_cat_t<iter_cat_t<Iterator>, std::forward_iterator_tag>, default_sentinel_t> {\n    std::pair<Iterator, Iterator> _sub_range_end{};\n    Iterator _sub_range_begin{};\n    Iterator2 _to_search{};\n    S _end{};\n    S2 _to_search_end{};\n    bool _ends_with_trailing{ true };\n\npublic:\n    using iterator_category = strongest_cat_t<iter_cat_t<Iterator>, std::forward_iterator_tag>;\n    using value_type = ValueType;\n    using reference = value_type;\n    using difference_type = diff_type<Iterator>;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr split_iterator(const split_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 split_iterator& operator=(const split_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr split_iterator()\n        requires(std::default_initializable<Iterator> && std::default_initializable<S> && std::default_initializable<Iterator2> &&\n                 std::default_initializable<S2>)\n    = default;\n\n#else\n\n    template<class I = Iterator,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<S>::value &&\n                                 std::is_default_constructible<Iterator2>::value && std::is_default_constructible<S2>::value>>\n    constexpr split_iterator() noexcept(std::is_nothrow_default_constructible<Iterator>::value &&\n                                        std::is_nothrow_default_constructible<S>::value &&\n                                        std::is_nothrow_default_constructible<Iterator2>::value &&\n                                        std::is_nothrow_default_constructible<S2>::value) {\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 split_iterator(Iterator begin, S end, Iterator2 begin2, S2 end2) :\n        _sub_range_end{ begin, begin },\n        _sub_range_begin{ std::move(begin) },\n        _to_search{ std::move(begin2) },\n        _end{ std::move(end) },\n        _to_search_end{ std::move(end2) } {\n        if (_sub_range_begin != _end) {\n            _sub_range_end =\n                detail::search(_sub_range_end.second, _end, _to_search, _to_search_end, LZ_BIN_OP(equal_to, val_t<Iterator>){});\n        }\n        else {\n            _ends_with_trailing = false;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 split_iterator& operator=(default_sentinel_t) {\n        _sub_range_begin = _end;\n        _sub_range_end.first = _end;\n        _ends_with_trailing = false;\n        return *this;\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    constexpr reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(_sub_range_begin != _end);\n        if constexpr (std::is_constructible_v<ValueType, Iterator, Iterator>) {\n            return { _sub_range_begin, _sub_range_end.first };\n        }\n        else {\n            return { detail::addressof(*_sub_range_begin),\n                     static_cast<size_t>(std::distance(_sub_range_begin, _sub_range_end.first)) };\n        }\n    }\n\n#else\n\n    template<class V = ValueType>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_constructible<V, Iterator, Iterator>::value, reference> dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(_sub_range_begin != _end);\n        return { _sub_range_begin, _sub_range_end.first };\n    }\n\n    // Overload for std::string, [std/lz]::string_view\n    template<class V = ValueType>\n    LZ_CONSTEXPR_CXX_17 enable_if_t<!std::is_constructible<V, Iterator, Iterator>::value, reference> dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(_sub_range_begin != _end);\n        return { detail::addressof(*_sub_range_begin),\n                 static_cast<size_t>(std::distance(_sub_range_begin, _sub_range_end.first)) };\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n\n        if (_ends_with_trailing && _sub_range_end.second == _end) {\n            _sub_range_begin = _sub_range_end.first;\n            _ends_with_trailing = false;\n            return;\n        }\n\n        if (!_ends_with_trailing && _sub_range_end.second == _end) {\n            _sub_range_begin = _sub_range_end.first = _sub_range_end.second;\n            return;\n        }\n\n        _sub_range_end.first = _sub_range_end.second;\n        if (_sub_range_end.first != _end) {\n            _sub_range_begin = _sub_range_end.first;\n            _sub_range_end =\n                detail::search(_sub_range_end.second, _end, _to_search, _to_search_end, LZ_BIN_OP(equal_to, val_t<Iterator>){});\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const split_iterator& rhs) const {\n        LZ_ASSERT_COMPATIBLE(_end == rhs._end && _to_search_end == rhs._to_search_end);\n        return _sub_range_begin == rhs._sub_range_begin && _sub_range_end.first == rhs._sub_range_end.first &&\n               _ends_with_trailing == rhs._ends_with_trailing;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _sub_range_begin == _end && !_ends_with_trailing;\n    }\n};\n\ntemplate<class ValueType, class Iterator, class S, class T>\nclass split_single_iterator\n    : public iterator<split_single_iterator<ValueType, Iterator, S, T>, ValueType, fake_ptr_proxy<ValueType>, std::ptrdiff_t,\n                      strongest_cat_t<iter_cat_t<Iterator>, std::forward_iterator_tag>, default_sentinel_t> {\n    Iterator _sub_range_begin{};\n    Iterator _sub_range_end{};\n    S _end{};\n    T _delimiter{};\n    bool _ends_with_trailing{ true };\n\npublic:\n    constexpr split_single_iterator(const split_single_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 split_single_iterator& operator=(const split_single_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr split_single_iterator()\n        requires(std::default_initializable<Iterator> && std::default_initializable<S> && std::default_initializable<T>)\n    = default;\n\n#else\n\n    template<class I = Iterator,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<S>::value &&\n                                 std::is_default_constructible<T>::value>>\n    constexpr split_single_iterator() noexcept(std::is_nothrow_default_constructible<Iterator>::value &&\n                                               std::is_nothrow_default_constructible<S>::value &&\n                                               std::is_nothrow_default_constructible<T>::value) {\n    }\n\n#endif\n\n    using iterator_category = std::forward_iterator_tag;\n    using value_type = ValueType;\n    using reference = value_type;\n    using difference_type = diff_type<Iterator>;\n    using pointer = fake_ptr_proxy<reference>;\n\n    LZ_CONSTEXPR_CXX_14 split_single_iterator(Iterator begin, S end, T delimiter) :\n        _sub_range_begin{ std::move(begin) },\n        _sub_range_end{},\n        _end{ std::move(end) },\n        _delimiter{ std::move(delimiter) } {\n        if (_sub_range_begin != _end) {\n            _sub_range_end =\n                detail::find_if(_sub_range_begin, _end, [this](detail::ref_t<Iterator> val) { return _delimiter == val; });\n        }\n        else {\n            _ends_with_trailing = false;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 split_single_iterator& operator=(default_sentinel_t) {\n        _sub_range_begin = _end;\n        _sub_range_end = _end;\n        _ends_with_trailing = false;\n        return *this;\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    constexpr reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        if constexpr (std::is_constructible_v<ValueType, Iterator, Iterator>) {\n            return { _sub_range_begin, _sub_range_end };\n        }\n        else {\n            return { detail::addressof(*_sub_range_begin), static_cast<size_t>(std::distance(_sub_range_begin, _sub_range_end)) };\n        }\n    }\n\n#else\n\n    template<class V = ValueType>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<std::is_constructible<V, Iterator, Iterator>::value, reference> dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return { _sub_range_begin, _sub_range_end };\n    }\n\n    // Overload for std::string, [std/lz]::string_view\n    template<class V = ValueType>\n    LZ_CONSTEXPR_CXX_17 enable_if_t<!std::is_constructible<V, Iterator, Iterator>::value, reference> dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return { detail::addressof(*_sub_range_begin), static_cast<size_t>(std::distance(_sub_range_begin, _sub_range_end)) };\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        _sub_range_begin = _sub_range_end;\n        if (_sub_range_begin == _end) {\n            _ends_with_trailing = false;\n            _sub_range_begin = _sub_range_end;\n            return;\n        }\n\n        ++_sub_range_begin;\n\n        if (_sub_range_begin == _end && _ends_with_trailing) {\n            _sub_range_begin = _sub_range_end;\n            _ends_with_trailing = false;\n            return;\n        }\n\n        using lz::find;\n        using std::find;\n        _sub_range_end =\n            detail::find_if(_sub_range_begin, _end, [this](ref_t<Iterator> value) { return value == _delimiter; });\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const split_single_iterator& rhs) const {\n        LZ_ASSERT_COMPATIBLE(_end == rhs._end);\n        return _sub_range_begin == rhs._sub_range_begin && _sub_range_end == rhs._sub_range_end &&\n               _ends_with_trailing == rhs._ends_with_trailing;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _sub_range_begin == _end && !_ends_with_trailing;\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/take.hpp",
    "content": "#pragma once\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterator>\nclass n_take_iterator : public iterator<n_take_iterator<Iterator>, ref_t<Iterator>, fake_ptr_proxy<ref_t<Iterator>>,\n                                        diff_type<Iterator>, iter_cat_t<Iterator>, default_sentinel_t> {\n\n    using traits = std::iterator_traits<Iterator>;\n\npublic:\n    using value_type = typename traits::value_type;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\nprivate:\n    Iterator _iterator{};\n    difference_type _n{};\n\npublic:\n    constexpr n_take_iterator(const n_take_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 n_take_iterator& operator=(const n_take_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr n_take_iterator()\n        requires(std::default_initializable<Iterator>)\n    = default;\n\n#else\n\n    template<class I = Iterator, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr n_take_iterator() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    constexpr n_take_iterator(Iterator it, const difference_type n) : _iterator{ std::move(it) }, _n{ n } {\n    }\n\n#ifdef LZ_HAS_CXX_17\n\n    constexpr n_take_iterator& operator=(default_sentinel_t) {\n        if constexpr (is_bidi_v<Iterator>) {\n            _iterator = std::next(_iterator, _n);\n        }\n        _n = 0;\n        return *this;\n    }\n\n#else\n\n    template<class I = Iterator>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<is_bidi<I>::value, n_take_iterator&> operator=(default_sentinel_t) {\n        _iterator = std::next(_iterator, _n);\n        _n = 0;\n        return *this;\n    }\n\n    template<class I = Iterator>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!is_bidi<I>::value, n_take_iterator&> operator=(default_sentinel_t) {\n        _n = 0;\n        return *this;\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        ++_iterator;\n        --_n;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        --_iterator;\n        ++_n;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        _iterator += offset;\n        _n -= offset;\n    }\n\n    constexpr difference_type difference(const n_take_iterator& other) const {\n        return other._n - _n;\n    }\n\n    constexpr difference_type difference(default_sentinel_t) const {\n        return -_n;\n    }\n\n    constexpr bool eq(const n_take_iterator& other) const {\n        return _n == other._n;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _n == 0;\n    }\n};\n} // namespace detail\n} // namespace lz\n"
  },
  {
    "path": "include/Lz/detail/iterators/take_every.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_EVERY_ITERATOR_HPP\n#define LZ_TAKE_EVERY_ITERATOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, class = void>\nclass take_every_iterator;\n\ntemplate<class Iterable>\nclass take_every_iterator<Iterable, enable_if_t<!is_bidi<iter_t<Iterable>>::value>>\n    : public iterator<take_every_iterator<Iterable>, ref_t<iter_t<Iterable>>, fake_ptr_proxy<ref_t<iter_t<Iterable>>>,\n                      diff_type<iter_t<Iterable>>, iter_cat_t<iter_t<Iterable>>, default_sentinel_t> {\n    using it = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n    using traits = std::iterator_traits<it>;\n\npublic:\n    using value_type = typename traits::value_type;\n    using iterator_category = iter_cat_t<it>;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\n    it _iterator{};\n    sent _end{};\n    difference_type _offset{};\n\npublic:\n    constexpr take_every_iterator(const take_every_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 take_every_iterator& operator=(const take_every_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr take_every_iterator()\n        requires(std::default_initializable<it> && std::default_initializable<sent>)\n    = default;\n\n#else\n\n    template<class I = it,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<sent>::value>>\n    constexpr take_every_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<sent>::value) {\n    }\n\n#endif\n\n    constexpr take_every_iterator(it iter, sent end, const difference_type offset) :\n        _iterator{ std::move(iter) },\n        _end{ std::move(end) },\n        _offset{ offset } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 take_every_iterator& operator=(default_sentinel_t) {\n        _iterator = _end;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        for (difference_type count = 0; _iterator != _end && count < _offset; ++_iterator, ++count) {\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const take_every_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_end == other._end && _offset == other._offset);\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _end;\n    }\n};\n\ntemplate<class Iterable>\nclass take_every_iterator<Iterable, enable_if_t<is_bidi<iter_t<Iterable>>::value && !is_ra<iter_t<Iterable>>::value>>\n    : public iterator<take_every_iterator<Iterable>, ref_t<iter_t<Iterable>>, fake_ptr_proxy<ref_t<iter_t<Iterable>>>,\n                      diff_type<iter_t<Iterable>>, std::bidirectional_iterator_tag, default_sentinel_t> {\n    using it = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n    using traits = std::iterator_traits<it>;\n\npublic:\n    using value_type = typename traits::value_type;\n    using iterator_category = typename traits::iterator_category;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\n    it _iterator{};\n    sent _end{};\n    difference_type _offset{};\n    difference_type _distance{};\n\npublic:\n    constexpr take_every_iterator(const take_every_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 take_every_iterator& operator=(const take_every_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr take_every_iterator()\n        requires(std::default_initializable<it> && std::default_initializable<sent>)\n    = default;\n\n#else\n\n    template<class I = it,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<sent>::value>>\n    constexpr take_every_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<sent>::value) {\n    }\n\n#endif\n\n    constexpr take_every_iterator(it iter, sent end, const difference_type offset, const difference_type distance) :\n        _iterator{ std::move(iter) },\n        _end{ std::move(end) },\n        _offset{ offset },\n        _distance{ distance } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 take_every_iterator& operator=(default_sentinel_t) {\n        _iterator = _end;\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        for (difference_type count = 0; _iterator != _end && count < _offset; ++_iterator, ++count, ++_distance) {\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_distance != 0);\n        const auto start_pos = _distance % _offset;\n        const auto adjusted_start_pos = start_pos == 0 ? _offset : start_pos;\n\n        for (difference_type count = 0; count < adjusted_start_pos; count++, --_iterator, --_distance) {\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const take_every_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_end == other._end && _offset == other._offset);\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _end;\n    }\n};\n\ntemplate<class Iterable>\nclass take_every_iterator<Iterable, enable_if_t<is_ra<iter_t<Iterable>>::value>>\n    : public iterator<take_every_iterator<Iterable>, ref_t<iter_t<Iterable>>, fake_ptr_proxy<ref_t<iter_t<Iterable>>>,\n                      diff_type<iter_t<Iterable>>, iter_cat_t<iter_t<Iterable>>, default_sentinel_t> {\n\n    using it = iter_t<Iterable>;\n    using sent = sentinel_t<Iterable>;\n    using traits = std::iterator_traits<it>;\n\npublic:\n    using value_type = typename traits::value_type;\n    using iterator_category = typename traits::iterator_category;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\nprivate:\n    Iterable _iterable{};\n    it _iterator{};\n    difference_type _offset{};\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference_impl(const sent& iter) const {\n        const auto remaining = _iterator - iter;\n        const auto quot = remaining / _offset;\n        const auto rem = remaining % _offset;\n        return rem == 0 ? quot : quot + (remaining < 0 ? -1 : 1);\n    }\n\npublic:\n    constexpr take_every_iterator(const take_every_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 take_every_iterator& operator=(const take_every_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr take_every_iterator()\n        requires(std::default_initializable<it> && std::default_initializable<sent>)\n    = default;\n\n#else\n\n    template<class I = it,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<sent>::value>>\n    constexpr take_every_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<sent>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr take_every_iterator(I&& iterable, it iter, const difference_type offset) :\n        _iterable{ std::forward<I>(iterable) },\n        _iterator{ std::move(iter) },\n        _offset{ offset } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 take_every_iterator& operator=(default_sentinel_t) {\n        _iterator = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        const auto distance = _iterable.end() - _iterator;\n        if (_offset >= distance) {\n            _iterator = _iterable.end();\n        }\n        else {\n            _iterator += _offset;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_iterator != _iterable.begin());\n        const auto remaining = _iterator - _iterable.begin();\n        const auto rem = remaining % _offset;\n        _iterator -= rem == 0 ? _offset : rem;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        const auto to_add = offset * _offset;\n\n        if (to_add >= 0) {\n            const auto current_distance = _iterable.end() - _iterator;\n            if (to_add <= current_distance) {\n                _iterator += to_add;\n                return;\n            }\n            LZ_ASSERT_ADDABLE(to_add - current_distance < _offset);\n            _iterator = _iterable.end();\n            return;\n        }\n\n        const auto current_distance = _iterator - _iterable.begin();\n        const auto remainder = current_distance % _offset;\n        if (remainder == 0) {\n            LZ_ASSERT_SUBTRACTABLE(-to_add <= current_distance);\n            _iterator += to_add;\n            return;\n        }\n        LZ_ASSERT_SUBTRACTABLE(-((offset + 1) * _offset - remainder) <= current_distance);\n        _iterator += (offset + 1) * _offset - remainder;\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const take_every_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.end() == other._iterable.end() && _offset == other._offset &&\n                             _iterable.begin() == other._iterable.begin());\n        return difference_impl(other._iterator);\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(default_sentinel_t) const {\n        return difference_impl(_iterable.end());\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const take_every_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.end() == other._iterable.end() && _offset == other._offset &&\n                             _iterable.begin() == other._iterable.begin());\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/take_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_WHILE_ITERATOR_HPP\n#define LZ_TAKE_WHILE_ITERATOR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/func_container.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/iterators/common.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class UnaryPredicate>\nclass take_while_iterator : public iterator<take_while_iterator<Iterable, UnaryPredicate>, ref_t<iter_t<Iterable>>,\n                                            fake_ptr_proxy<ref_t<iter_t<Iterable>>>, diff_type<iter_t<Iterable>>,\n                                            bidi_strongest_cat<iter_cat_t<iter_t<Iterable>>>, default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n\n    iter _iterator{};\n    Iterable _iterable{};\n    mutable UnaryPredicate _unary_predicate{};\n\n    using traits = std::iterator_traits<iter>;\n\n    LZ_CONSTEXPR_CXX_14 void incremented_check() {\n        if (_iterator != _iterable.end() && !_unary_predicate(*_iterator)) {\n            _iterator = _iterable.end();\n        }\n    }\n\npublic:\n    using value_type = typename traits::value_type;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr take_while_iterator(const take_while_iterator&)  = default;\n    LZ_CONSTEXPR_CXX_14 take_while_iterator& operator=(const take_while_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr take_while_iterator()\n        requires(std::default_initializable<iter> && std::default_initializable<UnaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = iter,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value &&\n                                 std::is_default_constructible<UnaryPredicate>::value>>\n    constexpr take_while_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                             std::is_nothrow_default_constructible<Iterable>::value &&\n                                             std::is_nothrow_default_constructible<UnaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 take_while_iterator(I&& iterable, UnaryPredicate unary_predicate, std::true_type /* is_begin */) :\n        _iterator{ std::begin(iterable) },\n        _iterable{ std::forward<I>(iterable) },\n        _unary_predicate{ std::move(unary_predicate) } {\n        incremented_check();\n    }\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 take_while_iterator(I&& iterable, UnaryPredicate unary_predicate, std::false_type /* is_begin */) :\n        _iterator{ std::end(iterable) },\n        _iterable{ std::forward<I>(iterable) },\n        _unary_predicate{ std::move(unary_predicate) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 take_while_iterator& operator=(default_sentinel_t) {\n        _iterator = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        ++_iterator;\n        incremented_check();\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_iterator != _iterable.begin());\n        if (_iterator != _iterable.end()) {\n            --_iterator;\n            return;\n        }\n        do {\n            --_iterator;\n        } while (_iterator != _iterable.begin() && !_unary_predicate(*_iterator));\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const take_while_iterator& other) const noexcept {\n        LZ_ASSERT_COMPATIBLE(_iterable.end() == other._iterable.end() && _iterable.begin() == other._iterable.begin());\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const noexcept {\n        return _iterator == _iterable.end();\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_TAKE_WHILE_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/iterators/unique.hpp",
    "content": "#pragma once\n\n#ifndef LZ_UNIQUE_ITERATOR_HPP\n#define LZ_UNIQUE_ITERATOR_HPP\n\n#include <Lz/algorithm/adjacent_find.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/util/default_sentinel.hpp>\n#include <algorithm>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class BinaryPredicate>\nclass unique_iterator : public iterator<unique_iterator<Iterable, BinaryPredicate>, ref_t<iter_t<Iterable>>,\n                                        fake_ptr_proxy<ref_t<iter_t<Iterable>>>, diff_type<iter_t<Iterable>>,\n                                        bidi_strongest_cat<iter_cat_t<iter_t<Iterable>>>, default_sentinel_t> {\n\n    using iter = iter_t<Iterable>;\n    using traits = std::iterator_traits<iter>;\n\n    iter _iterator{};\n    Iterable _iterable{};\n    mutable BinaryPredicate _predicate{};\n\npublic:\n    using value_type = typename traits::value_type;\n    using difference_type = typename traits::difference_type;\n    using reference = typename traits::reference;\n    using pointer = fake_ptr_proxy<reference>;\n\n    constexpr unique_iterator(const unique_iterator&) = default;\n    LZ_CONSTEXPR_CXX_14 unique_iterator& operator=(const unique_iterator&) = default;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr unique_iterator()\n        requires(std::default_initializable<iter> && std::default_initializable<Iterable> &&\n                 std::default_initializable<BinaryPredicate>)\n    = default;\n\n#else\n\n    template<class I = iter,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<Iterable>::value &&\n                                 std::is_default_constructible<BinaryPredicate>::value>>\n    constexpr unique_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                         std::is_nothrow_default_constructible<Iterable>::value &&\n                                         std::is_nothrow_default_constructible<BinaryPredicate>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr unique_iterator(I&& iterable, iter it, BinaryPredicate binary_predicate) :\n        _iterator{ std::move(it) },\n        _iterable{ std::forward<I>(iterable) },\n        _predicate{ std::move(binary_predicate) } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 unique_iterator& operator=(default_sentinel_t) {\n        _iterator = _iterable.end();\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return *_iterator;\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n\n        using std::adjacent_find;\n        _iterator = adjacent_find(std::move(_iterator), _iterable.end(), _predicate);\n\n        if (_iterator != _iterable.end()) {\n            ++_iterator;\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        LZ_ASSERT_DECREMENTABLE(_iterator != _iterable.begin());\n        --_iterator;\n        if (_iterator == _iterable.begin()) {\n            return;\n        }\n\n        auto next = _iterator;\n        for (--next; next != _iterable.begin(); --next, --_iterator) {\n            if (!_predicate(*next, *_iterator)) {\n                return;\n            }\n        }\n        _iterator = std::move(next);\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const unique_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_iterable.end() == other._iterable.end());\n        return _iterator == other._iterator;\n    }\n\n    constexpr bool eq(default_sentinel_t) const {\n        return _iterator == _iterable.end();\n    }\n};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/zip.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ZIP_ITERATOR_HPP\n#define LZ_ZIP_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/procs/decompose.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nclass zip_sentinel {\n    T value{};\n\n    template<class...>\n    friend class zip_iterator;\n\n    template<class...>\n    friend class zip_iterable;\n\n    explicit constexpr zip_sentinel(T v) noexcept(std::is_nothrow_move_constructible<T>::value) : value{ std::move(v) } {\n    }\n\npublic:\n#ifdef LZ_HAS_CXX_20\n    constexpr zip_sentinel()\n        requires(std::default_initializable<T>)\n    = default;\n#else\n    template<class I = T, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr zip_sentinel() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n#endif\n};\n\ntemplate<class... Iterables>\nclass zip_iterator : public iterator<zip_iterator<Iterables...>, iter_tuple_ref_type_t<std::tuple<iter_t<Iterables>...>>,\n                                     fake_ptr_proxy<iter_tuple_ref_type_t<std::tuple<iter_t<Iterables>...>>>,\n                                     iter_tuple_diff_type_t<std::tuple<iter_t<Iterables>...>>,\n                                     iter_tuple_iter_cat_t<std::tuple<iter_t<Iterables>...>>,\n                                     zip_sentinel<std::tuple<sentinel_t<Iterables>...>>> {\n\n    using iter_tuple = std::tuple<iter_t<Iterables>...>;\n\npublic:\n    using iterator_category = iter_tuple_iter_cat_t<iter_tuple>;\n    using value_type = iter_tuple_value_type_t<iter_tuple>;\n    using difference_type = iter_tuple_diff_type_t<iter_tuple>;\n    using reference = iter_tuple_ref_type_t<iter_tuple>;\n    using pointer = fake_ptr_proxy<reference>;\n    using sentinel = zip_sentinel<std::tuple<sentinel_t<Iterables>...>>;\n\nprivate:\n    using is = make_index_sequence<tuple_size<iter_tuple>::value>;\n    iter_tuple _iterators{};\n\n    template<size_t... I>\n    constexpr reference dereference(index_sequence<I...>) const {\n        return reference{ *std::get<I>(_iterators)... };\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void increment(index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        (++std::get<I>(_iterators), ...);\n#else\n        decompose(++std::get<I>(_iterators)...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void decrement(index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        (--std::get<I>(_iterators), ...);\n#else\n        decompose(--std::get<I>(_iterators)...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset, index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_iterators) += offset), ...);\n#else\n        decompose(std::get<I>(_iterators) += offset...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 difference_type minus(const zip_iterator& other, index_sequence<I...>) const {\n        const auto max = max_variadic(static_cast<difference_type>(std::get<I>(_iterators) - std::get<I>(other._iterators))...);\n        if (max > 0) {\n            return max;\n        }\n        return min_variadic(static_cast<difference_type>(std::get<I>(_iterators) - std::get<I>(other._iterators))...);\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 difference_type minus(const sentinel& other, index_sequence<I...>) const {\n        return max_variadic(static_cast<difference_type>(std::get<I>(_iterators) - std::get<I>(other.value))...);\n    }\n\n    template<class EndIter, size_t... I>\n    LZ_CONSTEXPR_CXX_14 bool eq(const EndIter& other, index_sequence<I...>) const {\n#ifdef LZ_HAS_CXX_17\n        return ((std::get<I>(_iterators) == std::get<I>(other)) || ...);\n#else\n        return max_variadic(static_cast<bool>(std::get<I>(_iterators) == std::get<I>(other))...);\n#endif\n    }\n\n    template<class cat = iterator_category, size_t... I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<!is_bidi_tag<cat>::value> assign_sentinels(const sentinel& other, index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_iterators) = std::get<I>(other.value)), ...);\n#else\n        decompose(std::get<I>(_iterators) = std::get<I>(other.value)...);\n#endif\n    }\n\n    template<class cat = iterator_category, size_t... I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<is_bidi_tag<cat>::value && !is_ra_tag<cat>::value>\n    assign_sentinels(const sentinel& other, index_sequence<I...>) {\n        while (*this != other) {\n            ++(*this);\n        }\n    }\n\n    template<class cat = iterator_category, size_t... I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<is_ra_tag<cat>::value> assign_sentinels(const sentinel& other, index_sequence<I...>) {\n        *this += (other - *this);\n    }\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr zip_iterator()\n        requires(std::default_initializable<iter_tuple>)\n    = default;\n\n#else\n\n    template<class I = iter_tuple, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr zip_iterator() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    constexpr zip_iterator(iter_tuple iterators) : _iterators{ std::move(iterators) } {\n        static_assert(tuple_size<iter_tuple>::value > 1, \"Cannot concat one/zero iterables\");\n    }\n\n    LZ_CONSTEXPR_CXX_14 zip_iterator& operator=(const sentinel& end) {\n        assign_sentinels(end, is{});\n        return *this;\n    }\n\n    constexpr reference dereference() const {\n        return dereference(is{});\n    }\n\n    constexpr pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        increment(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 void decrement() {\n        decrement(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        plus_is(offset, is{});\n    }\n\n    LZ_CONSTEXPR_CXX_20 difference_type difference(const zip_iterator& other) const {\n        return minus(other, is{});\n    }\n\n    LZ_CONSTEXPR_CXX_20 difference_type difference(const sentinel& other) const {\n        return minus(other, is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const zip_iterator& other) const {\n        return eq(other._iterators, is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const sentinel& other) const {\n        return eq(other.value, is{});\n    }\n};\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/iterators/zip_longest.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ZIP_LONGEST_ITERATOR_HPP\n#define LZ_ZIP_LONGEST_ITERATOR_HPP\n\n#include <Lz/detail/fake_ptr_proxy.hpp>\n#include <Lz/detail/iterator.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/detail/traits/index_sequence.hpp>\n#include <Lz/detail/tuple_helpers.hpp>\n#include <Lz/util/default_sentinel.hpp>\n#include <Lz/util/optional.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/procs/decompose.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class>\nstruct optional_iter_tuple_value_type;\n\ntemplate<class... Iterators>\nstruct optional_iter_tuple_value_type<std::tuple<Iterators...>> {\n    using type = std::tuple<optional<val_t<Iterators>>...>;\n};\n\ntemplate<class IterTuple>\nusing optional_value_type_iter_tuple_t = typename optional_iter_tuple_value_type<IterTuple>::type;\n\ntemplate<bool>\nstruct reference_or_value_type;\n\ntemplate<>\nstruct reference_or_value_type<true /* is lvalue reference */> {\n    template<class Ref, class>\n    using type = std::reference_wrapper<remove_ref_t<Ref>>;\n};\n\ntemplate<>\nstruct reference_or_value_type<false /* is lvalue reference */> {\n    template<class, class Val>\n    using type = Val;\n};\n\ntemplate<class Ref, class Val>\nusing reference_or_value_type_t = typename reference_or_value_type<std::is_lvalue_reference<Ref>::value>::template type<Ref, Val>;\n\ntemplate<class>\nstruct optional_iter_tuple_ref_type;\n\ntemplate<class... Iterators>\nstruct optional_iter_tuple_ref_type<std::tuple<Iterators...>> {\n    using type = std::tuple<optional<reference_or_value_type_t<ref_t<Iterators>, val_t<Iterators>>>...>;\n};\n\ntemplate<class IterTuple>\nusing optional_iter_tuple_ref_type_t = typename optional_iter_tuple_ref_type<IterTuple>::type;\n\ntemplate<class, class...>\nclass zip_longest_iterator;\n\ntemplate<class... Iterables>\nclass zip_longest_iterator<std::input_iterator_tag, Iterables...>\n    : public iterator<zip_longest_iterator<std::input_iterator_tag, Iterables...>,\n                      optional_iter_tuple_ref_type_t<std::tuple<iter_t<Iterables>...>>,\n                      fake_ptr_proxy<optional_iter_tuple_ref_type_t<std::tuple<iter_t<Iterables>...>>>,\n                      iter_tuple_diff_type_t<std::tuple<iter_t<Iterables>...>>,\n                      iter_tuple_iter_cat_t<std::tuple<iter_t<Iterables>...>>, default_sentinel_t> {\n\n    using iters = std::tuple<iter_t<Iterables>...>;\n    using sentinels = std::tuple<sentinel_t<Iterables>...>;\n\npublic:\n    using iterator_category = iter_tuple_iter_cat_t<iters>;\n    using value_type = optional_value_type_iter_tuple_t<iters>;\n    using difference_type = iter_tuple_diff_type_t<iters>;\n    using reference = optional_iter_tuple_ref_type_t<iters>;\n    using pointer = fake_ptr_proxy<value_type>;\n\nprivate:\n    using is = make_index_sequence<tuple_size<iters>::value>;\n\n    iters _iterators{};\n    sentinels _end{};\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 auto deref_one() const\n        -> optional<reference_or_value_type_t<ref_t<remove_cvref_t<decltype(std::get<I>(_iterators))>>,\n                                              val_t<remove_cvref_t<decltype(std::get<I>(_iterators))>>>> {\n\n        using iter_type = remove_cvref_t<decltype(std::get<I>(_iterators))>;\n        using ref_or_value_type = optional<reference_or_value_type_t<ref_t<iter_type>, val_t<iter_type>>>;\n\n        if (std::get<I>(_iterators) == std::get<I>(_end)) {\n            return lz::nullopt;\n        }\n\n        return ref_or_value_type{ *std::get<I>(_iterators) };\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 reference dereference(index_sequence<I...>) const {\n        return reference{ deref_one<I>()... };\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void increment(index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_iterators) != std::get<I>(_end) ? static_cast<void>(++std::get<I>(_iterators)) : (void)(0)), ...);\n#else\n        decompose(\n            (std::get<I>(_iterators) != std::get<I>(_end) ? static_cast<void>(++std::get<I>(_iterators)) : (void)(0), 0)...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_17 bool eq(const zip_longest_iterator& other, index_sequence<I...>) const {\n#ifdef LZ_HAS_CXX_17\n        return ((std::get<I>(_iterators) == std::get<I>(other._iterators)) && ...);\n#else\n        return min_variadic(static_cast<bool>(std::get<I>(_iterators) == std::get<I>(other._iterators))...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_17 bool eq(index_sequence<I...>) const {\n#ifdef LZ_HAS_CXX_17\n        return ((std::get<I>(_iterators) == std::get<I>(_end)) && ...);\n#else\n        return min_variadic(static_cast<bool>(std::get<I>(_iterators) == std::get<I>(_end))...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void assign_sentinels(index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_iterators) = std::get<I>(_end)), ...);\n#else\n        decompose(std::get<I>(_iterators) = std::get<I>(_end)...);\n#endif\n    }\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr zip_longest_iterator()\n        requires(std::default_initializable<iters> && std::default_initializable<sentinels>)\n    = default;\n\n#else\n\n    template<class I = iters,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<sentinels>::value>>\n    constexpr zip_longest_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                              std::is_nothrow_default_constructible<sentinels>::value) {\n    }\n\n#endif\n\n    LZ_CONSTEXPR_CXX_14 zip_longest_iterator(iters iterators, sentinels end) :\n        _iterators{ std::move(iterators) },\n        _end{ std::move(end) } {\n        static_assert(tuple_size<iters>::value > 1, \"Cannot concat one/zero iterables\");\n    }\n\n    LZ_CONSTEXPR_CXX_14 zip_longest_iterator& operator=(default_sentinel_t) {\n        assign_sentinels(is{});\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        return dereference(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        increment(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_17 bool eq(const zip_longest_iterator& other) const {\n        LZ_ASSERT_COMPATIBLE(_end == other._end);\n        return eq(other, is{});\n    }\n\n    LZ_CONSTEXPR_CXX_17 bool eq(default_sentinel_t) const {\n        return eq(is{});\n    }\n};\n\ntemplate<class... Iterables>\nclass zip_longest_iterator<std::forward_iterator_tag, Iterables...>\n    : public zip_longest_iterator<std::input_iterator_tag, Iterables...> {\n    using base = zip_longest_iterator<std::input_iterator_tag, Iterables...>;\n\npublic:\n    using base::base;\n    using base::operator=;\n};\n\ntemplate<class... Iterables>\nclass zip_longest_iterator<std::bidirectional_iterator_tag, Iterables...>\n    : public iterator<zip_longest_iterator<std::bidirectional_iterator_tag, Iterables...>,\n                      optional_iter_tuple_ref_type_t<std::tuple<iter_t<Iterables>...>>,\n                      fake_ptr_proxy<optional_iter_tuple_ref_type_t<std::tuple<iter_t<Iterables>...>>>,\n                      iter_tuple_diff_type_t<std::tuple<iter_t<Iterables>...>>,\n                      iter_tuple_iter_cat_t<std::tuple<iter_t<Iterables>...>>, default_sentinel_t> {\n\n    using iters = std::tuple<iter_t<Iterables>...>;\n    using sentinels = std::tuple<sentinel_t<Iterables>...>;\n\npublic:\n    using iterator_category = iter_tuple_iter_cat_t<iters>;\n    using value_type = optional_value_type_iter_tuple_t<iters>;\n    using difference_type = iter_tuple_diff_type_t<iters>;\n    using reference = optional_iter_tuple_ref_type_t<iters>;\n    using pointer = fake_ptr_proxy<value_type>;\n\nprivate:\n    static constexpr size_t tup_size = tuple_size<iters>::value;\n    using is = make_index_sequence<tup_size>;\n    using difference_tuple = decltype(tuple_of<difference_type>(is{}));\n\n    iters _iterators{};\n    std::tuple<Iterables...> _iterables{};\n    difference_tuple _distances{};\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 auto deref_one() const\n        -> optional<reference_or_value_type_t<ref_t<remove_cvref_t<decltype(std::get<I>(_iterators))>>,\n                                              val_t<remove_cvref_t<decltype(std::get<I>(_iterators))>>>> {\n\n        using iter_type = remove_cvref_t<decltype(std::get<I>(_iterators))>;\n        using ref_or_value_type = optional<reference_or_value_type_t<ref_t<iter_type>, val_t<iter_type>>>;\n\n        if (std::get<I>(_iterators) == std::get<I>(_iterables).end()) {\n            return lz::nullopt;\n        }\n\n        return ref_or_value_type{ *std::get<I>(_iterators) };\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 reference dereference(index_sequence<I...>) const {\n        return reference{ deref_one<I>()... };\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void increment(index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_iterators) != std::get<I>(_iterables).end()\n              ? static_cast<void>(++std::get<I>(_iterators), ++std::get<I>(_distances))\n              : (void)(0)),\n         ...);\n#else\n        decompose((std::get<I>(_iterators) != std::get<I>(_iterables).end()\n                       ? static_cast<void>(++std::get<I>(_iterators), ++std::get<I>(_distances))\n                       : (void)(0),\n                   0)...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void decrement(index_sequence<I...>) {\n        const auto longest = max_variadic(std::get<I>(_distances)...);\n        LZ_ASSERT_DECREMENTABLE(longest > 0);\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_distances) == longest ? static_cast<void>(--std::get<I>(_iterators), --std::get<I>(_distances))\n                                             : (void)(0)),\n         ...);\n#else\n        decompose((std::get<I>(_distances) == longest ? static_cast<void>(--std::get<I>(_iterators), --std::get<I>(_distances))\n                                                      : (void)(0),\n                   0)...);\n#endif\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 void plus_is_one(const difference_type offset) {\n        auto& it = std::get<I>(_iterators);\n        const auto end = std::get<I>(_iterables).end();\n        auto& distance = std::get<I>(_distances);\n\n        const auto difference = end - it;\n        if (offset > difference) {\n            it = end;\n            distance = difference;\n        }\n        else {\n            it += offset;\n            distance += offset;\n        }\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset, index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        (plus_is_one<I>(offset), ...);\n#else\n        decompose((plus_is_one<I>(offset), 0)...);\n#endif\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 void min_is_one(const difference_type longest, const difference_type offset) {\n        auto& this_iter_length = std::get<I>(_distances);\n        auto& it = std::get<I>(_iterators);\n\n        if (this_iter_length == longest) {\n            it -= offset;\n            this_iter_length -= offset;\n            return;\n        }\n        const auto surplus = this_iter_length + offset;\n        if (surplus > longest) {\n            const auto surplus_longest_diff = surplus - longest;\n            it -= surplus_longest_diff;\n            this_iter_length -= surplus_longest_diff;\n        }\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void min_is(const difference_type offset, index_sequence<I...>) {\n        const auto longest = max_variadic(std::get<I>(_distances)...);\n        LZ_ASSERT_SUBTRACTABLE(offset <= longest);\n#ifdef LZ_HAS_CXX_17\n        (min_is_one<I>(longest, offset), ...);\n#else\n        decompose((min_is_one<I>(longest, offset), 0)...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 difference_type minus(const zip_longest_iterator& other, index_sequence<I...>) const {\n        const auto max = max_variadic(static_cast<difference_type>(std::get<I>(_iterators) - std::get<I>(other._iterators))...);\n        if (max > 0) {\n            return max;\n        }\n        return min_variadic(static_cast<difference_type>(std::get<I>(_iterators) - std::get<I>(other._iterators))...);\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 difference_type minus(index_sequence<I...>) const {\n        return min_variadic(static_cast<difference_type>(std::get<I>(_iterators) - std::get<I>(_iterables).end())...);\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 bool eq(index_sequence<I...>) const {\n#ifdef LZ_HAS_CXX_17\n        return ((std::get<I>(_iterators) == std::get<I>(_iterables).end()) && ...);\n#else\n        return min_variadic(static_cast<bool>(std::get<I>(_iterators) == std::get<I>(_iterables).end())...);\n#endif\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 bool eq(const zip_longest_iterator& other, index_sequence<I...>) const {\n        LZ_ASSERT_COMPATIBLE(\n            min_variadic(static_cast<bool>(std::get<I>(_iterables).end() == std::get<I>(other._iterables).end())...) &&\n            min_variadic(static_cast<bool>(std::get<I>(_iterables).begin() == std::get<I>(other._iterables).begin())...));\n        return _iterators == other._iterators;\n    }\n\n    template<size_t... I>\n    LZ_CONSTEXPR_CXX_14 void assign_sentinels(index_sequence<I...>) {\n#ifdef LZ_HAS_CXX_17\n        ((std::get<I>(_iterators) = std::get<I>(_iterables).end()), ...);\n        ((std::get<I>(_distances) = lz::eager_ssize(std::get<I>(_iterables))), ...);\n#else\n        decompose(std::get<I>(_iterators) = std::get<I>(_iterables).end()...);\n        decompose(std::get<I>(_distances) = lz::eager_ssize(std::get<I>(_iterables))...);\n#endif\n    }\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr zip_longest_iterator()\n        requires(std::default_initializable<iters> && std::default_initializable<sentinels>)\n    = default;\n\n#else\n\n    template<class I = iters,\n             class = enable_if_t<std::is_default_constructible<I>::value && std::is_default_constructible<sentinels>::value>>\n    constexpr zip_longest_iterator() noexcept(std::is_nothrow_default_constructible<I>::value &&\n                                              std::is_nothrow_default_constructible<sentinels>::value) {\n    }\n\n#endif\n\n    template<class I>\n    constexpr zip_longest_iterator(I&& iterables, iters it, difference_tuple distances) :\n        _iterators{ std::move(it) },\n        _iterables{ std::forward<I>(iterables) },\n        _distances{ distances } {\n        static_assert(tup_size > 1, \"Cannot zip one/zero iterables\");\n    }\n\n    LZ_CONSTEXPR_CXX_14 zip_longest_iterator& operator=(default_sentinel_t) {\n        assign_sentinels(is{});\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 reference dereference() const {\n        LZ_ASSERT_DEREFERENCABLE(!eq(lz::default_sentinel));\n        return dereference(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 pointer arrow() const {\n        return fake_ptr_proxy<decltype(**this)>(**this);\n    }\n\n    LZ_CONSTEXPR_CXX_14 void increment() {\n        LZ_ASSERT_INCREMENTABLE(!eq(lz::default_sentinel));\n        increment(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_20 void decrement() {\n        decrement(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 void plus_is(const difference_type offset) {\n        if (offset < 0) {\n            min_is(-offset, is{});\n        }\n        else {\n            LZ_ASSERT_INCREMENTABLE(difference(lz::default_sentinel) <= offset);\n            plus_is(offset, is{});\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(const zip_longest_iterator& other) const {\n        return minus(other, is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 difference_type difference(default_sentinel_t) const {\n        return minus(is{});\n    }\n\n    LZ_CONSTEXPR_CXX_14 bool eq(const zip_longest_iterator& other) const {\n        return eq(other, is{});\n    }\n\n    LZ_CONSTEXPR_CXX_20 bool eq(default_sentinel_t) const {\n        return eq(is{});\n    }\n};\n\ntemplate<class... Iterables>\nclass zip_longest_iterator<std::random_access_iterator_tag, Iterables...>\n    : public zip_longest_iterator<std::bidirectional_iterator_tag, Iterables...> {\n    using base = zip_longest_iterator<std::bidirectional_iterator_tag, Iterables...>;\n\npublic:\n    using base::base;\n    using base::operator=;\n};\n\n#ifdef LZ_HAS_CXX_20\n\ntemplate<class... Iterables>\nclass zip_longest_iterator<std::contiguous_iterator_tag, Iterables...>\n    : public zip_longest_iterator<std::random_access_iterator_tag, Iterables...> {\n    using base = zip_longest_iterator<std::random_access_iterator_tag, Iterables...>;\n\npublic:\n    using base::base;\n    using base::operator=;\n};\n\n#endif\n\n} // namespace detail\n} // namespace lz\n#endif // LZ_ZIP_LONGEST_ITERATOR_HPP\n"
  },
  {
    "path": "include/Lz/detail/maybe_owned.hpp",
    "content": "#pragma once\n\n#ifndef LZ_MAYBE_OWNED_HPP\n#define LZ_MAYBE_OWNED_HPP\n\n#include <Lz/detail/procs/addressof.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n#include <Lz/detail/traits/is_sized.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/traits/iter_type.hpp>\n#include <Lz/traits/lazy_view.hpp>\n\n#ifdef LZ_HAS_CONCEPTS\n#include <Lz/traits/concepts.hpp>\n#endif\n\nnamespace lz {\nnamespace detail {\ntemplate<class Iterable, bool /* is base of lazy_view && !is c array */>\nclass maybe_owned_impl;\n\ntemplate<class Iterable>\nclass maybe_owned_impl<Iterable, false> : public lazy_view {\n    Iterable* _iterable_ref_ptr{ nullptr };\n\npublic:\n    static constexpr bool holds_reference = true;\n\n    constexpr maybe_owned_impl() noexcept = default;\n\n    constexpr maybe_owned_impl(const maybe_owned_impl<Iterable, false>& other) = default;\n    constexpr maybe_owned_impl(maybe_owned_impl<Iterable, false>&& other) = default;\n    LZ_CONSTEXPR_CXX_14 maybe_owned_impl& operator=(const maybe_owned_impl<Iterable, false>& other) = default;\n    LZ_CONSTEXPR_CXX_14 maybe_owned_impl& operator=(maybe_owned_impl<Iterable, false>&& other) = default;\n\n    template<class I>\n    constexpr maybe_owned_impl(I&& iterable) noexcept : _iterable_ref_ptr{ detail::addressof(iterable) } {\n        static_assert(std::is_lvalue_reference<I>::value,\n                      \"Can only bind to lvalues. Check if you are passing a temporary \"\n                      \"object, forgot to add/remove const/volatile qualifiers or forgot to inherit from lz::lazy_view.\");\n    }\n\n    template<class T, size_t N>\n    constexpr maybe_owned_impl(const T (&iterable)[N]) noexcept : _iterable_ref_ptr{ detail::addressof(iterable) } {\n    }\n\n    template<class T, size_t N>\n    constexpr maybe_owned_impl(T (&iterable)[N]) noexcept : _iterable_ref_ptr{ detail::addressof(iterable) } {\n    }\n\n    template<class I>\n    constexpr maybe_owned_impl(const maybe_owned_impl<I, false>& other) noexcept : _iterable_ref_ptr{ other._iterable_ref_ptr } {\n    }\n\n    template<class I>\n    constexpr maybe_owned_impl(maybe_owned_impl<I, false>& other) noexcept : _iterable_ref_ptr{ other._iterable_ref_ptr } {\n    }\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 maybe_owned_impl& operator=(const maybe_owned_impl<I, false>& other) noexcept {\n        if (this == &other) {\n            return *this;\n        }\n        _iterable_ref_ptr = other._iterable_ref_ptr;\n        return *this;\n    }\n\n    template<class I>\n    LZ_CONSTEXPR_CXX_14 maybe_owned_impl& operator=(maybe_owned_impl<I, false>& other) noexcept {\n        if (this == &other) {\n            return *this;\n        }\n        _iterable_ref_ptr = other._iterable_ref_ptr;\n        return *this;\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>(lz::size(*_iterable_ref_ptr));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_sized<I>::value, size_t> size() const {\n        return static_cast<size_t>(lz::size(*_iterable_ref_ptr));\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> begin() const {\n        if (!_iterable_ref_ptr) {\n            return iter_t<Iterable>{};\n        }\n        return detail::begin(*_iterable_ref_ptr);\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 sentinel_t<Iterable> end() const {\n        if (!_iterable_ref_ptr) {\n            return sentinel_t<Iterable>{};\n        }\n        return detail::end(*_iterable_ref_ptr);\n    }\n};\n\n// Class that contains a c-array or a view to a maybe_owned_impl<Iterable, true>\ntemplate<class Iterable>\nclass maybe_owned_impl<Iterable, true> : public lazy_view {\n    using it = typename std::remove_cv<Iterable>::type;\n    it _iterable_value{};\n\npublic:\n    static constexpr bool holds_reference = false;\n\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr maybe_owned_impl()\n        requires(std::default_initializable<it>)\n    = default;\n\n#else\n\n    template<class I = it, class = enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr maybe_owned_impl() noexcept(std::is_nothrow_default_constructible<I>::value) {\n    }\n\n#endif\n\n    constexpr maybe_owned_impl(const maybe_owned_impl<Iterable, true>& other) = default;\n    constexpr maybe_owned_impl(maybe_owned_impl<Iterable, true>&& other) = default;\n    LZ_CONSTEXPR_CXX_14 maybe_owned_impl& operator=(const maybe_owned_impl<Iterable, true>& other) = default;\n    LZ_CONSTEXPR_CXX_14 maybe_owned_impl& operator=(maybe_owned_impl<Iterable, true>&& other) = default;\n\n    constexpr maybe_owned_impl(it&& iterable) noexcept(std::is_nothrow_move_constructible<it>::value) :\n        _iterable_value{ std::move(iterable) } {\n    }\n\n    constexpr maybe_owned_impl(const it& iterable) : _iterable_value{ iterable } {\n    }\n\n    template<class I>\n    constexpr maybe_owned_impl(maybe_owned_impl<I, true>&& other) noexcept(std::is_nothrow_move_constructible<it>::value) :\n        maybe_owned_impl{ std::move(other._iterable_value) } {\n    }\n\n    template<class I>\n    constexpr maybe_owned_impl(const maybe_owned_impl<I, true>& other) : maybe_owned_impl{ other._iterable_value } {\n    }\n\n    template<class I>\n    maybe_owned_impl& operator=(const maybe_owned_impl<I, true>& other) {\n        if (this == &other) {\n            return *this;\n        }\n        _iterable_value = other._iterable_value;\n        return *this;\n    }\n\n    template<class I>\n    maybe_owned_impl& operator=(maybe_owned_impl<I, true>&& other) noexcept(std::is_nothrow_move_assignable<it>::value) {\n        if (this == &other) {\n            return *this;\n        }\n        _iterable_value = std::move(other._iterable_value);\n        return *this;\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    [[nodiscard]] constexpr size_t size() const\n        requires(sized<Iterable>)\n    {\n        return static_cast<size_t>(lz::size(_iterable_value));\n    }\n\n#else\n\n    template<class I = Iterable>\n    LZ_NODISCARD constexpr enable_if_t<is_sized<I>::value, size_t> size() const {\n        return static_cast<size_t>(lz::size(_iterable_value));\n    }\n\n#endif\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> begin() const {\n        return _iterable_value.begin();\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 sentinel_t<Iterable> end() const {\n        return _iterable_value.end();\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 iter_t<Iterable> begin() {\n        return _iterable_value.begin();\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 sentinel_t<Iterable> end() {\n        return _iterable_value.end();\n    }\n};\n\ntemplate<class Iterable>\nusing maybe_owned = maybe_owned_impl<Iterable, std::is_base_of<lazy_view, typename std::remove_cv<Iterable>::type>::value>;\n} // namespace detail\n\n/**\n * @brief Class that contains a reference or a copy of an iterable depending on the type of the iterable. If the iterable is\n * inherited from `lz::lazy_view`, it will store a copy of the iterable. In all other cases, it will store a reference to the\n * iterable. This is done because lazy views are generally cheap to copy, because they contain references to the actual container,\n * rather than the container itself. This class is used in various places in the library to store a reference or a copy of an\n * iterable, depending on the type of the iterable.\n * Example:\n * ```cpp\n * std::vector<int> vec{ 1, 2, 3 };\n * lz::maybe_owned<std::vector<int>> maybe_owned{ vec }; // will store a reference to the vector\n *\n * auto filter = vec | lz::filter([](int i) { return i > 1; });\n * lz::maybe_owned<decltype(filter)> maybe_owned_filter{ filter }; // will store a copy of the filter\n * ```\n */\nusing detail::maybe_owned;\n\n/**\n * @brief This class can be useful for iterables that are not inherited from `lz::lazy_view`, but are still easy to copy. For\n * instance, you can also use `lz::copied<boost::iterator_range<int*>>` to hold a copy of the underlying range.\n * Internally, this will only copy the pointers to the data, not the data itself.\n *\n * But, for instance, `lz::copied<std::vector<int>>` will hold a copy of the underlying vector. This of course will in\n * turn be slow because it needs to copy all the vector data into the copied iterable.\n * Example:\n * ```cpp\n * std::vector<int> vec{ 1, 2, 3 };\n * lz::copied<std::vector<int>> copied{ vec }; // will store a copy of the vector\n *\n * boost::iterator_range<int*> range{ vec.data(), vec.data() + vec.size() }); // Holds a copy to vector data\n * // lz::filter(range, [](int i) { return i > 1; }); // *CAREFUL* this will hold a reference because it is not a lazy view\n * // Instead, use:\n * lz::copied<boost::iterator_range<int*>> copied{ range }; // will store a copy of the range\n * ```\n */\ntemplate<class Iterable>\nusing copied = detail::maybe_owned_impl<Iterable, true>;\n\n/**\n * @brief This helper function can be useful for iterables that are not inherited from `lz::lazy_view`, but are still easy to\n * copy. For instance, you can use `lz::copied<boost::iterator_range<int*>>` to hold a copy of the underlying range,\n * instead of a reference (default behaviour, as it is not inherited from lazy_view). Internally, this will only copy the pointers\n * to the data, not the data itself.\n *\n * But, for instance, `lz::copied<std::vector<int>>` will also hold a copy of the underlying vector. This of course will\n * in turn be slow because it needs to copy all the vector data into the copied iterable. Example:\n * ```cpp\n * std::vector<int> vec{ 1, 2, 3 };\n * auto copied_vec = lz::as_copied(vec); // will store a copy of the vector, slow\n * auto copied_vec = lz::as_copied(std::move(vec)); // will store a copy of the vector, fast with std::move\n *\n * boost::iterator_range<int*> range{ vec.data(), vec.data() + vec.size() });\n * // lz::filter(range, [](int i) { return i > 1; }); // *CAREFUL* this will hold a reference to `range` because range is *NOT*\n * inherited\n * // from `lazy_view`. Instead, use:\n * auto copied_range = lz::as_copied(range); // will store a copy of the range, cheap\n * ```\n */\ntemplate<class Iterable>\nconstexpr copied<detail::remove_ref_t<Iterable>> as_copied(Iterable&& iterable) {\n    return copied<detail::remove_ref_t<Iterable>>{ std::forward<Iterable>(iterable) };\n}\n} // namespace lz\n\n#endif // LZ_MAYBE_OWNED_HPP\n"
  },
  {
    "path": "include/Lz/detail/procs/addressof.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_ADDRESSOF_HPP\n#define LZ_DETAIL_PROCS_ADDRESSOF_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <type_traits>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class T>\n[[nodiscard]] constexpr T* addressof(T& arg) noexcept {\n    if constexpr (std::is_object_v<T>) {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wcast-align\"\n#endif\n        return reinterpret_cast<T*>(&const_cast<char&>(reinterpret_cast<const volatile char&>(arg)));\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n    else {\n        return &arg;\n    }\n}\n\ntemplate<class T>\nLZ_NODISCARD constexpr enable_if_t<!std::is_object<T>::value, T*> addressof(T& arg) noexcept {\n    return &arg;\n}\n\n#else\n\ntemplate<class T>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_17 enable_if_t<std::is_object<T>::value, T*> addressof(T& arg) noexcept {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wcast-align\"\n#endif\n    return reinterpret_cast<T*>(&const_cast<char&>(reinterpret_cast<const volatile char&>(arg)));\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n}\n\ntemplate<class T>\nLZ_NODISCARD constexpr enable_if_t<!std::is_object<T>::value, T*> addressof(T& arg) noexcept {\n    return &arg;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/procs/assert.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_ASSERT_HPP\n#define LZ_DETAIL_PROCS_ASSERT_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\n// clang-format off\n\n#if defined(LZ_DEBUG_ASSERTIONS)\n  #define LZ_USE_DEBUG_ASSERTIONS\n#endif\n\n#if defined(LZ_USE_DEBUG_ASSERTIONS)\n  #include <cstdio>\n  #include <exception>\n  #if defined(LZ_HAS_CXX_23) && LZ_HAS_INCLUDE(<stacktrace>)\n    #include <stacktrace>\n  #endif\n#endif\n\n// clang-format on\n\n#if defined(LZ_USE_DEBUG_ASSERTIONS)\n\nnamespace lz {\nnamespace detail {\n\n[[noreturn]] inline void\nassertion_fail(const char* file, const int line, const char* func, const char* message, const char* expr) {\n#if defined(LZ_HAS_CXX_23) && defined(__cpp_lib_stacktrace) && LZ_HAS_INCLUDE(<stacktrace>)\n\n    auto st = std::stacktrace::current();\n    auto str = std::to_string(st);\n    std::fprintf(stderr, \"%s:%d assertion \\\"%s\\\" failed in function '%s' with message:\\n\\t%s\\nStacktrace:\\n%s\\n\", file, line,\n                 expr, func, message, str.c_str());\n\n#else // ^^ defined(__cpp_lib_stacktrace) vv !defined(__cpp_lib_stacktrace)\n\n    std::fprintf(stderr, \"%s:%d assertion \\\"%s\\\" failed in function '%s' with message:\\n\\t%s\\n\", file, line, expr, func, message);\n\n#endif // __cpp_lib_stacktrace\n\n    std::terminate();\n}\n} // namespace detail\n} // namespace lz\n\n#define LZ_ASSERT(CONDITION, MSG)                                                                                                \\\n    ((CONDITION) ? (static_cast<void>(0)) : (lz::detail::assertion_fail(__FILE__, __LINE__, __func__, MSG, #CONDITION)))\n#define LZ_ASSERT_INCREMENTABLE(CONDITION) LZ_ASSERT(CONDITION, \"Cannot increment after end\")\n#define LZ_ASSERT_DECREMENTABLE(CONDITION) LZ_ASSERT(CONDITION, \"Cannot decrement before begin\")\n#define LZ_ASSERT_DEREFERENCABLE(CONDITION) LZ_ASSERT(CONDITION, \"Cannot dereference end\")\n#define LZ_ASSERT_ADDABLE(CONDITION) LZ_ASSERT(CONDITION, \"Cannot add after end\")\n#define LZ_ASSERT_SUBTRACTABLE(CONDITION) LZ_ASSERT(CONDITION, \"Cannot subtract before end\")\n#define LZ_ASSERT_SUB_ADDABLE(CONDITION) LZ_ASSERT(CONDITION, \"Cannot add after end/cannot subtract before begin\")\n#define LZ_ASSERT_COMPATIBLE(CONDITION) LZ_ASSERT(CONDITION, \"Incompatible iterators\")\n\n#else // ^^ defined(LZ_USE_DEBUG_ASSERTIONS) vv !defined(LZ_USE_DEBUG_ASSERTIONS)\n\n#define LZ_ASSERT(CONDITION, MSG)                                                                                                \\\n    static_cast<void>(CONDITION);                                                                                                \\\n    static_cast<void>(MSG)\n\n#define LZ_ASSERT_INCREMENTABLE(CONDITION) static_cast<void>(CONDITION)\n#define LZ_ASSERT_DECREMENTABLE(CONDITION) static_cast<void>(CONDITION)\n#define LZ_ASSERT_DEREFERENCABLE(CONDITION) static_cast<void>(CONDITION)\n#define LZ_ASSERT_ADDABLE(CONDITION) static_cast<void>(CONDITION)\n#define LZ_ASSERT_SUBTRACTABLE(CONDITION) static_cast<void>(CONDITION)\n#define LZ_ASSERT_SUB_ADDABLE(CONDITION) static_cast<void>(CONDITION)\n#define LZ_ASSERT_COMPATIBLE(CONDITION) static_cast<void>(CONDITION)\n\n#endif // defined(LZ_USE_DEBUG_ASSERTIONS)\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/procs/begin_end.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_BEGIN_END_HPP\n#define LZ_DETAIL_PROCS_BEGIN_END_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class C>\nLZ_NODISCARD constexpr auto begin(C& c) -> decltype(c.begin()) {\n    return c.begin();\n}\n\ntemplate<class C>\nLZ_NODISCARD constexpr auto begin(const C& c) -> decltype(c.begin()) {\n    return c.begin();\n}\n\ntemplate<class C>\nLZ_NODISCARD constexpr auto end(C& c) -> decltype(c.end()) {\n    return c.end();\n}\n\ntemplate<class C>\nLZ_NODISCARD constexpr auto end(const C& c) -> decltype(c.end()) {\n    return c.end();\n}\n\ntemplate<class C, size_t N>\nLZ_NODISCARD constexpr C* begin(C (&c)[N]) noexcept {\n    return c;\n}\n\ntemplate<class C, size_t N>\nLZ_NODISCARD constexpr C* end(C (&c)[N]) noexcept {\n    return c + N;\n}\n\ntemplate<class C, size_t N>\nLZ_NODISCARD constexpr const C* begin(const C (&c)[N]) noexcept {\n    return c;\n}\n\ntemplate<class C, size_t N>\nLZ_NODISCARD constexpr const C* end(const C (&c)[N]) noexcept {\n    return c + N;\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/procs/decompose.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_DECOMPOSE_HPP\n#define LZ_DETAIL_PROCS_DECOMPOSE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\n#ifndef LZ_HAS_CXX_17\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class... Ts>\nLZ_CONSTEXPR_CXX_14 void decompose(const Ts&...) noexcept {\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_HAS_CXX_17\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/procs/distance.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_DISTANCE_HPP\n#define LZ_DETAIL_PROCS_DISTANCE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator, class S>\n[[nodiscard]] constexpr diff_type<Iterator> distance_impl(Iterator begin, S end) {\n    if constexpr (is_ra_v<Iterator>) {\n        return end - begin;\n    }\n    else {\n        diff_type<Iterator> dist = 0;\n        for (; begin != end; ++begin, ++dist) {\n        }\n        return dist;\n    }\n}\n\n#else\n\ntemplate<class Iterator, class S>\nLZ_NODISCARD constexpr enable_if_t<is_ra<Iterator>::value, diff_type<Iterator>> distance_impl(Iterator begin, S end) {\n    return end - begin;\n}\n\ntemplate<class Iterator, class S>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 enable_if_t<!is_ra<Iterator>::value, diff_type<Iterator>> distance_impl(Iterator begin, S end) {\n    diff_type<Iterator> dist = 0;\n    for (; begin != end; ++begin, ++dist) {\n    }\n    return dist;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/procs/get_end.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_GET_END_HPP\n#define LZ_DETAIL_PROCS_GET_END_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterator, class S>\nLZ_NODISCARD constexpr Iterator get_end(Iterator begin, S end) {\n    return begin + (end - begin);\n}\n\ntemplate<class Iterator>\nLZ_NODISCARD constexpr Iterator get_end(Iterator, Iterator end) {\n    return end;\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/procs/min_max.hpp",
    "content": "#pragma once\n\n#include <Lz/detail/compiler_config.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nconstexpr T max_variadic2(const T a, const T b) {\n    return a > b ? a : b;\n}\n\ntemplate<class T, class... U>\nconstexpr T max_variadic2(const T value1, const T value2, const U... values) {\n    return value1 < value2 ? max_variadic2(value2, values...) : max_variadic2(value1, values...);\n}\n\ntemplate<class T, class... U>\nconstexpr T max_variadic(const T value, const U... values) {\n    return max_variadic2(value, values...);\n}\n\ntemplate<class T>\nconstexpr T min_variadic2(const T a, const T b) {\n    return a < b ? a : b;\n}\n\ntemplate<class T, class... U>\nconstexpr T min_variadic2(const T value1, const T value2, const U... values) {\n    return value1 > value2 ? min_variadic2(value2, values...) : min_variadic2(value1, values...);\n}\n\ntemplate<class T, class... U>\nconstexpr T min_variadic(const T value, const U... values) {\n    return min_variadic2(value, values...);\n}\n\n} // namespace detail\n} // namespace lz\n"
  },
  {
    "path": "include/Lz/detail/procs/next_fast.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_NEXT_FAST_HPP\n#define LZ_DETAIL_PROCS_NEXT_FAST_HPP\n\n#include <Lz/detail/procs/begin_end.hpp>\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <Lz/detail/traits/is_sized.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n\n#ifndef LZ_HAS_CXX_17\n\n#include <Lz/detail/traits/enable_if.hpp>\n\n#endif\n\n// next_fast(_safe): check whether it's faster to go forward or backward when incrementing n steps\n// Only relevant for sized (requesting size is then O(1)) bidirectional iterators because\n// for random access iterators this is O(1) anyway. For forward iterators this is not O(1),\n// but we can't go any other way than forward. Random access iterators will use the same\n// implementation as the forward non-sized iterables so we can skip that if statement.\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class I>\n[[nodiscard]] constexpr iter_t<I> next_fast(I&& iterable, diff_iterable_t<I> n) {\n    using iter = iter_t<I>;\n\n    if constexpr (is_sized_v<I> && !is_sentinel_v<iter, sentinel_t<I>> && is_bidi_v<iter>) {\n        const auto size = lz::ssize(iterable);\n        if (n > size / 2) {\n            return std::prev(detail::end(iterable), size - n);\n        }\n    }\n    return std::next(detail::begin(iterable), n);\n}\n\ntemplate<class I>\n[[nodiscard]] constexpr iter_t<I> next_fast_safe(I&& iterable, const diff_iterable_t<I> n) {\n\n    auto begin = iterable.begin();\n    auto end = iterable.end();\n\n    if constexpr (is_sized_v<I> && is_bidi_v<iter_t<I>> && !is_sentinel_v<iter_t<I>, sentinel_t<I>>) {\n        const auto size = lz::ssize(iterable);\n        if (n >= size) {\n            return end;\n        }\n        return next_fast(std::forward<I>(iterable), n);\n    }\n    else {\n        using diff_type_u = std::make_unsigned_t<diff_iterable_t<I>>;\n        for (diff_type_u i = 0; i < static_cast<diff_type_u>(n) && begin != end; ++i, ++begin) {\n        }\n        return begin;\n    }\n}\n\n#else\n\ntemplate<class I>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_17\n    enable_if_t<is_sized<I>::value && !is_sentinel<iter_t<I>, sentinel_t<I>>::value && is_bidi<iter_t<I>>::value, iter_t<I>>\n    next_fast(I&& iterable, const diff_iterable_t<I> n) {\n\n    const auto size = lz::ssize(iterable);\n    if (n > size / 2) {\n        return std::prev(detail::end(iterable), size - n);\n    }\n    return std::next(detail::begin(iterable), n);\n}\n\ntemplate<class I>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_17\n    enable_if_t<!is_sized<I>::value || is_sentinel<iter_t<I>, sentinel_t<I>>::value || !is_bidi<iter_t<I>>::value, iter_t<I>>\n    next_fast(I&& iterable, diff_iterable_t<I> n) {\n    return std::next(detail::begin(iterable), n);\n}\n\ntemplate<class I>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_17\n    enable_if_t<is_sized<I>::value && !is_sentinel<iter_t<I>, sentinel_t<I>>::value && is_bidi<iter_t<I>>::value, iter_t<I>>\n    next_fast_safe(I&& iterable, const diff_iterable_t<I> n) {\n\n    const auto size = lz::ssize(iterable);\n    if (n >= size) {\n        return detail::end(iterable);\n    }\n    return next_fast(std::forward<I>(iterable), n);\n}\n\ntemplate<class I>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14\n    enable_if_t<!is_sized<I>::value || is_sentinel<iter_t<I>, sentinel_t<I>>::value || !is_bidi<iter_t<I>>::value, iter_t<I>>\n    next_fast_safe(I&& iterable, const diff_iterable_t<I> n) {\n\n    auto begin = detail::begin(iterable);\n    const auto end = detail::end(iterable);\n\n    using diff_type_u = typename std::make_unsigned<diff_iterable_t<I>>::type;\n    for (diff_type_u i = 0; i < static_cast<diff_type_u>(n) && begin != end; ++i, ++begin) {\n    }\n    return begin;\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/procs/operators.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_OPERATORS_HPP\n#define LZ_DETAIL_PROCS_OPERATORS_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <functional>\n\n#ifdef LZ_HAS_CXX_14\n#define LZ_BIN_OP(OP, T) std::OP<>\n#else\n#define LZ_BIN_OP(OP, T) std::OP<T>\n#endif\n\n#endif // LZ_DETAIL_PROCS_OPERATORS_HPP\n"
  },
  {
    "path": "include/Lz/detail/procs/sentinel_operators.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_SENTINEL_OPERATORS_HPP\n#define LZ_DETAIL_PROCS_SENTINEL_OPERATORS_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class IterCat, class S>\nstruct iterator;\n\n} // namespace detail\n} // namespace lz\n\nLZ_MODULE_EXPORT namespace lz {\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class IterCat, class Sentinel>\nconstexpr bool\noperator==(const Sentinel& sent, const detail::iterator<Derived, Reference, Pointer, DifferenceType, IterCat, Sentinel>& it) {\n    return it.operator==(sent);\n}\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class IterCat, class Sentinel>\nconstexpr bool\noperator!=(const Sentinel& sent, const detail::iterator<Derived, Reference, Pointer, DifferenceType, IterCat, Sentinel>& it) {\n    return !(it == sent);\n}\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class IterCat, class Sentinel>\nLZ_NODISCARD constexpr bool\noperator<(const Sentinel& sent, const detail::iterator<Derived, Reference, Pointer, DifferenceType, IterCat, Sentinel>& it) {\n    return sent - it < 0;\n}\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class IterCat, class Sentinel>\nLZ_NODISCARD constexpr bool\noperator>(const Sentinel& sent, const detail::iterator<Derived, Reference, Pointer, DifferenceType, IterCat, Sentinel>& it) {\n    return it < sent;\n}\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class IterCat, class Sentinel>\nLZ_NODISCARD constexpr bool\noperator<=(const Sentinel& sent, const detail::iterator<Derived, Reference, Pointer, DifferenceType, IterCat, Sentinel>& it) {\n    return !(it < sent);\n}\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class IterCat, class Sentinel>\nLZ_NODISCARD constexpr bool\noperator>=(const Sentinel& sent, const detail::iterator<Derived, Reference, Pointer, DifferenceType, IterCat, Sentinel>& it) {\n    return !(sent < it);\n}\n\ntemplate<class Derived, class Reference, class Pointer, class DifferenceType, class IterCat, class Sentinel>\nLZ_NODISCARD constexpr typename Derived::difference_type\noperator-(const Sentinel& sent, const detail::iterator<Derived, Reference, Pointer, DifferenceType, IterCat, Sentinel>& it) {\n    return -(it - sent);\n}\n\n} // namespace lz\n\n#endif // LZ_DETAIL_PROCS_SENTINEL_OPERATORS_HPP\n"
  },
  {
    "path": "include/Lz/detail/procs/tuple_expand.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_PROCS_TUPLE_EXPAND_HPP\n#define LZ_DETAIL_PROCS_TUPLE_EXPAND_HPP\n\n#include <Lz/detail/traits/index_sequence.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n\n#ifndef LZ_HAS_CONCEPTS\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\n#include <utility>\n#include <type_traits>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Fn>\nclass tuple_expand {\n    Fn _fn;\n\npublic:\n#ifdef LZ_HAS_CONCEPTS\n\n    constexpr tuple_expand()\n        requires std::default_initializable<Fn>\n    = default;\n\n#else\n\n    template<class F, class = enable_if_t<std::is_default_constructible<F>::value>>\n    constexpr tuple_expand() noexcept(std::is_nothrow_default_constructible<F>::value) {\n    }\n\n#endif\n\n    template<class F>\n    explicit constexpr tuple_expand(F&& fn) : _fn{ std::forward<F>(fn) } {\n    }\n\nprivate:\n    template<class Tuple, size_t... I>\n    LZ_CONSTEXPR_CXX_14 auto call(Tuple&& tuple, index_sequence<I...>) -> decltype(_fn(std::get<I>(std::forward<Tuple>(tuple))...)) {\n        return _fn(std::get<I>(std::forward<Tuple>(tuple))...);\n    }\n\n    template<class Tuple, size_t... I>\n    LZ_CONSTEXPR_CXX_14 auto\n    call(Tuple&& tuple, index_sequence<I...>) const -> decltype(_fn(std::get<I>(std::forward<Tuple>(tuple))...)) {\n        return _fn(std::get<I>(std::forward<Tuple>(tuple))...);\n    }\n\npublic:\n    template<class Tuple>\n    LZ_CONSTEXPR_CXX_14 auto\n    operator()(Tuple&& tuple) -> decltype(call(std::forward<Tuple>(tuple),\n                                               make_index_sequence<std::tuple_size<remove_cvref_t<Tuple>>::value>{})) {\n        return call(std::forward<Tuple>(tuple), make_index_sequence<std::tuple_size<remove_cvref_t<Tuple>>::value>{});\n    }\n\n    template<class Tuple>\n    LZ_CONSTEXPR_CXX_14 auto\n    operator()(Tuple&& tuple) const -> decltype(call(std::forward<Tuple>(tuple),\n                                                     make_index_sequence<std::tuple_size<remove_cvref_t<Tuple>>::value>{})) {\n        return call(std::forward<Tuple>(tuple), make_index_sequence<std::tuple_size<remove_cvref_t<Tuple>>::value>{});\n    }\n};\n\ntemplate<class Fn>\nconstexpr tuple_expand<typename std::decay<Fn>::type> make_expand_fn(Fn&& fn) {\n    return tuple_expand<typename std::decay<Fn>::type>{ std::forward<Fn>(fn) };\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/conditional.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_CONDITIONAL_HPP\n#define LZ_DETAIL_TRAITS_CONDITIONAL_HPP\n\nnamespace lz {\nnamespace detail {\n\ntemplate<bool B>\nstruct conditional_impl;\n\ntemplate<>\nstruct conditional_impl<true> {\n    template<class IfTrue, class /* IfFalse */>\n    using type = IfTrue;\n};\n\ntemplate<>\nstruct conditional_impl<false> {\n    template<class /* IfTrue */, class IfFalse>\n    using type = IfFalse;\n};\n\ntemplate<bool B, class IfTrue, class IfFalse>\nusing conditional_t = typename conditional_impl<B>::template type<IfTrue, IfFalse>;\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/conjunction.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_CONJUNCTION_HPP\n#define LZ_DETAIL_TRAITS_CONJUNCTION_HPP\n\n#include <Lz/detail/traits/conditional.hpp>\n#include <type_traits>\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class... Ts>\nstruct conjunction : std::bool_constant<(Ts::value && ...)> {};\n\n#else\n\ntemplate<class... Ts>\nstruct conjunction : std::true_type {};\n\ntemplate<class T, class... Ts>\nstruct conjunction<T, Ts...> : conditional_t<T::value, conjunction<Ts...>, std::false_type> {};\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/enable_if.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_ENABLE_IF_HPP\n#define LZ_DETAIL_TRAITS_ENABLE_IF_HPP\n\nnamespace lz {\nnamespace detail {\n\ntemplate<bool B>\nstruct enable_if {};\n\ntemplate<>\nstruct enable_if<true> {\n    template<class T>\n    using type = T;\n};\n\ntemplate<bool B, class T = void>\nusing enable_if_t = typename enable_if<B>::template type<T>;\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_ENABLE_IF_HPP\n"
  },
  {
    "path": "include/Lz/detail/traits/first_arg.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_FIRST_ARG_HPP\n#define LZ_DETAIL_TRAITS_FIRST_ARG_HPP\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class... Args>\nstruct first_arg {};\n\ntemplate<class Arg, class... Rest>\nstruct first_arg<Arg, Rest...> {\n    using type = Arg;\n};\n\ntemplate<>\nstruct first_arg<> {\n    using type = void;\n};\n\ntemplate<class... Args>\nusing first_arg_t = typename first_arg<Args...>::type;\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/func_ret_type.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_FUNC_RET_TYPE_HPP\n#define LZ_DETAIL_TRAITS_FUNC_RET_TYPE_HPP\n\n#include <utility>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Function, class... Args>\nusing func_ret_type = decltype(std::declval<Function>()(std::declval<Args>()...));\n\ntemplate<class Function, class Iterator>\nusing func_ret_type_iter = decltype(std::declval<Function>()(*std::declval<Iterator>()));\n\ntemplate<class Function, class... Iterator>\nusing func_ret_type_iters = decltype(std::declval<Function>()(*(std::declval<Iterator>())...));\n\n}}\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/index_sequence.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_INDEX_SEQUENCE_HPP\n#define LZ_DETAIL_TRAITS_INDEX_SEQUENCE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\n#ifndef LZ_HAS_CXX_11\n#include <utility>\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_11\n\ntemplate<size_t... Is>\nstruct index_sequence {\n    using type = index_sequence;\n};\n\ntemplate<size_t N>\nstruct make_index_sequence_impl {\nprivate:\n    using left = typename make_index_sequence_impl<N / 2>::type;\n    using right = typename make_index_sequence_impl<N - N / 2>::type;\n\n    template<class L, class R>\n    struct concat;\n\n    template<size_t... Ls, size_t... Rs>\n    struct concat<index_sequence<Ls...>, index_sequence<Rs...>> {\n        using type = index_sequence<Ls..., (Rs + sizeof...(Ls))...>;\n    };\n\npublic:\n    using type = typename concat<left, right>::type;\n};\n\ntemplate<>\nstruct make_index_sequence_impl<0> {\n    using type = index_sequence<>;\n};\n\ntemplate<>\nstruct make_index_sequence_impl<1> {\n    using type = index_sequence<0>;\n};\n\ntemplate<size_t N>\nusing make_index_sequence = typename make_index_sequence_impl<N>::type;\n\n#else // ^^^ has cxx 11 vvv cxx > 11\n\ntemplate<size_t... N>\nusing index_sequence = std::index_sequence<N...>;\n\ntemplate<size_t N>\nusing make_index_sequence = std::make_index_sequence<N>;\n\n#endif // LZ_HAS_CXX_11\n\n} // namespace detail\n} // namespace lz\n\n#endif // LZ_DETAIL_TRAITS_INDEX_SEQUENCE_HPP\n"
  },
  {
    "path": "include/Lz/detail/traits/is_adaptor.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_IS_ADAPTOR_HPP\n#define LZ_DETAIL_TRAITS_IS_ADAPTOR_HPP\n\n#include <Lz/detail/traits/void.hpp>\n#include <type_traits>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T, class = void>\nstruct is_adaptor : std::false_type {};\n\ntemplate<class T>\nstruct is_adaptor<T, void_t<typename T::adaptor>> : std::true_type {};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/is_invocable.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_IS_INVOCABLE_HPP\n#define LZ_DETAIL_TRAITS_IS_INVOCABLE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <type_traits>\n\n#if !defined(LZ_HAS_CXX_17)\n\n#include <Lz/detail/traits/conditional.hpp>\n#include <Lz/detail/traits/void.hpp>\n#include <utility>\n\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#if defined(LZ_HAS_CXX_17)\n\ntemplate<class Function, class... Args>\nusing is_invocable = std::is_invocable<Function, Args...>;\n\n#else\n\ntemplate<class Function, class = void>\nstruct is_invocable_impl_no_args : std::false_type {};\n\ntemplate<class Function>\nstruct is_invocable_impl_no_args<Function, void_t<decltype(std::declval<Function>()())>> : std::true_type {};\n\ntemplate<class, class Function, class...>\nstruct is_invocable_impl_n_args : std::false_type {};\n\ntemplate<class Function, class... Args>\nstruct is_invocable_impl_n_args<void_t<decltype(std::declval<Function>()(std::declval<Args>()...))>, Function, Args...>\n    : std::true_type {};\n\ntemplate<class F, class... Args>\nusing is_invocable =\n    conditional_t<sizeof...(Args) == 0, is_invocable_impl_no_args<F>, is_invocable_impl_n_args<void, F, Args...>>;\n\n#endif // LZ_HAS_CXX_17\n\n} // namespace detail\n} // namespace lz\n#endif // LZ_DETAIL_TRAITS_IS_INVOCABLE_HPP\n"
  },
  {
    "path": "include/Lz/detail/traits/is_iterable.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_IS_ITERABLE_HPP\n#define LZ_DETAIL_TRAITS_IS_ITERABLE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/void.hpp>\n#include <Lz/traits/iter_type.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class, class = void>\nstruct is_iterable : std::false_type {};\n\ntemplate<class T>\nstruct is_iterable<T, void_t<iter_t<T>, sentinel_t<T>>> : std::true_type {};\n\ntemplate<class T, size_t N>\nstruct is_iterable<T[N]> : std::true_type {};\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class T, class = void>\nLZ_INLINE_VAR constexpr bool is_iterable_v = false;\n\ntemplate<class T>\nLZ_INLINE_VAR constexpr bool is_iterable_v<T, void_t<iter_t<T>, sentinel_t<T>>> = true;\n\ntemplate<class T, size_t N>\nLZ_INLINE_VAR constexpr bool is_iterable_v<T[N]> = true;\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/is_reference_wrapper.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_IS_REFERENCE_WRAPPER_HPP\n#define LZ_DETAIL_TRAITS_IS_REFERENCE_WRAPPER_HPP\n\n#include <functional>\n#include <type_traits>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nstruct is_reference_wrapper : std::false_type {};\n\ntemplate<class T>\nstruct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/is_sentinel.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_IS_SENTINEL_HPP\n#define LZ_DETAIL_TRAITS_IS_SENTINEL_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n#include <type_traits>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class I, class S>\nusing is_sentinel = std::integral_constant<bool, !std::is_same<I, S>::value>;\n\ntemplate<class I>\nusing has_sentinel = is_sentinel<decltype(detail::begin(std::declval<I&>())), decltype(detail::end(std::declval<I&>()))>;\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class I, class S>\nLZ_INLINE_VAR constexpr bool is_sentinel_v = is_sentinel<I, S>::value;\n\ntemplate<class I>\nLZ_INLINE_VAR constexpr bool has_sentinel_v = has_sentinel<I>::value;\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/is_sized.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_IS_SIZED_HPP\n#define LZ_DETAIL_TRAITS_IS_SIZED_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/void.hpp>\n#include <Lz/procs/size.hpp>\n\nnamespace lz {\nnamespace detail {\n\nLZ_MODULE_EXPORT template<class T, class = void>\nstruct is_sized : std::false_type {};\n\nLZ_MODULE_EXPORT template<class T>\nstruct is_sized<T, void_t<decltype(lz::size(std::declval<T>()))>> : std::true_type {};\n\n#ifdef LZ_HAS_CXX_17\n\nLZ_MODULE_EXPORT template<class T, class = void>\nLZ_INLINE_VAR constexpr bool is_sized_v = false;\n\nLZ_MODULE_EXPORT template<class T>\nLZ_INLINE_VAR constexpr bool is_sized_v<T, void_t<decltype(lz::size(std::declval<T>()))>> = true;\n\n#endif // LZ_HAS_CXX_17\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/iterator_categories.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_ITERATOR_CATEGORIES_HPP\n#define LZ_DETAIL_TRAITS_ITERATOR_CATEGORIES_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n#include <Lz/traits/iter_type.hpp>\n#include <iterator>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class IterTag>\nusing is_ra_tag = std::is_convertible<IterTag, std::random_access_iterator_tag>;\n\ntemplate<class IterTag>\nusing is_bidi_tag = std::is_convertible<IterTag, std::bidirectional_iterator_tag>;\n\ntemplate<class IterTag>\nusing is_fwd_tag = std::is_convertible<IterTag, std::forward_iterator_tag>;\n\ntemplate<class I>\nusing is_ra = std::is_convertible<typename std::iterator_traits<I>::iterator_category, std::random_access_iterator_tag>;\n\ntemplate<class I>\nusing is_bidi = std::is_convertible<typename std::iterator_traits<I>::iterator_category, std::bidirectional_iterator_tag>;\n\ntemplate<class I>\nusing is_fwd = std::is_convertible<typename std::iterator_traits<I>::iterator_category, std::forward_iterator_tag>;\n\ntemplate<class Cat, class Cat2>\nusing strongest_cat_t = typename std::common_type<Cat, Cat2>::type;\n\ntemplate<class Cat>\nusing bidi_strongest_cat = strongest_cat_t<Cat, std::bidirectional_iterator_tag>;\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class I>\nconstexpr bool is_ra_v = is_ra<I>::value;\n\ntemplate<class I>\nconstexpr bool is_bidi_v = is_bidi<I>::value;\n\ntemplate<class I>\nconstexpr bool is_fwd_v = is_fwd<I>::value;\n\ntemplate<class Tag>\nconstexpr bool is_ra_tag_v = is_ra_tag<Tag>::value;\n\ntemplate<class Tag>\nconstexpr bool is_bidi_tag_v = is_bidi_tag<Tag>::value;\n\ntemplate<class Tag>\nconstexpr bool is_fwd_tag_v = is_fwd_tag<Tag>::value;\n\n#endif\n\ntemplate<class Iterator>\nusing iter_cat_t = typename std::iterator_traits<Iterator>::iterator_category;\n\ntemplate<class Iterable>\nusing iter_cat_iterable_t = typename std::iterator_traits<iter_t<Iterable>>::iterator_category;\n\ntemplate<class Iterable>\nusing is_sentinel_assignable = std::is_assignable<iter_t<Iterable>, sentinel_t<Iterable>>;\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/remove_ref.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_REMOVE_CVREF_HPP\n#define LZ_DETAIL_TRAITS_REMOVE_CVREF_HPP\n\n#include <type_traits>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nstruct remove_rvalue_reference {\n    using type = T;\n};\n\ntemplate<class T>\nstruct remove_rvalue_reference<T&&> {\n    using type = T;\n};\n\ntemplate<class T>\nusing remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type;\n\ntemplate<class T>\nusing remove_ref_t = typename std::remove_reference<T>::type;\n\ntemplate<class T>\nusing remove_cref_t = typename std::remove_const<remove_ref_t<T>>::type;\n\n#ifdef LZ_HAS_CXX_20\n\ntemplate<class T>\nusing remove_cvref_t = std::remove_cvref_t<T>;\n\n#else // ^^^ has cxx 20 vvv cxx < 20\n\ntemplate<class T>\nusing remove_cvref_t = typename std::remove_cv<remove_ref_t<T>>::type;\n\n#endif // LZ_HAS_CXX_20\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/std_algo_compat.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_STD_ALGO_COMPAT_HPP\n#define LZ_DETAIL_TRAITS_STD_ALGO_COMPAT_HPP\n\n#include <Lz/detail/traits/is_sentinel.hpp>\n#include <type_traits>\n#include <algorithm>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class I, class S>\nusing std_algo_compat = std::integral_constant<bool, !detail::is_sentinel<I, S>::value || is_ra<I>::value>;\n\ntemplate<class I1, class S1, class I2, class S2>\nusing std_algo_compat2 = std::integral_constant<bool, std_algo_compat<I1, S1>::value && std_algo_compat<I2, S2>::value>;\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nconstexpr bool std_equal_helper(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n    return std::equal(begin, end, begin2, end2, std::move(binary_predicate));\n}\n\ntemplate<class I, class S>\ninline constexpr bool std_algo_compat_v = std_algo_compat<I, S>::value;\n\ntemplate<class I1, class S1, class I2, class S2>\ninline constexpr bool std_algo_compat2_v = std_algo_compat2<I1, S1, I2, S2>::value;\n\n#else\n\ntemplate<class Iterator1, class S1, class Iterator2, class S2, class BinaryPredicate>\nLZ_CONSTEXPR_CXX_14 bool std_equal_helper(Iterator1 begin, S1 end, Iterator2 begin2, S2 end2, BinaryPredicate binary_predicate) {\n#ifdef LZ_HAS_CXX_14\n    return std::equal(begin, end, begin2, end2, std::move(binary_predicate));\n#else\n    static_cast<void>(end2);\n    return std::equal(begin, end, begin2, std::move(binary_predicate));\n#endif\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/strict_iterator_traits.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_STRICT_ITERATOR_TRAITS_HPP\n#define LZ_DETAIL_TRAITS_STRICT_ITERATOR_TRAITS_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/traits/iter_type.hpp>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T>\nstruct strict_iterator_traits {\n    using value_type = typename T::value_type;\n    using reference = typename T::reference;\n    using pointer = typename T::pointer;\n    using difference_type = typename T::difference_type;\n};\n\ntemplate<class T>\nstruct strict_iterator_traits<T*> {\n    using value_type = typename std::remove_cv<T>::type;\n    using reference = T&;\n    using pointer = T*;\n    using difference_type = std::ptrdiff_t;\n};\n\n\ntemplate<class Iterator>\nusing val_t = typename strict_iterator_traits<Iterator>::value_type;\n\ntemplate<class Iterator>\nusing ref_t = typename strict_iterator_traits<Iterator>::reference;\n\ntemplate<class Iterator>\nusing ptr_t = typename strict_iterator_traits<Iterator>::pointer;\n\ntemplate<class Iterator>\nusing diff_type = typename strict_iterator_traits<Iterator>::difference_type;\n\ntemplate<class Iterable>\nusing val_iterable_t = typename strict_iterator_traits<iter_t<Iterable>>::value_type;\n\ntemplate<class Iterable>\nusing diff_iterable_t = typename strict_iterator_traits<iter_t<Iterable>>::difference_type;\n\ntemplate<class Iterable>\nusing ref_iterable_t = typename strict_iterator_traits<iter_t<Iterable>>::reference;\n\ntemplate<class Iterable>\nusing ptr_iterable_t = typename strict_iterator_traits<iter_t<Iterable>>::pointer;\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/traits/void.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_VOID_T_HPP\n#define LZ_DETAIL_TRAITS_VOID_T_HPP\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class...>\nusing void_t = void;\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/tuple_helpers.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TUPLE_HELPERS_HPP\n#define LZ_TUPLE_HELPERS_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/detail/procs/min_max.hpp>\n#include <Lz/detail/traits/conjunction.hpp>\n#include <Lz/detail/traits/index_sequence.hpp>\n#include <Lz/detail/traits/is_reference_wrapper.hpp>\n#include <Lz/procs/eager_size.hpp>\n#include <tuple>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Tuple>\nstruct first_it;\n\ntemplate<class Iterator, class... Iterators>\nstruct first_it<std::tuple<Iterator, Iterators...>> {\n    using type = Iterator;\n};\n\ntemplate<class Iterators>\nusing first_it_t = typename first_it<Iterators>::type;\n\ntemplate<class Tuple>\nstruct tuple_size : std::tuple_size<Tuple> {};\n\n// To prevent rvalue references from happening in a tuple, use canonicalize_reference\ntemplate<class T, size_t... Is>\nconstexpr std::tuple<remove_rvalue_reference_t<decltype((void)Is, std::declval<T>())>...> tuple_of(index_sequence<Is...>) {\n    return {};\n}\n\ntemplate<class>\nstruct iter_tuple_diff_type;\n\ntemplate<class... Iterators>\nstruct iter_tuple_diff_type<std::tuple<Iterators...>> {\n    using type = typename std::common_type<diff_type<Iterators>...>::type;\n};\n\ntemplate<class IterTuple>\nusing iter_tuple_diff_type_t = typename iter_tuple_diff_type<IterTuple>::type;\n\ntemplate<class>\nstruct iter_tuple_iter_cat;\n\ntemplate<class... Iterators>\nstruct iter_tuple_iter_cat<std::tuple<Iterators...>> {\n    using type = typename std::common_type<iter_cat_t<Iterators>...>::type;\n};\n\ntemplate<class IterTuple>\nusing iter_tuple_iter_cat_t = typename iter_tuple_iter_cat<IterTuple>::type;\n\ntemplate<class>\nstruct iter_tuple_value_type;\n\ntemplate<class... Iterators>\nstruct iter_tuple_value_type<std::tuple<Iterators...>> {\n    using type = std::tuple<val_t<Iterators>...>;\n};\n\ntemplate<class IterTuple>\nusing iter_tuple_value_type_t = typename iter_tuple_value_type<IterTuple>::type;\n\ntemplate<class>\nstruct iter_tuple_ref_type;\n\ntemplate<class... Iterators>\nstruct iter_tuple_ref_type<std::tuple<Iterators...>> {\n    using type = std::tuple<ref_t<Iterators>...>;\n};\n\ntemplate<class IterTuple>\nusing iter_tuple_ref_type_t = typename iter_tuple_ref_type<IterTuple>::type;\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class... Ts>\nstruct disjunction : std::bool_constant<(Ts::value || ...)> {};\n\n#else\n\ntemplate<class... Ts>\nstruct disjunction : std::false_type {};\n\ntemplate<class T, class... Ts>\nstruct disjunction<T, Ts...> : conditional_t<T::value, std::true_type, disjunction<Ts...>> {};\n\n#endif\n\ntemplate<class T, class U>\nstruct copy_cv {\n    using const_applied = conditional_t<std::is_const<T>::value, const U, U>;\n    using type = conditional_t<std::is_volatile<T>::value, volatile const_applied, const_applied>;\n};\n\ntemplate<class T, class U>\nusing copy_cv_t = typename copy_cv<T, U>::type;\n\ntemplate<class T, class U>\nstruct common_reference2 {\n    using ref_remove_t = typename std::remove_reference<T>::type;\n    using ref_remove_u = typename std::remove_reference<U>::type;\n    using pure_t = typename std::remove_cv<ref_remove_t>::type;\n    using pure_u = typename std::remove_cv<ref_remove_u>::type;\n\n    using type = conditional_t<std::is_lvalue_reference<T>::value && std::is_lvalue_reference<U>::value &&\n                                   std::is_same<pure_t, pure_u>::value,\n                               copy_cv_t<ref_remove_t, ref_remove_u>&, typename std::common_type<pure_t, pure_u>::type>;\n};\n\ntemplate<class T, class U>\nstruct common_reference2<T&, std::reference_wrapper<U>> {\n    using type = std::reference_wrapper<copy_cv_t<T, U>>;\n};\n\ntemplate<class T, class U>\nstruct common_reference2<std::reference_wrapper<T>, U&> {\n    using type = std::reference_wrapper<copy_cv_t<T, U>>;\n};\n\ntemplate<class T, class U>\nusing common_reference2_t = typename common_reference2<T, U>::type;\n\ntemplate<class... Ts>\nstruct common_reference;\n\ntemplate<class T>\nstruct common_reference<T> {\n    using type = T;\n};\n\ntemplate<class T1, class T2, class... Ts>\nstruct common_reference<T1, T2, Ts...> {\n    template<class T>\n    using clean_reference_wrapper = conditional_t<is_reference_wrapper<remove_cvref_t<T>>::value, remove_cvref_t<T>, T>;\n\n    using type = typename common_reference<common_reference2_t<clean_reference_wrapper<T1>, clean_reference_wrapper<T2>>,\n                                           clean_reference_wrapper<Ts>...>::type;\n};\n\ntemplate<class... Ts>\nusing common_reference_t = typename common_reference<Ts...>::type;\n\ntemplate<class>\nstruct iter_tuple_common_ref;\n\ntemplate<class... Iterators>\nstruct iter_tuple_common_ref<std::tuple<Iterators...>> {\n    using type = common_reference_t<ref_t<Iterators>...>;\n};\n\ntemplate<class IterTuple>\nusing iter_tuple_common_ref_t = typename iter_tuple_common_ref<IterTuple>::type;\n\ntemplate<class Iterable>\nLZ_CONSTEXPR_CXX_17 iter_t<Iterable>\nincrement_or_decrement_iterator(Iterable&& iterable, const size_t this_size, const size_t min) {\n    using diff_type = diff_iterable_t<Iterable>;\n    // Check whether we can better use decrement or increment. For random access iterators, we can\n    // use either, but for bidirectional iterators, we need to be \"careful\" about the direction.\n    // We want to use the iterator that requires the least amount of increments/decrements.\n    const auto to_decrement = static_cast<diff_type>(this_size - min);\n    const auto to_increment = static_cast<diff_type>(min);\n    if (to_increment > to_decrement) {\n        return std::prev(detail::end(iterable), to_decrement);\n    }\n    return std::next(detail::begin(iterable), to_increment);\n}\n\ntemplate<size_t... Is, class Iterable>\nLZ_CONSTEXPR_CXX_17 auto smallest_end_tuple(Iterable&& iterable_tuple, index_sequence<Is...>)\n    -> std::tuple<decltype(detail::begin(std::get<Is>(iterable_tuple)))...> {\n    const size_t sizes[] = { static_cast<size_t>(lz::eager_size(std::get<Is>(iterable_tuple)))... };\n    const auto min = min_variadic(sizes[Is]...);\n\n    return { increment_or_decrement_iterator(std::get<Is>(std::forward<Iterable>(iterable_tuple)), sizes[Is], min)... };\n}\n\ntemplate<class T, class IterableTuple, size_t... Is>\nLZ_CONSTEXPR_CXX_14 std::tuple<decltype(Is, T{})...> iterables_size_tuple(IterableTuple&& iterables, index_sequence<Is...>) {\n    return { static_cast<T>(lz::eager_size(std::get<Is>(std::forward<IterableTuple>(iterables))))... };\n}\n\ntemplate<class IterableTuple, size_t... I>\nLZ_CONSTEXPR_CXX_14 auto begin_tuple_impl(IterableTuple&& iterable_tuple, index_sequence<I...>)\n    -> std::tuple<decltype(detail::begin(std::get<I>(iterable_tuple)))...> {\n    return { detail::begin(std::get<I>(iterable_tuple))... };\n}\n\ntemplate<class IterableTuple, size_t... I>\nLZ_CONSTEXPR_CXX_14 auto end_tuple_impl(IterableTuple&& iterable_tuple, index_sequence<I...>)\n    -> std::tuple<decltype(detail::end(std::get<I>(iterable_tuple)))...> {\n    return { detail::end(std::get<I>(iterable_tuple))... };\n}\n\ntemplate<class IterableTuple>\nLZ_CONSTEXPR_CXX_14 auto begin_tuple(IterableTuple&& iterable_tuple)\n    -> decltype(begin_tuple_impl(std::forward<IterableTuple>(iterable_tuple),\n                                 make_index_sequence<tuple_size<remove_cvref_t<IterableTuple>>::value>{})) {\n    return begin_tuple_impl(std::forward<IterableTuple>(iterable_tuple),\n                            make_index_sequence<tuple_size<remove_cvref_t<IterableTuple>>::value>{});\n}\n\ntemplate<class IterableTuple>\nLZ_CONSTEXPR_CXX_14 auto end_tuple(IterableTuple&& iterable_tuple)\n    -> decltype(end_tuple_impl(std::forward<IterableTuple>(iterable_tuple),\n                               make_index_sequence<tuple_size<remove_cvref_t<IterableTuple>>::value>{})) {\n    return end_tuple_impl(std::forward<IterableTuple>(iterable_tuple),\n                          make_index_sequence<tuple_size<remove_cvref_t<IterableTuple>>::value>{});\n}\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/unique_ptr.hpp",
    "content": "#pragma once\n\n#ifndef LZ_UNIQUE_PTR_HPP\n#define LZ_UNIQUE_PTR_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\n#ifdef LZ_HAS_CXX_14\n\n#include <memory>\n\n#endif\n\n#ifdef LZ_HAS_CXX_11\n\n#include <utility>\n#include <Lz/detail/procs/assert.hpp>\n\n#endif\n\nnamespace lz {\nnamespace detail {\n\n#ifdef LZ_HAS_CXX_14\n\nusing std::make_unique;\nusing std::unique_ptr;\n\n#else\n\ntemplate<class T>\nclass unique_ptr {\n    T* _ptr{ nullptr };\n\n    template<class>\n    friend class unique_ptr;\n\n    void check_pointer_compat() const noexcept {\n        LZ_ASSERT(_ptr != nullptr, \"Cannot dereference nullptr\");\n    }\n\npublic:\n    constexpr unique_ptr() noexcept = default;\n\n    constexpr unique_ptr(T* ptr) noexcept : _ptr{ ptr } {\n    }\n\n    constexpr unique_ptr(const unique_ptr&) = delete;\n    constexpr unique_ptr& operator=(const unique_ptr&) const = delete;\n\n    template<class U>\n    unique_ptr(unique_ptr<U>&& other) noexcept {\n        _ptr = other.release();\n        other._ptr = nullptr;\n    }\n\n    template<class U>\n    unique_ptr& operator=(unique_ptr<U>&& other) noexcept {\n        if (this != &other) {\n            reset(other.release());\n        }\n        return *this;\n    }\n\n    ~unique_ptr() {\n        if (_ptr) {\n            delete _ptr;\n            _ptr = nullptr;\n        }\n    }\n\n    constexpr const T* get() const noexcept {\n        return _ptr;\n    }\n\n    T* release() noexcept {\n        T* ptr = _ptr;\n        _ptr = nullptr;\n        return ptr;\n    }\n\n    void reset(T* ptr = nullptr) noexcept {\n        if (_ptr == ptr) {\n            return;\n        }\n        delete _ptr;\n        _ptr = ptr;\n    }\n\n    T& operator*() noexcept {\n        check_pointer_compat();\n        return *_ptr;\n    }\n\n    const T& operator*() const noexcept {\n        check_pointer_compat();\n        return *_ptr;\n    }\n\n    T* operator->() noexcept {\n        check_pointer_compat();\n        return _ptr;\n    }\n\n    const T* operator->() const noexcept {\n        check_pointer_compat();\n        return _ptr;\n    }\n\n    constexpr explicit operator bool() const noexcept {\n        return _ptr != nullptr;\n    }\n};\n\ntemplate<class T, class... Args>\nunique_ptr<T> make_unique(Args&&... args) {\n    return unique_ptr<T>(new T(std::forward<Args>(args)...));\n}\n\n#endif\n\n} // namespace detail\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/detail/variant.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_VARIANT_HPP\n#define LZ_DETAIL_VARIANT_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\n#ifdef LZ_HAS_CXX_17\n\n#include <variant>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T, class T2>\nusing variant = std::variant<T, T2>;\n\n} // namespace detail\n} // namespace lz\n\n#else\n\n#include <cstdint>\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class T, class T2>\nclass variant {\n    static_assert(!std::is_same<T, T2>::value, \"T and T2 must be different types\");\n\n    enum class state : std::uint_least8_t {\n        none = static_cast<std::uint_least8_t>(-1),\n        t = 0,\n        t2 = 1,\n    } _state{ state::none };\n\n    union types {\n        T _t;\n        T2 _t2;\n\n        types() {\n        }\n\n        ~types() {\n        }\n    } _variant{};\n\n    template<class U, class V>\n    void reconstruct(state s, U&& this_variant, V&& other_variant_type) {\n        this->~variant();\n        _state = s;\n        ::new (detail::addressof(this_variant)) typename std::decay<U>::type(std::forward<V>(other_variant_type));\n    }\n\n    template<class V, class V2>\n    void construct(V&& other_variant_type, V2&& other_variant_type2) {\n        switch (_state) {\n        case state::t:\n            ::new (detail::addressof(_variant._t)) T(std::forward<V>(other_variant_type));\n            break;\n        case state::t2:\n            ::new (detail::addressof(_variant._t2)) T2(std::forward<V2>(other_variant_type2));\n            break;\n        default:\n            break;\n        }\n    }\n\npublic:\n    constexpr variant() noexcept = default;\n\n    variant(const T& t) : _state{ state::t } {\n        ::new (detail::addressof(_variant._t)) T(t);\n    }\n\n    variant(const T2& t2) : _state{ state::t } {\n        ::new (detail::addressof(_variant._t2)) T2(t2);\n    }\n\n    variant(T&& t) noexcept(std::is_nothrow_move_constructible<T>::value) : _state{ state::t } {\n        ::new (detail::addressof(_variant._t)) T(std::move(t));\n    }\n\n    variant(T2&& t2) noexcept(std::is_nothrow_move_constructible<T2>::value) : _state{ state::t2 } {\n        ::new (detail::addressof(_variant._t2)) T2(std::move(t2));\n    }\n\n    LZ_CONSTEXPR_CXX_14 variant(const variant& other) : _state{ other._state } {\n        construct(other._variant._t, other._variant._t2);\n    }\n\n    LZ_CONSTEXPR_CXX_14 variant(variant&& other) noexcept(std::is_nothrow_move_constructible<T>::value &&\n                                                          std::is_nothrow_move_constructible<T2>::value) :\n        _state{ other._state } {\n        construct(std::move(other._variant._t), std::move(other._variant._t2));\n    }\n\n    LZ_CONSTEXPR_CXX_14 variant& operator=(const T& t) {\n        reconstruct(state::t, _variant._t, t);\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 variant& operator=(const T2& t2) {\n        reconstruct(state::t2, _variant._t2, t2);\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 variant& operator=(T&& t) noexcept(std::is_nothrow_move_constructible<T>::value) {\n        reconstruct(state::t, _variant._t, std::move(t));\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 variant& operator=(T2&& t2) noexcept(std::is_nothrow_move_constructible<T2>::value) {\n        reconstruct(state::t2, _variant._t2, std::move(t2));\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 variant& operator=(const variant& other) {\n        this->~variant();\n        _state = other._state;\n        construct(other._variant._t, other._variant._t2);\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 variant& operator=(variant&& other) noexcept(std::is_nothrow_move_constructible<T>::value &&\n                                                                     std::is_nothrow_move_constructible<T2>::value) {\n        this->~variant();\n        _state = other._state;\n        construct(std::move(other._variant._t), std::move(other._variant._t2));\n        return *this;\n    }\n\n    template<size_t I, class... Args>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 0> emplace(Args&&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value) {\n        this->~variant();\n        ::new (detail::addressof(_variant._t)) T(std::forward<Args>(args)...);\n        _state = state::t;\n    }\n\n    template<size_t I, class... Args>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 1> emplace(Args&&... args) noexcept(std::is_nothrow_constructible<T2, Args...>::value) {\n        this->~variant();\n        ::new (detail::addressof(_variant._t2)) T2(std::forward<Args>(args)...);\n        _state = state::t2;\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 0, const T&> get() const noexcept {\n        LZ_ASSERT(_state == state::t, \"Invalid variant access\");\n        return _variant._t;\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 const enable_if_t<I == 1, const T2&> get() const noexcept {\n        LZ_ASSERT(_state == state::t2, \"Invalid variant access\");\n        return _variant._t2;\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 0, T&> get() noexcept {\n        LZ_ASSERT(_state == state::t, \"Invalid variant access\");\n        return _variant._t;\n    }\n\n    template<size_t I>\n    LZ_CONSTEXPR_CXX_14 enable_if_t<I == 1, T2&> get() noexcept {\n        LZ_ASSERT(_state == state::t2, \"Invalid variant access\");\n        return _variant._t2;\n    }\n\n    LZ_CONSTEXPR_CXX_14 std::int_least8_t index() const noexcept {\n        return static_cast<std::int_least8_t>(_state);\n    }\n\n    ~variant() noexcept(noexcept(std::is_nothrow_destructible<T>::value) && noexcept(std::is_nothrow_destructible<T2>::value)) {\n        switch (_state) {\n        case state::t:\n            _variant._t.~T();\n            break;\n        case state::t2:\n            _variant._t2.~T2();\n            break;\n        default:\n            break;\n        }\n    }\n};\n\ntemplate<size_t I, class T, class T2>\nLZ_CONSTEXPR_CXX_14 auto get(const variant<T, T2>& v) noexcept -> decltype(v.template get<I>()) {\n    return v.template get<I>();\n}\n\ntemplate<size_t I, class T, class T2>\nLZ_CONSTEXPR_CXX_14 auto get(variant<T, T2>& v) noexcept -> decltype(v.template get<I>()) {\n    return v.template get<I>();\n}\n\n} // namespace detail\n} // namespace lz\n\n#endif // !defined(__cpp_lib_variant) && !defined(LZ_HAS_CXX_17)\n\n#endif // LZ_DETAIL_VARIANT_HPP\n"
  },
  {
    "path": "include/Lz/drop.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DROP_HPP\n#define LZ_DROP_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/drop.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief This adaptor is used to drop the first n elements of an iterable. The iterator category is the same as the input\n * iterator category. Its end() function will return the same type as its input iterable. If its input iterable has a\n * .size() method, then this iterable will also have a .size() method. If the amount to drop is larger than the size, size will be\n * 0. Example:\n * ```cpp\n * auto vec = std::vector<int>{1, 2, 3, 4, 5};\n * auto res = lz::drop(vec, 2); // res = {3, 4, 5}\n * // or\n * auto res = vec | lz::drop(2); // res = {3, 4, 5}\n * auto res = lz::drop(vec, 50); // res = {}, size = 0\n * ```\n */\nLZ_INLINE_VAR constexpr detail::drop_adaptor drop{};\n\n/**\n * @brief This is a type alias for the `drop` iterable.\n * @tparam Iterable The iterable to drop elements from.\n * ```cpp\n * auto vec = std::vector<int>{1, 2, 3, 4, 5};\n * lz::drop_iterable<std::vector<int>> res = lz::drop(vec, 2);\n * ```\n */\ntemplate<class Iterable>\nusing drop_iterable = detail::drop_iterable<Iterable>;\n\n} // namespace lz\n\n#endif // LZ_DROP_HPP\n"
  },
  {
    "path": "include/Lz/drop_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DROP_WHILE_HPP\n#define LZ_DROP_WHILE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/drop_while.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief This adaptor is used to make an iterable where the iterator keeps dropping elements as long as the predicate returns\n * `true`. Once it has returned `false`, it will no longer do such thing. The iterator category is the same as its input iterable.\n * Its end() function will return a sentinel if its input iterable is forward or less or has a sentinel. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto dropped = lz::drop_while(vec, [](int i) { return i < 3; }); // dropped = { 3, 4, 5 }\n * // or\n * auto dropped = vec | lz::drop_while([](int i) { return i < 3; }); // dropped = { 3, 4, 5 }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::drop_while_adaptor drop_while{};\n\n/**\n * @brief This is a type alias for the `drop_while` iterable.\n * @tparam Iterable The iterable to drop elements from.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto f = [](int i) { return i < 3; };\n * lz::drop_while_iterable<std::vector<int>, std::function<bool(int)> dropped = lz::drop_while(vec, f);\n */\ntemplate<class Iterable>\nusing drop_while_iterable = detail::drop_while_iterable<Iterable>;\n\n} // namespace lz\n\n#endif // LZ_DROP_WHILE_HPP\n"
  },
  {
    "path": "include/Lz/duplicates.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DUPLICATES_HPP\n#define LZ_DUPLICATES_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/duplicates.hpp>\n\nnamespace lz {\n\n/**\n * @brief This iterable returns a pair of each element in the iterable and the number of times it appears in the iterable.\n * `pair::first` is the element and `pair::second` is the count. The count is `size_t` and starts from 1. The input iterable\n * needs to be sorted first in order to count the duplicates. The iterator category is min(bidirectional, <input iterable\n * category>). Its end() function will return a sentinel if the input iterable is forward or has a sentinel. This iterable does\n * not contain a .size() method. Example:\n * ```cpp\n * std::vector<int> input{ 1, 2, 2, 3, 4, 4, 5 };\n * auto dupes = input | lz::duplicates; // { { 1, 1 }, { 2, 2 }, { 3, 1 }, { 4, 2 }, { 5, 1 } }\n * auto dupes = lz::duplicates(input); // same as above\n * ```\n */\nLZ_INLINE_VAR constexpr detail::duplicates_adaptor duplicates{};\n\n/**\n * @brief This is a type alias for the `duplicates` iterable.\n * @tparam Iterable The iterable to check the duplicates of.\n * @tparam BinaryPredicate The binary predicate to compare the elements of the iterable. Defaults to `std::less`.\n * ```cpp\n * std::forward_list<int> list = {1, 2, 3, 4, 5};\n * lz::duplicates_iterable<std::forward_list<int>> res = lz::duplicates(list);\n * ```\n */\ntemplate<class Iterable, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable>)>\nusing duplicates_iterable = detail::duplicates_iterable<Iterable, BinaryPredicate>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/enumerate.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_ENUMERATE_HPP\r\n#define LZ_ENUMERATE_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/enumerate.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Returns an iterable that enumerates the elements of the input iterable, meaning it returns a std::pair<IntType,\r\n * ValueType>, where std::pair::first_type is an integer (corresponding to the current index) and std::pair::second_type is\r\n * the value of the input iterable (by reference). The index starts at 0 by default, but can be changed by passing a start\r\n * value to the function. It will return a `default_sentinel_t` if the input iterable is forward or has a sentinel. The iterator\r\n * category is the same as its input iterable.\r\n * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is traversed\r\n * to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you can use\r\n * `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the iterable.\r\n * `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its entire size is\r\n * therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will traverse the iterable\r\n * once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but will return the cached\r\n * size instead. The following iterables require a(n) (eagerly)sized iterable:\r\n * - `lz::chunks`\r\n * - `lz::enumerate`\r\n * - `lz::exclude`\r\n * - `lz::interleave`\r\n * - `lz::rotate`\r\n * - `lz::take`\r\n * - `lz::take_every`\r\n * - `lz::zip_longest`\r\n * - `lz::zip`\r\n *\r\n * Example:\r\n * ```cpp\r\n * std::forward_list<int> list = {1, 2, 3, 4, 5};\r\n * auto enumerated = lz::enumerate(list); // enumerated = { {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5} }\r\n * auto enumerated = lz::enumerate(list, 5); // enumerated = { {5, 1}, {6, 2}, {7, 3}, {8, 4}, {9, 5} }\r\n *\r\n * // or\r\n *\r\n * auto enumerated = list | lz::enumerate; // enumerated = { {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5} }\r\n * auto enumerated = list | lz::enumerate(5); // enumerated = { {5, 1}, {6, 2}, {7, 3}, {8, 4}, {9, 5} }\r\n * ```\r\n * Use lz::cache_size if:\r\n * - Your iterable is exactly bidirectional (so forward/random access excluded) and\r\n * - Your iterable is not sized and\r\n * - Your iterable is not sentinelled\r\n * - You either use multiple/a combination of the following iterables OR (see last point):\r\n * - `lz::chunks`\r\n * - `lz::enumerate`\r\n * - `lz::exclude`\r\n * - `lz::interleave`\r\n * - `lz::rotate`\r\n * - `lz::take`\r\n * - `lz::take_every`\r\n * - `lz::zip_longest`\r\n * - `lz::zip`\r\n * - Are planning call end() multiple times on the same instance (with one or more of the above iterable\r\n * combinations)\r\n */\r\nLZ_INLINE_VAR constexpr detail::enumerate_adaptor enumerate{};\r\n\r\n/**\r\n * @brief This is a type alias for the `enumerate` iterable.\r\n * @tparam Iterable The iterable to enumerate.\r\n * @tparam IntType The type of the index. Defaults to int.\r\n * ```cpp\r\n * std::forward_list<int> list = {1, 2, 3, 4, 5};\r\n * lz::enumerate_iterable<std::forward_list<int>> res = lz::enumerate(list);\r\n * ```\r\n */\r\ntemplate<class Iterable, class IntType = int>\r\nusing enumerate_iterable = detail::enumerate_iterable<Iterable, IntType>;\r\n\r\n} // namespace lz\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/Lz/except.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_EXCEPT_HPP\r\n#define LZ_EXCEPT_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/except.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Excepts an iterable with another iterable. This means that it returns every item that is not in the second iterable\r\n * argument. Can be used with a custom comparer. Does not contain a .size() method, stores the (eager) size of the second iterable\r\n * internally using lz::cache_size, and its category is min(bidirectional, <category first iterable>). Example:\r\n * ```cpp\r\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\r\n * std::vector<int> to_except = { 5, 3 };\r\n * std::sort(to_except.begin(), to_except.end()); // to_except is now { 3, 5 }\r\n * auto excepted = lz::except(vec, to_except); // excepted = { 1, 2, 4 }\r\n * // or\r\n * auto excepted = lz::except(vec, to_except, std::less<int>{}); // excepted = { 1, 2, 4 }\r\n * // or\r\n * auto excepted = vec | lz::except(to_except); // excepted = { 1, 2, 4 }\r\n * // or\r\n * auto excepted = vec | lz::except(to_except, std::less<int>{}); // excepted = { 1, 2, 4 }\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::except_adaptor except{};\r\n\r\n/**\r\n * @brief A type alias for the except iterable.\r\n * @tparam Iterable1 The first iterable type.\r\n * @tparam Iterable2 The second iterable type.\r\n * @tparam BinaryPredicate The binary predicate type used for comparison. Defaults to `std::less`.\r\n * ```cpp\r\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\r\n * std::vector<int> to_except = { 5, 3 };\r\n * std::sort(to_except.begin(), to_except.end());\r\n * using except_t = lz::except_iterable<std::vector<int>, std::vector<int>, std::less<int>>;\r\n * except_t excepted = lz::except(vec, to_except, std::less<int>{});\r\n * ```\r\n */\r\ntemplate<class Iterable1, class Iterable2, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable1>)>\r\nusing except_iterable = detail::except_iterable<Iterable1, Iterable2, BinaryPredicate>;\r\n\r\n} // end namespace lz\r\n\r\n#endif // end LZ_EXCEPT_HPP\r\n"
  },
  {
    "path": "include/Lz/exclude.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCLUDE_HPP\n#define LZ_EXCLUDE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/exclude.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Excludes elements from a container, using two indexes. The first index is means the start index, the second index means\n * the end index. Contains a .size() method if the input iterable contains a .size() method, its iterator category is the same as\n * the input iterable.\n * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is traversed\n * to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you can use\n * `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the iterable.\n * `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its entire size is\n * therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will traverse the iterable\n * once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but will return the cached\n * size instead. The following iterables require a(n) (eagerly)sized iterable:\n * - `lz::chunks`\n * - `lz::enumerate`\n * - `lz::exclude`\n * - `lz::interleave`\n * - `lz::rotate`\n * - `lz::take`\n * - `lz::take_every`\n * - `lz::zip_longest`\n * - `lz::zip`\n *\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n * // Exclude index [3, 6)\n * auto excluded = lz::exclude(vec, 3, 6); // excluded = { 1, 2, 3, 6, 7, 8, 9 }\n * // or\n * auto excluded = vec | lz::exclude(3, 6); // excluded = { 1, 2, 3, 6, 7, 8, 9 }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::exclude_adaptor exclude{};\n\n/**\n * @brief Helper alias for the exclude iterable.\n * @tparam Iterable The type of the iterable to exclude elements from.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n * lz::exclude_iterable<std::vector<int>> excluded = lz::exclude(vec, 3, 6);\n * ```\n */\ntemplate<class Iterable>\nusing exclude_iterable = detail::exclude_iterable<Iterable>;\n\n} // namespace lz\n\n#endif // LZ_SKIP_HPP\n"
  },
  {
    "path": "include/Lz/exclusive_scan.hpp",
    "content": "#pragma once\n\n#ifndef LZ_EXCLUSIVE_SCAN_HPP\n#define LZ_EXCLUSIVE_SCAN_HPP\n\n#include <Lz/detail/adaptors/exclusive_scan.hpp>\n#include <Lz/procs/chain.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Performs an exclusive scan on a container. The first element will be the init value, the second element will be the\n * first element of the container + the init value, the third element will be the second element + last returned element\n * previously, etc. It contains a .size() method if the input iterable also has a .size() method. Its end() function returns a\n * sentinel rather than an iterator. Its iterator category is forward. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * // std::plus is used as the default binary operation\n *\n * // you can also add a custom operator:\n * auto scan = vec | lz::exclusive_scan(0, std::plus<int>{}); // scan = { 0, 1, 3, 6, 10, 15 }\n * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions,\n * you\n * // can omit the init value, in which case it will be a default constructed object of the type of the container.\n * // Example\n * auto scan = lz::exclusive_scan(vec);\n * auto scan = lz::exclusive_scan(vec, 0);\n * auto scan = vec | lz::exclusive_scan; // uses 0 and std::plus\n * auto scan = vec | lz::exclusive_scan(0);\n * ```\n */\nLZ_INLINE_VAR constexpr detail::exclusive_scan_adaptor exclusive_scan{};\n\n/**\n * @brief A type alias for the exclusive scan iterable.\n * @tparam Iterable The iterable type.\n * @tparam T The type of the elements in the iterable. Defaults to the value type of the iterable.\n * @tparam BinaryOp The binary operation used for the scan. Defaults to `std::plus`.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * lz::exclusive_scan<std::vector<int>> scan = lz::exclusive_scan_iterable(vec, 0);\n * ```\n */\ntemplate<class Iterable, class T = detail::val_iterable_t<Iterable>,\n         class BinaryOp = LZ_BIN_OP(plus, detail::val_iterable_t<Iterable>)>\nusing exclusive_scan_iterable = detail::exclusive_scan_iterable<Iterable, T, BinaryOp>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/filter.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_FILTER_HPP\r\n#define LZ_FILTER_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/filter.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Drops elements based on a condition predicate. If it returns `false`, the element in question is dropped. If it returns\r\n * `true` then this item is will be yielded. If the input iterable is forward an end() sentinel is returned, otherwise the end()\r\n * iterator is the same as the begin() iterator. It is bidirectional if the input iterable is also at least bidirectional. It does\r\n * not contain a .size() method. Example:\r\n * ```cpp\r\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\r\n * auto filtered = lz::filter(vec, [](int i) { return i % 2 == 0; }); // { 2, 4 }\r\n * // or\r\n * auto filtered = vec | lz::filter([](int i) { return i % 2 == 0; }); // { 2, 4 }\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::filter_adaptor filter{};\r\n\r\n/**\r\n * @brief Filter iterable helper alias.\r\n * @tparam Iterable The type of the iterable to filter.\r\n * @tparam UnaryPredicate The type of the predicate function to use for filtering.\r\n * ```cpp\r\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\r\n * lz::filter_iterable<std::vector<int>, std::function<bool(int)>> filtered = lz::filter(vec, [](int i) { return i % 2 == 0; });\r\n * ```\r\n */\r\ntemplate<class Iterable, class UnaryPredicate>\r\nusing filter_iterable = detail::filter_iterable<Iterable, UnaryPredicate>;\r\n\r\n} // namespace lz\r\n\r\n#endif // end LZ_FILTER_HPP\r\n"
  },
  {
    "path": "include/Lz/flatten.hpp",
    "content": "#ifndef LZ_FLATTEN_HPP\r\n#define LZ_FLATTEN_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/flatten.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Flattens a nested iterable. For instance a vector of vectors will be flattened to a single iterable. Returns a\r\n * bidirectional iterable if the input is bidirectional, otherwise the same as the input iterable. If its input iterable is\r\n * forward or less, the end iterator will be a sentinel. Contains a .size() method if all the input iterables are sized. Example:\r\n * ```cpp\r\n * std::vector<std::vector<int>> vectors = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\r\n * auto flattened = lz::flatten(vectors); // { 1, 2, 3, 4, 5, 6, 7 }\r\n * // or\r\n * auto flattened = vectors | lz::flatten; // { 1, 2, 3, 4, 5, 6, 7 }\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::flatten_adaptor flatten{};\r\n\r\n/**\r\n * @brief Flatten iterable helper alias.\r\n * @tparam Iterable The type of the iterable to flatten.\r\n * ```cpp\r\n * std::vector<std::vector<int>> vectors = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\r\n * lz::flatten_iterable<std::vector<std::vector<int>>> flattened = lz::flatten(vectors);\r\n * ```\r\n */\r\ntemplate<class Iterable>\r\nusing flatten_iterable = detail::flatten_iterable<Iterable, dimensions<Iterable>::value - !std::is_array<Iterable>::value>;\r\n\r\n} // namespace lz\r\n\r\n#endif // LZ_FLATTEN_HPP\r\n"
  },
  {
    "path": "include/Lz/generate.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_GENERATE_HPP\r\n#define LZ_GENERATE_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/generate.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Generates n amount of elements using a generator function. Is a forward iterable, contains a .size() function and\r\n * returns a sentinel. Example:\r\n * ```cpp\r\n * lz::generate([]() { return 10; }, 5); // Generates 5 times the number 10\r\n * // or\r\n * lz::generate([]() { return 10; }); // Generates infinite times the number 10\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::generate_adaptor generate{};\r\n\r\n/**\r\n * @brief Generate iterable helper alias for infinite generation.\r\n * @tparam GeneratorFunc The type of the generator function.\r\n * ```cpp\r\n * lz::generate_iterable_inf<std::function<int()>> = lz::generate([]() { return 10; });\r\n * ```\r\n */\r\ntemplate<class GeneratorFunc>\r\nusing generate_iterable_inf = detail::generate_iterable<GeneratorFunc, true>;\r\n\r\n/**\r\n * @brief Generate iterable helper alias for finite generation.\r\n * @tparam GeneratorFunc The type of the generator function.\r\n * ```cpp\r\n * lz::generate_iterable<std::function<int()>> = lz::generate([]() { return 10; }, 10);\r\n * ```\r\n */\r\ntemplate<class GeneratorFunc>\r\nusing generate_iterable = detail::generate_iterable<GeneratorFunc, false>;\r\n\r\n} // namespace lz\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/Lz/generate_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GENERATE_WHILE_HPP\n#define LZ_GENERATE_WHILE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/generate_while.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Generates elements while the predicate returns true. The predicate must return an object that is compatible with\n * std::get. The second element (std::get<1>) must be an object convertible to bool, the first element (std::get<0>) can be any\n * type. This iterable does not contain a .size() member function. Its end() function returns a sentinel, rather than an actual\n * iterator object. It returns a (std::)input_iterator(_tag). Example:\n * ```cpp\n * int i = 0;\n * auto generator = lz::generate_while([&i]() {\n *    auto copy = i++;\n *    return std::make_pair(copy, copy != 4);\n * }); // { 0, 1, 2, 3 }\n * // or (cxx 14)\n * auto generator = lz::generate_while([i = 0]() {\n *   auto pair = std::make_pair(i, i != 4);\n *    ++i;\n *   return pair;\n * }); // { 0, 1, 2, 3 }\n * ```\n *\n */\nLZ_INLINE_VAR constexpr detail::generate_while_adaptor generate_while{};\n\n/**\n * @brief Type alias helper for the generate_while iterable.\n * @tparam GeneratorFunc The type of the generator function.\n * ```cpp\n * int i = 0;\n * lz::generate_while_iterable<std::function<std::pair<bool, int>()>> generator([&i]() {\n *     auto copy = i++;\n *     return std::make_pair(copy, copy != 4);\n * });\n * ```\n */\ntemplate<class GeneratorFunc>\nusing generate_while_iterable = detail::generate_while_iterable<GeneratorFunc>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/group_by.hpp",
    "content": "#pragma once\n\n#ifndef LZ_GROUP_BY_HPP\n#define LZ_GROUP_BY_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/group_by.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n    /**\n     * @brief Creates chunks of elements of which the predicate returns true. Input iterable must be sorted first before using\n     * this function. Its end iterator is a sentinel one if ithe input iterable is forward or has a sentinel. The iterable does\n     * not contain a .size() method. Its iterator category is bidirectional if possible. Example:\n     * ```cpp\n     * char str[] = \"aaabbccccd\";\n     * // normally, use std::sort(str.begin(), str.end()) before using this function, if it isn't sorted already\n     * auto grouper = lz::group_by(cstr, [](char a, char b) { return a == b; });\n     * // grouper = {{'a', 'a', 'a'}, {'b', 'b'}, {'c', 'c', 'c', 'c'}, {'d'}}\n     * // or\n     * auto grouper = cstr | lz::group_by([](char a, char b) { return a == b; });\n     * // grouper = {{'a', 'a', 'a'}, {'b', 'b'}, {'c', 'c', 'c', 'c'}, {'d'}}\n     * ```\n     */\n    LZ_INLINE_VAR constexpr detail::group_by_adaptor group_by{};\n\n    /**\n     * @brief Helper alias for group by iterable.\n     * @tparam Iterable The type of the iterable to group by.\n     * ```cpp\n     * char str[] = \"aaabbccccd\";\n     * using group_t = lz::group_by_iterable<decltype(str), std::function<bool(char, char)>>;\n     * group_t grouper = lz::group_by(str, [](char a, char b) { return a == b; });\n     * ```\n     */\n    template<class Iterable, class BinaryPredicate>\n    using group_by_iterable = detail::group_by_iterable<Iterable, BinaryPredicate>;\n\n} // namespace lz\n\n#endif // LZ_GROUP_BY_HPP\n"
  },
  {
    "path": "include/Lz/inclusive_scan.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INCLUSIVE_SCAN_HPP\n#define LZ_INCLUSIVE_SCAN_HPP\n\n#include <Lz/detail/adaptors/inclusive_scan.hpp>\n#include <Lz/procs/chain.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Performs an inclusive scan on a container. The first element will be the result of the binary operation with init value\n * and the first element of the input iterable. The second element will be the previous result + the next value of the input\n * iterable, the third element will be previous result + the next value of the input iterable, etc. It contains a .size() method\n * if the input iterable also has a .size() method. Its end() function returns a sentinel rather than an iterator and its iterator\n * category is forward. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * // std::plus is used as the default binary operation\n * auto scan = lz::inclusive_scan(vec, 0); // scan = { 1, 3, 6, 10, 15 }\n * // so 0 + 1 (= 1), 1 + 2 (= 3), 3 + 3 (= 6), 6 + 4 (= 10), 10 + 5 (= 15)\n *\n * // or\n * auto scan = vec | lz::inclusive_scan(0); // scan = { 1, 3, 6, 10, 15 }\n * // you can also add a custom operator:\n * auto scan = vec | lz::inclusive_scan(0, std::plus<int>{}); // scan = { 1, 3, 6, 10, 15 }\n * // When working with pipe expressions, you always need to specify the init value. When working with 'regular' functions, you\n * // can omit the init value, in which case it will be a default constructed object of the type of the container.\n * // Example\n * auto scan = lz::inclusive_scan(vec);\n * auto scan = lz::inclusive_scan(vec, 0);\n * // auto scan = vec | lz::inclusive_scan; // uses 0 and std::plus\n * auto scan = vec | lz::inclusive_scan(0);\n * ```\n */\nconstexpr detail::inclusive_scan_adaptor inclusive_scan{};\n\n/**\n * @brief Inclusive scan helper alias.\n * @tparam Iterable The input iterable\n * @tparam T The value type of the output (defaults to the value_type of `Iterable`)\n * @tparam BinaryOp The binary operation to use (defaults to `std::plus`)\n * ```\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * lz::inclusive_scan_iterable<std::vector<int>> scan = lz::inclusive_scan(vec, 0); // scan = { 1, 3, 6, 10, 15 }\n * ```\n*/\ntemplate<class Iterable, class T = detail::val_iterable_t<Iterable>,\n         class BinaryOp = LZ_BIN_OP(plus, lz::detail::val_iterable_t<Iterable>)>\nusing inclusive_scan_iterable = detail::inclusive_scan_iterable<Iterable, T, BinaryOp>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/interleave.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INTERLEAVE_HPP\n#define LZ_INTERLEAVE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/interleave.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Interleave the elements of the given iterables. This means it returns the element of the first iterable, then the\n * second, etc... and resets when the last iterable is reached. The iterator will stop at the end of the shortest iterable.\n * The iterator category is the same as its \"weakest\" input iterable. The reference type returned by operator* will:\n * - be by value if one of the iterables yields by value\n * - be by const reference if one of the iterables yield by const reference.\n * - be by mutable reference if all iterables yield by mutable reference.\n * Contains a .size() function if all iterables have a .size() function. The size is the sum of all the sizes of the\n * iterables. Contains a sentinel if one of the iterables contains a sentinel. Its iterator category is the 'weakest' of the\n * input iterables. Contains a sentinel if one of the iterables contains a sentinel or if one of them is forward.\n *\n * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is traversed\n * to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you can use\n * `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the iterable.\n * `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its entire size is\n * therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will traverse the iterable\n * once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but will return the cached\n * size instead. The following iterables require a(n) (eagerly)sized iterable:\n * - `lz::chunks`\n * - `lz::enumerate`\n * - `lz::exclude`\n * - `lz::interleave`\n * - `lz::rotate`\n * - `lz::take`\n * - `lz::take_every`\n * - `lz::zip_longest`\n * - `lz::zip`\n * Example:\n * ```cpp\n * std::vector<int> vec1 = { 1, 2, 3 }, vec2 = { 4, 5, 6, 7 }, vec3 = { 8, 9, 10, 11, 12 };\n * auto interleaved = lz::interleave(vec1, vec2, vec3); // interleaved = { 1, 4, 8, 2, 5, 9, 3, 6, 10 }\n * // or\n * auto interleaved = vec1 | lz::interleave(vec2, vec3); // interleaved = { 1, 4, 8, 2, 5, 9, 3, 6, 10 }\n * auto c = lz::interleave(vec1, lz::range(5, 10)); // yields by value, not by reference\n * ```\n */\nLZ_INLINE_VAR constexpr detail::interleave_adaptor interleave{};\n\n\n/**\n * @brief Interleave alias helper.\n * @tparam Iterables The iterables to interleave.\n * ```\n * std::vector<int> vec1 = { 1, 2, 3 }, vec2 = { 4, 5, 6, 7 };\n * lz::interleave_iterable<std::vector<int>, std::vector<int>> interleaved = lz::interleave(vec1, vec2);\n * ```\n*/\ntemplate<class... Iterables>\nusing interleave_iterable = detail::interleave_iterable<Iterables...>;\n\n} // namespace lz\n\n#endif // LZ_INTERLEAVE_HPP\n"
  },
  {
    "path": "include/Lz/intersection.hpp",
    "content": "#pragma once\n\n#ifndef LZ_INTERSECTION_HPP\n#define LZ_INTERSECTION_HPP\n\n#include <Lz/detail/adaptors/intersection.hpp>\n#include <Lz/procs/chain.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Intersects the first iterable with the second iterable. The result is a new iterable containing the elements that are in\n * both iterables. Both iterables must be sorted first. Returns a bidirectional iterable if the input iterables are at least\n * bidirectional, otherwise forward. Returns a sentinel if it is a forward iterable or has a sentinel. Does not contain a .size()\n * method. Example:\n * ```cpp\n * std::string a = \"aaaabbcccddee\";\n * std::string b = \"aabccce\";\n *\n * std::sort(a.begin(), a.end());\n * std::sort(b.begin(), b.end());\n *\n * auto intersect = lz::intersection(a, b); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n * // or\n * auto intersect = lz::intersection(a, b, std::less<>{}); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n * // or\n * auto intersect = a | lz::intersection(b); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n * // or\n * auto intersect = a | lz::intersection(b, std::less<>{}); // { 'a', 'a', 'b', 'c', 'c', 'c', 'e' }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::intersection_adaptor intersection{};\n\n/**\n * @brief Intersection helper alias.\n *\n * @tparam Iterable The first iterable type.\n * @tparam Iterable2 The second iterable type.\n * @tparam BinaryPredicate The binary predicate type used to compare elements from both iterables. Defaults to `std::less<>`.\n * ```cpp\n * std::string a = \"aaaabbcccddee\";\n * std::string b = \"aabccce\";\n * std::sort(a.begin(), a.end());\n * std::sort(b.begin(), b.end());\n * lz::intersection_iterable<std::string, std::string> intersect = lz::intersection(a, b);\n * ```\n */\ntemplate<class Iterable, class Iterable2, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable>)>\nusing intersection_iterable = detail::intersection_iterable<Iterable, Iterable2, BinaryPredicate>;\n\n} // namespace lz\n\n#endif // LZ_INTERSECTION_HPP\n"
  },
  {
    "path": "include/Lz/iter_tools.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ITER_TOOLS_HPP\n#define LZ_ITER_TOOLS_HPP\n\n#include <Lz/detail/adaptors/iter_tools.hpp>\n#include <Lz/detail/procs/tuple_expand.hpp>\n#include <Lz/procs/chain.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Zip with iterable helper alias\n * @tparam Fn The function to apply on the elements of the zipped iterables.\n * @tparam Iterables The iterables to zip together.\n * ```cpp\n * std::vector<int> a = { 1, 2, 3 };\n * std::vector<int> b = { 1, 2, 3 };\n * using zip_t = lz::zip_with_iterable<std::function<int(int, int)>, std::vector<int>, std::vector<int>>;\n * zip_t zipped = lz::zip_with([](int a, int b) { return a + b; }, a, b);\n */\ntemplate<class Fn, class... Iterables>\nusing zip_with_iterable = map_iterable<zip_iterable<Iterables...>, detail::tuple_expand<Fn>>;\n\n/**\n * @brief Unzip with helper alias\n * @tparam Iterable The iterable to unzip.\n * @tparam Predicate The function to apply on the elements of the zipped iterables.\n * ```cpp\n * std::vector<std::tuple<int, int>> zipped = { std::make_tuple(1, 6), std::make_tuple(2, 7), std::make_tuple(3, 8) };\n * using unzip_t = lz::unzip_with_iterable<std::vector<std::tuple<int, int>>, std::function<bool(int, int)>>;\n * unzip_t unzipped = lz::unzip_with(zipped, [](int a, int b) { return a + b; });\n * ```\n */\ntemplate<class Iterable, class Predicate>\nusing unzip_with_iterable = map_iterable<Iterable, detail::tuple_expand<Predicate>>;\n\n/**\n * @brief Lines iterable helper alias\n *\n * @tparam Iterable The iterable to split into lines.\n * @tparam CharT The character type of the string to split into lines. Defaults to `char`.\n * ```cpp\n * std::string str = \"Hello\\nWorld\\n!\";\n * lz::lines_iterable<std::string> splitted = lz::lines(str);\n * ```\n */\ntemplate<class Iterable, class CharT = char>\nusing lines_iterable = detail::lines_iterable<CharT, Iterable>;\n\n/**\n * @brief Lines iterable helper alias for string_views. string_views are not held by reference, but copied.\n *\n * @tparam CharT The character type of the string to split into lines. Defaults to `char`.\n * ```cpp\n * lz::lines_iterable_sv<> actual = lz::lines(lz::string_view(\"hello world\\nthis is a message\\ntesting\"));\n * ```\n */\ntemplate<class CharT = char>\nusing lines_iterable_sv = lines_iterable<lz::copied<lz::basic_string_view<CharT>>, CharT>;\n\n/**\n * @brief As iterable helper alias\n *\n * @tparam Iterable The iterable to convert the elements of.\n * @tparam T The type to convert the elements to.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * // c++ 11\n * lz::as_iterable<std::vector<int>, float> iterable = lz::as<float>{}(vec);\n * // c++ 14 and later\n * lz::as_iterable<std::vector<int>, float> iterable = lz::as<float>(vec);\n */\ntemplate<class Iterable, class T>\nusing as_iterable = detail::as_iterable<Iterable, T>;\n\n/**\n * @brief Keys, values and get_nth iterable helper alias\n *\n * @tparam Iterable The iterable to get the nth element from.\n * @tparam N The index of the element to get from the iterable. 0 for keys, 1 for values, and any other index for the nth\n * element.\n * ```cpp\n * std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n * lz::get_nth_iterable<std::vector<std::tuple<int, int, int>>, 2> iterable = lz::get_nth<2>(three_tuple_vec);\n * lz::get_nth_iterable<std::vector<std::tuple<int, int, int>>, 0> keys_iterable = lz::keys(three_tuple_vec);\n * ```\n */\ntemplate<class Iterable, size_t N>\nusing get_nth_iterable = detail::get_nth_iterable<Iterable, N>;\n\n/**\n * @brief Keys iterable helper alias\n *\n * @tparam Iterable A std::get-able iterable, such as a vector of tuples or map\n * ```cpp\n * std::map<int, std::string> m = { { 1, \"hello\" }, { 2, \"world\" }, { 3, \"!\" } };\n * lz::keys_iterable<decltype(m)> iterable = lz::keys(m);\n * ```\n */\ntemplate<class Iterable>\nusing keys_iterable = get_nth_iterable<Iterable, 0>;\n\n/**\n * @brief Values iterable helper alias\n *\n * @tparam Iterable A std::get-able iterable, such as a vector of tuples or map\n * ```cpp\n * std::map<int, std::string> m = { { 1, \"hello\" }, { 2, \"world\" }, { 3, \"!\" } };\n * lz::values_iterable<decltype(m)> iterable = lz::values(m);\n * ```\n */\ntemplate<class Iterable>\nusing values_iterable = get_nth_iterable<Iterable, 1>;\n\n/**\n * @brief get nths iterable helper alias\n *\n * @tparam Iterable A std::get-able iterable, such as a vector of tuples.\n * @tparam N The indexes of the elements to get from the iterable. For example, `0, 2` will get the first and third elements\n * from each tuple in the iterable.\n * ```cpp\n * std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n * lz::get_nths_iterable<std::vector<std::tuple<int, int, int>>, 0, 2> iterable = lz::get_nths<0, 2>(three_tuple_vec);\n * lz::get_nths_iterable<std::vector<std::tuple<int, int, int>>, 0, 2> iterable = three_tuple_vec | lz::get_nths<0, 2>;\n * ```\n */\ntemplate<class Iterable, size_t... N>\nusing get_nths_iterable = detail::get_nths_iterable<Iterable, N...>;\n\n/**\n * @brief Filter map iterable helper alias\n *\n * @tparam Iterable The iterable to filter and map.\n * @tparam UnaryFilterPredicate The predicate to filter the elements with.\n * @tparam UnaryMapOp The function to map the filtered elements with.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * using filter_map_iterable = lz::filter_map_iterable<std::vector<int>, std::function<bool(int)>, std::function<int(int)>>;\n * filter_map_iterable fm_iterable = lz::filter_map(vec, [](int i) { return i % 2 == 0; }, [](int i) { return i * 2; });\n * ```\n */\ntemplate<class Iterable, class UnaryFilterPredicate, class UnaryMapOp>\nusing filter_map_iterable = detail::filter_map_iterable<Iterable, UnaryFilterPredicate, UnaryMapOp>;\n\n/**\n * @brief Select iterable helper alias\n * @tparam Iterable The iterable to select elements from.\n * @tparam SelectorIterable The iterable that contains the selection criteria (true/false).\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * std::vector<bool> selector = { true, false, true, false, true };\n * using select_iterable = lz::select_iterable<std::vector<int>, std::vector<bool>>;\n * select_iterable selected = lz::select(vec, selector);\n * ```\n */\ntemplate<class Iterable, class SelectorIterable>\nusing select_iterable = detail::select_iterable<Iterable, SelectorIterable>;\n\n/**\n * @brief Drop back iterable helper alias\n * @tparam Iterable The iterable to drop elements from the back.\n * @tparam UnaryPredicate The predicate to determine which elements to drop from the back.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * using drop_back_iterable = lz::drop_back_iterable<std::vector<int>>;\n * drop_back_iterable dropped = lz::drop_back_while(vec, [](int i) { return i > 3; });\n * ```\n */\ntemplate<class Iterable, class UnaryPredicate>\nusing drop_back_iterable = detail::drop_back_iterable<Iterable>;\n\n/**\n * @brief Trim iterable helper alias\n * @tparam Iterable The iterable to trim.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * using trim_iterable = lz::trim_iterable<std::vector<int>>;\n * trim_iterable trimmed = lz::trim(vec, [](int i) { return i < 3; }, [](int i) { return i > 4; });\n * ```\n */\ntemplate<class Iterable>\nusing trim_iterable = detail::trim_iterable<Iterable>;\n\n/**\n * @brief Trim string helper alias\n * @tparam String The string type to trim, for example `std::string` or `std::wstring`.\n * ```cpp\n * std::string str = \"  hello  \";\n * lz::trim_string_iterable<std::string> trimmed = lz::trim(str); // \"hello\"\n * ```\n */\ntemplate<class String>\nusing trim_string_iterable = trim_iterable<String>;\n\n#ifdef LZ_HAS_CXX_11\n\n/**\n * @brief Returns an iterable that converts the elements in the given container to the type @p `T`, using `lz::map` and\n * `static_cast`.\n * @tparam T The type to convert the elements to.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto floats = lz::as<float>{}(vec); // { 1.f, 2.f, 3.f, 4.f, 5.f }\n * // or\n * auto floats = vec | lz::as<float>{}; // { 1.f, 2.f, 3.f, 4.f, 5.f }\n * ```\n */\ntemplate<class T>\nusing as = detail::as_adaptor<T>;\n\n/**\n * @brief Gets the nth element from a std::get-able container, using `std::get` and `lz::map`. Example:\n * ```cpp\n * std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n * auto actual = lz::get_nth<2>{}(three_tuple_vec); // {3, 6, 9}\n * // or\n * auto actual = three_tuple_vec | lz::get_nth<2>{}; // {3, 6, 9}\n * ```\n * @tparam I The index to get from the std::get-able container.\n */\ntemplate<size_t I>\nusing get_nth = detail::get_n_adaptor<I>;\n\n/**\n * @brief Gets nths indexes from a std::get-able container, using `std::get<N>` and `lz::zip`. Example:\n * ```cpp\n * std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n * auto actual = lz::get_nths<0, 2>{}(three_tuple_vec); // { {1, 3}, {4, 6}, {7, 9} }\n * // or\n * auto actual = three_tuple_vec | lz::get_nths<0, 2>{}; // { {1, 3}, {4, 6}, {7, 9} }\n * ```\n * @tparam N The indexes of the elements to get from the iterable. For example, `0, 2` will get the first and third elements\n * from each tuple in the iterable.\n */\ntemplate<size_t... N>\nusing get_nths = detail::get_nths_adaptor<N...>;\n\n#else\n\n/**\n * @brief Returns an iterable that converts the elements in the given container to the type @p `T` using `lz::map`.\n * @tparam T The type to convert the elements to.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto floats = lz::as<float>(vec); // { 1.f, 2.f, 3.f, 4.f, 5.f }\n * // or\n * auto floats = vec | lz::as<float>; // { 1.f, 2.f, 3.f, 4.f, 5.f }\n * ```\n */\ntemplate<class T>\nLZ_INLINE_VAR constexpr detail::as_adaptor<T> as{};\n\n/**\n * @brief Gets the nth element from a std::get-able container, using `std::get` and `lz::map`. Example:\n * ```cpp\n * std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n * auto actual = lz::get_nth<2>(three_tuple_vec); // {3, 6, 9}\n * // or\n * auto actual = three_tuple_vec | lz::get_nth<2>; // {3, 6, 9}\n * ```\n * @tparam I The index to get from the std::get-able container.\n */\ntemplate<size_t I>\nLZ_INLINE_VAR constexpr detail::get_n_adaptor<I> get_nth{};\n\n/**\n * @brief Gets nths indexes from a std::get-able container, using `std::get<N>` and `lz::zip`. Example:\n * ```cpp\n * std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n * auto actual = lz::get_nths<0, 2>(three_tuple_vec); // { {1, 3}, {4, 6}, {7, 9} }\n * // or\n * auto actual = three_tuple_vec | lz::get_nths<0, 2>; // { {1, 3}, {4, 6}, {7, 9} }\n * ```\n * @tparam N The indexes of the elements to get from the iterable. For example, `0, 2` will get the first and third elements\n * from each tuple in the iterable.\n */\ntemplate<size_t... N>\nLZ_INLINE_VAR constexpr detail::get_nths_adaptor<N...> get_nths{};\n\n#endif\n\n/**\n * @brief Zips the given iterables together and applies the function @p `fn` on the elements using `lz::map` and `lz::zip`.\n * Cannot be used in a pipe expression. Example:\n * ```cpp\n * std::vector<int> a = { 1, 2, 3 };\n * std::vector<int> b = { 1, 2, 3 };\n * std::vector<int> c = { 1, 2, 3 };\n * auto zipped = lz::zip_with([](int a, int b, int c) { return a + b + c; }, a, b, c); // {3, 6, 9}\n * ```\n *\n * @param iterables The iterables to zip together.\n * @param fn The function to apply on the elements.\n * @return A map object that can be iterated over.\n */\ntemplate<class Fn, class... Iterables>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14 zip_with_iterable<Fn, detail::remove_ref_t<Iterables>...> zip_with(Fn fn,\n                                                                                                    Iterables && ... iterables) {\n    return lz::map(lz::zip(std::forward<Iterables>(iterables)...), detail::make_expand_fn(std::move(fn)));\n}\n\n/**\n * @brief Unzips an iterable of tuple-like elements using the provided predicate function. The predicate function should take\n * the same number of arguments as the number of elements in the tuples and return a value. Example:\n * ```cpp\n * std::vector<std::tuple<int, int>> zipped = { std::make_tuple(1, 6), std::make_tuple(2, 7), std::make_tuple(3, 8) };\n * auto unzipped = lz::unzip_with(zipped, [](int a, int b) { return a + b; }); // {7, 9, 11}\n * or\n * auto unzipped = zipped | lz::unzip_with([](int a, int b) { return a + b; }); // {7, 9, 11}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::unzip_with_adaptor unzip_with{};\n\n/**\n * @brief Returns a split_iterable, that splits the string on `'\\n'` using lz::split. Returns string_views to its substrings.\n * Example:\n * ```cpp\n * std::string str = \"Hello\\nWorld\\n!\";\n * auto splitted = lz::lines(str); // {\"Hello\", \"World\", \"!\"}\n * lz::string_view str_view = \"Hello\\nWorld\\n!\";\n *\n * // splitted_view, does not hold a reference to the original string\n * auto splitted_view = lz::lines(str_view); // {\"Hello\", \"World\", \"!\"},\n * // or\n * auto splitted = str | lz::lines; // {\"Hello\", \"World\", \"!\"}\n * // or\n * auto splitted = lz::lines(lz::c_string(\"Hello\\nWorld\\n!\")); // {\"Hello\", \"World\", \"!\"}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::lines_adaptor lines{};\n\n/**\n * @brief Gets the keys from a std::get-able container, using `std::get<0>` and `lz::map`. Example:\n * ```cpp\n * std::map<int, std::string> m = { { 1, \"hello\" }, { 2, \"world\" }, { 3, \"!\" } };\n * auto keys = lz::keys(m); // {1, 2, 3}\n * // or\n * auto keys = m | lz::keys; // {1, 2, 3}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::get_n_adaptor<0> keys{};\n\n/**\n * @brief Gets the values from a std::get-able container, using `std::get<1>` and `lz::map`. Example:\n * ```cpp\n * std::map<int, std::string> m = { { 1, \"hello\" }, { 2, \"world\" }, { 3, \"!\" } };\n * auto values = lz::values(m); // {\"hello\", \"world\", \"!\"}\n * // or\n * auto values = m | lz::values; // {\"hello\", \"world\", \"!\"}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::get_n_adaptor<1> values{};\n\n/**\n * @brief Filters an iterable using `predicate` and then maps those elements using `fn`, using `lz::filter` and `lz::map`.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto fm = lz::filter_map(vec, [](int i) { return i % 2 == 0; }, [](int i) { return i * 2; }); // {4, 8}\n * // or\n * auto fm = vec | lz::filter_map([](int i) { return i % 2 == 0; }, [](int i) { return i * 2; }); // {4, 8}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::filter_map_adaptor filter_map{};\n\n/**\n * @brief Selects elements from the first iterable if the corresponding element in the second iterable is `true`, using\n * `lz::filter`, `lz::zip` and `lz::map`. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * std::vector<bool> bools = { true, false, true, false, true };\n * auto selected = lz::select(vec, bools); // {1, 3, 5}\n * // or\n * auto selected = vec | lz::select(bools); // {1, 3, 5}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::select_adaptor select{};\n\n/**\n * @brief Trims the back of the sequence as long as `predicate` returns `true`, then reverses the sequence again. Essentially\n * turning the predicate into a not fn, using `lz::reverse`, `lz::drop_back_while` followed by another `lz::reverse`. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto trimmed = lz::drop_back_while(vec, [](int i) { return i > 3; }); // {1, 2, 3}\n * // or\n * auto trimmed = vec | lz::drop_back_while([](int i) { return i > 3; }); // {1, 2, 3}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::drop_back_while_adaptor drop_back_while{};\n\n/**\n * Trims the beginning and ending of a sequence, as long as the first predicate returns true for the trimming of the\n * beginning and as long as the last predicate returns true for the trimming of the ending. This is done using `lz::drop_while`\n * and `lz::drop_back_while`. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto trimmed = lz::trim(vec, [](int i) { return i < 3; }, [](int i) { return i > 4; }); // {3}\n * // or\n * auto trimmed = vec | lz::trim([](int i) { return i < 3; }, [](int i) { return i > 4; }); // {3}\n * std::string str = \"  hello  \";\n * auto trimmed = lz::trim(str); // \"hello\", uses std::isspace\n * // or\n * auto trimmed = str | lz::trim; // \"hello\", uses std::isspace\n * ```\n */\nLZ_INLINE_VAR constexpr detail::trim_adaptor trim{};\n\n/**\n * @brief Decays the given iterable to an iterator category of the given tag, using `lz::as_iterator`\n * and `lz::map`. This can be handy to return sentinels at some point or prevent `lz::eager_size` calls. Example:\n * ```cpp\n * auto v1 = {1, 2, 3};\n * auto v2 = {1, 2, 3};\n * // f1 is not sized and returns a bidirectional iterator\n * auto f1 = lz::filter(v1, [](auto&& i) {\n *     return i > 1;\n * });\n * // f2 is not sized and returns a bidirectional iterator\n * auto f2 = lz::filter(v2, [](auto&& i) {\n *     return i > 1;\n * });\n *\n * // lz::zip calls lz::eager_size if:\n * // - the iterable is at least bidirectional.\n * // - the iterable is not sentinelled\n * // f1 and f2 are both bidirectional, so lz::zip will call lz::eager_size on them.\n * // In this case we don't want to call lz::eager_size because we're not interested in going bidirectionally.\n * // We can use lz::iter_decay(std::forward_iterator_tag{}) to decay the iterables to a forward iterator\n *\n * iterable auto zipper = lz::zip(lz::iter_decay(f1, std::forward_iterator_tag{}), f2);\n *\n * // f1 or f2 can be forward, or both, for it not to call lz::eager_size on .end().\n * auto end = zipper.end(); // does not call lz::eager_size because f1 is decayed to a forward iterator.\n */\nLZ_INLINE_VAR constexpr detail::iter_decay iter_decay{};\n\n/**\n * @brief Pads the given iterable to a certain size, using `lz::concatenate` and `lz::repeat`. A size is needed to be provided\n * to pad it to a certain size. It will return the most suitable reference type. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3 };\n * auto padded = lz::pad(vec, 0, 2); // {1, 2, 3, 0, 0} by value\n * auto to_pad = 3;\n * auto padded = lz::pad(vec, to_pad, 0); // {1, 2, 3, 0, 0} by ref\n * auto padded = lz::pad(vec, std::ref(to_pad), 0); // {1, 2, 3, 0, 0} by ref for more flexibility (copy ctors etc.)\n * auto padded = vec | lz::pad(0, 2); // {1, 2, 3, 0, 0} by value\n * ```\n */\nLZ_INLINE_VAR constexpr detail::pad_adaptor pad{};\n\n} // End namespace lz\n\n#endif // LZ_ITER_TOOLS_HPP\n"
  },
  {
    "path": "include/Lz/join_where.hpp",
    "content": "#pragma once\n\n#ifndef LZ_JOIN_WHERE_HPP\n#define LZ_JOIN_WHERE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/join_where.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Performs an SQL-like join on two iterables where the join condition is specified by the selectors. Its end() function\n * returns a sentinel, it contains a forward iterator. It also does not contain a size() method. The second iterable must be\n * sorted first. It is therfore recommended to sort the smallest iterable, performance wise. Operator< is used to compare the\n * first and the second selector. For instance, in the example below operator< is used to compare int with int. Example:\n * ```cpp\n * std::vector<std::pair<int, int>> vec = {{1, 2}, {2, 3}, {3, 4}, {4, 5}};\n * std::vector<std::pair<int, int>> vec2 = {{1, 2}, {3, 4}, {4, 5}, {5, 6}};\n * auto joined = lz::join_where(vec, vec2,\n *                              [](const auto& a) { return a.first; }, // join on the first element of the pair in vec\n *                              [](const auto& a) { return a.first; }, // join on the first element of the pair in vec2\n *                              [](const auto& a, const auto& b) { return std::make_pair(a.first, b.second); }\n * ); // joined contains: {{1, 2}, {3, 4}, {4, 5}}\n * // or\n * auto joined = vec | lz::join_where(vec2,\n *                                   [](const auto& a) { return a.first; }, // join on the first element of the pair in vec\n *                                   [](const auto& a) { return a.first; }, // join on the first element of the pair in vec2\n *                                   [](const auto& a, const auto& b) { return std::make_pair(a.first, b.second); }\n * ); // joined contains: {{1, 2}, {3, 4}, {4, 5}}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::join_where_adaptor join_where{};\n\n/**\n * @brief Join where iterable helper alias.\n *\n * @tparam IterableA The first iterable type.\n * @tparam IterableB The second iterable type.\n * @tparam SelectorA The selector type for the first iterable.\n * @tparam SelectorB The selector type for the second iterable.\n * @tparam ResultSelector The result selector type that combines elements from both iterables.\n * ```cpp\n * std::vector<std::pair<int, int>> vec = {{1, 2}, {2, 3}, {3, 4}, {4, 5}};\n * std::vector<std::pair<int, int>> vec2 = {{1, 2}, {3, 4}, {4, 5}, {5, 6}};\n *\n * using it_a = std::vector<std::pair<int, int>>;\n * using it_b = std::vector<std::pair<int, int>>;\n * using selector_a = std::function<int(const std::pair<int, int>&)>;\n * using selector_b = std::function<int(const std::pair<int, int>&)>;\n * using result_selector = std::function<std::pair<int, int>(const std::pair<int, int>&, const std::pair<int, int>&)>;\n * using iterable = lz::join_where_iterable<it_a, it_b, selector_a, selector_b, result_selector>;\n *\n * iterable joined = lz::join_where_iterable(vec, vec2,\n *                             [](const auto& a) { return a.first; },\n *                             [](const auto& a) { return a.first; },\n *                             [](const auto& a, const auto& b) { return std::make_pair(a.first, b.second); }\n * );\n * ```\n */\ntemplate<class IterableA, class IterableB, class SelectorA, class SelectorB, class ResultSelector>\nusing join_where_iterable = detail::join_where_iterable<IterableA, IterableB, SelectorA, SelectorB, ResultSelector>;\n\n} // namespace lz\n\n#endif // LZ_JOIN_WHERE_HPP\n"
  },
  {
    "path": "include/Lz/loop.hpp",
    "content": "#pragma once\n\n#ifndef LZ_LOOP_HPP\n#define LZ_LOOP_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/loop.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Loops over an iterable. Can be finite or infinite.\n * If finite:\n *     Contains a .size() method if the input iterable has a .size() method. Will return an actual iterator if the input\n *     iterable is at least bidirectional and does not have a sentinel. Otherwise it will return a default_sentinel_t. Its input\n * iterator category will be the same as the input iterator category. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4 };\n * auto looper = lz::loop(vec, 2); // {1, 2, 3, 4, 1, 2, 3, 4}\n * // or\n * auto looper = vec | lz::loop(2); // {1, 2, 3, 4, 1, 2, 3, 4}\n * ```\n * If infinite:\n *     Does not contain a .size() method. Loop infinitely over the input iterable. Its input iterator category will\n *     always be forward. It returns a default_sentinel_t. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4 };\n * auto looper = lz::loop(vec); // {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, ...}\n * // or\n * auto looper = vec | lz::loop; // {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, ...}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::loop_adaptor loop{};\n\n/**\n * @brief Loop infinite helper alias.\n * @tparam Iterable The iterable type.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4 };\n * lz::loop_iterable_inf loop = lz::loop(vec);\n * ```\n */\ntemplate<class Iterable>\nusing loop_iterable_inf = detail::loop_iterable<Iterable, true>;\n\n/**\n * @brief Loop finite helper alias.\n * @tparam Iterable The iterable type.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4 };\n * lz::loop_iterable loop = lz::loop(vec, 2);\n * ```\n */\ntemplate<class Iterable>\nusing loop_iterable = detail::loop_iterable<Iterable, false>;\n\n} // namespace lz\n\n#endif // LZ_LOOP_HPP\n"
  },
  {
    "path": "include/Lz/map.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_MAP_HPP\r\n#define LZ_MAP_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/map.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief This adaptor is used to apply a function to each element in an iterable. The iterator category is the same as the input\r\n * iterator category. Its end() function will return a sentinel, if the input iterable has a forward iterator or has a sentinel.\r\n * If its input iterable has a .size() method, then this iterable will also have a .size() method. Example:\r\n * ```cpp\r\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\r\n * auto map = vec | lz::map([](int i) { return i * 2; }); // map = { 2, 4, 6, 8, 10 }\r\n * // or\r\n * auto map = lz::map(vec, [](int i) { return i * 2; }); // map = { 2, 4, 6, 8, 10 }\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::map_adaptor map{};\r\n\r\n/**\r\n * @brief Map iterable helper alias.\r\n * @tparam Iterable The type of the iterable to map.\r\n * @tparam UnaryOp The type of the unary operation to apply to each element.\r\n * ```cpp\r\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\r\n * lz::map_iterable<std::vector<int>, std::function<int(int)>> map_vec(vec, [](int i) { return i * 2; });\r\n * ```\r\n */\r\ntemplate<class Iterable, class UnaryOp>\r\nusing map_iterable = detail::map_iterable<Iterable, UnaryOp>;\r\n\r\n} // namespace lz\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/Lz/pairwise.hpp",
    "content": "#pragma once\n\n#ifndef LZ_PAIRWISE_HPP\n#define LZ_PAIRWISE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/pairwise.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Creates a pairwise iterable with pairs of size @p pair_size from the given @p iterable\n * ```cpp\n * auto vec = std::vector{1, 2, 3, 4, 5};\n * auto paired = lz::pairwise(vec, 2); // {{1, 2}, {2, 3}, {3, 4}, {4, 5}}\n * auto pairwise_by_2 = vec | lz::pairwise(2); // {{1, 2}, {2, 3}, {3, 4}, {4, 5}}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::pairwise_adaptor pairwise{};\n\n/**\n * @brief The pairwise iterable helper alias.\n *\n * @tparam Iterable The iterable type\n * ```cpp\n * std::vector<int> vec = {1, 2, 3, 4, 5};\n * lz::pairwise_iterable<std::vector<int>> paired = lz::pairwise(vec, 2);\n * ```\n */\ntemplate<class Iterable>\nusing pairwise_iterable = detail::pairwise_iterable<Iterable>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/procs/chain.hpp",
    "content": "#pragma once\n\n#ifndef LZ_PROCS_CHAIN_HPP\n#define LZ_PROCS_CHAIN_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/is_adaptor.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n\n#ifdef LZ_HAS_CONCEPTS\n\n#include <Lz/traits/concepts.hpp>\n\nLZ_MODULE_EXPORT template<class Iterable, class Adaptor>\n    requires(lz::adaptor<lz::detail::remove_cref_t<Adaptor>> && lz::iterable<lz::detail::remove_ref_t<Iterable>>)\n[[nodiscard]] constexpr auto operator|(Iterable&& iterable, Adaptor&& adaptor) {\n    return std::forward<Adaptor>(adaptor)(std::forward<Iterable>(iterable));\n}\n\n#else\n\nLZ_MODULE_EXPORT template<class Iterable, class Adaptor>\nLZ_NODISCARD constexpr auto operator|(Iterable&& iterable, Adaptor&& adaptor)\n    -> lz::detail::enable_if_t<lz::detail::is_adaptor<lz::detail::remove_cref_t<Adaptor>>::value &&\n                                   lz::detail::is_iterable<lz::detail::remove_ref_t<Iterable>>::value,\n                               decltype(std::forward<Adaptor>(adaptor)(std::forward<Iterable>(iterable)))> {\n    return std::forward<Adaptor>(adaptor)(std::forward<Iterable>(iterable));\n}\n\n#endif // LZ_HAS_CONCEPTS\n\n#endif // LZ_PROCS_CHAIN_HPP\n"
  },
  {
    "path": "include/Lz/procs/distance.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DISTANCE_HPP\n#define LZ_DISTANCE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/distance.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Gets the distance of an iterator pair. If iterator is not random access, the operation will be O(n), otherwise O(1).\n *\n * @param begin The beginning of the iterator\n * @param end The end of the iterator\n * @return The length of the iterator\n */\ntemplate<class Iterator, class S>\nLZ_NODISCARD constexpr detail::diff_type<Iterator> distance(Iterator begin, S end) {\n    return detail::distance_impl(begin, end);\n}\n\n/**\n * @brief Gets the distance of an iterable. If iterable is not random access, the operation will be O(n), otherwise O(1).\n *\n * @param iterable The iterable to get the distance of\n * @return The length of the iterable\n */\ntemplate<class Iterable>\nLZ_NODISCARD constexpr detail::diff_iterable_t<detail::remove_cvref_t<Iterable>> distance(Iterable&& iterable) {\n    using std::distance;\n    return distance(detail::begin(iterable), detail::end(iterable));\n}\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/procs/eager_size.hpp",
    "content": "#pragma once\n\n#ifndef LZ_HEAGER_SIZE_HPP\n#define LZ_HEAGER_SIZE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/is_sized.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/procs/distance.hpp>\n\n#ifndef LZ_HAS_CXX_17\n#include <Lz/detail/traits/enable_if.hpp>\n#endif\n\nLZ_MODULE_EXPORT namespace lz {\n\n#ifdef LZ_HAS_CXX_17\n\n/**\n * @brief Gets the size of an iterable. If the iterable is not sized, it will calculate the size by iterating over the\n * iterable, which is O(n). Example:\n * ```cpp\n * // Sized, making it O(1)\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * std::cout << lz::eager_size(vec) << '\\n'; // prints 5\n *\n * // Not sized, making it O(n)\n * auto not_sized = lz::c_string(\"Hello\");\n * std::cout << lz::eager_size(not_sized) << '\\n'; // prints 5\n * ```\n *\n * @param c The iterable to get the size of.\n * @return The size of the iterable.\n */\ntemplate<class Iterable>\n[[nodiscard]] constexpr auto eager_size(Iterable&& c) {\n    if constexpr (detail::is_sized_v<Iterable>) {\n        return lz::size(c);\n    }\n    else {\n        using std::distance;\n        return static_cast<size_t>(distance(c.begin(), c.end()));\n    }\n}\n\n#else\n\n/**\n * @brief Gets the size of an iterable. If the iterable is not sized, it will calculate the size by iterating over the\n * iterable, which is O(n). Example:\n * ```cpp\n * // Sized, making it O(1)\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * std::cout << lz::eager_size(vec) << '\\n'; // prints 5\n *\n * // Not sized, making it O(n)\n * auto not_sized = lz::c_string(\"Hello\");\n * std::cout << lz::eager_size(not_sized) << '\\n'; // prints 5\n * ```\n *\n * @param c The iterable to get the size of.\n * @return The size of the iterable.\n */\ntemplate<class Iterable>\nLZ_NODISCARD constexpr auto\neager_size(Iterable&& c) -> detail::enable_if_t<detail::is_sized<Iterable>::value, decltype(lz::size(c))> {\n    return lz::size(c);\n}\n\n/**\n * @brief Gets the size of an iterable. If the iterable is not sized, it will calculate the size by iterating over the\n * iterable, which is O(n), unless it is random access. Example:\n * ```cpp\n * // Sized, making it O(1)\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * std::cout << lz::eager_size(vec) << '\\n'; // prints 5\n *\n * // Not sized, not random access, making it O(n)\n * auto not_sized = lz::c_string(\"Hello\");\n * std::cout << lz::eager_size(not_sized) << '\\n'; // prints 5\n * ```\n *\n * @param c The iterable to get the size of.\n * @return The size of the iterable.\n */\ntemplate<class Iterable>\nLZ_NODISCARD constexpr detail::enable_if_t<!detail::is_sized<Iterable>::value, size_t> eager_size(Iterable&& c) {\n    using std::distance;\n    return static_cast<size_t>(distance(c.begin(), c.end()));\n}\n\n#endif\n\n/**\n * @brief Gets the signed size of an iterable. If the iterable is not sized, it will calculate the size by iterating over the\n * iterable, which is O(n), unless it is random access. Example:\n * ```cpp\n * // Sized, making it O(1)\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * std::cout << lz::eager_ssize(vec) << '\\n'; // prints 5\n *\n * // Not sized, not random access, making it O(n)\n * auto not_sized = lz::c_string(\"Hello\");\n * std::cout << lz::eager_ssize(not_sized) << '\\n'; // prints 5\n * ```\n *\n * @param c The iterable to get the size of.\n * @return The size of the iterable.\n */\ntemplate<class Iterable>\nLZ_NODISCARD constexpr detail::diff_iterable_t<Iterable> eager_ssize(Iterable&& c) {\n    return static_cast<detail::diff_iterable_t<Iterable>>(eager_size(std::forward<Iterable>(c)));\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/procs/procs.hpp",
    "content": "#pragma once\n\n#include \"chain.hpp\"\n#include \"distance.hpp\"\n#include \"eager_size.hpp\"\n#include \"size.hpp\"\n#include \"to.hpp\"\n"
  },
  {
    "path": "include/Lz/procs/size.hpp",
    "content": "\n#pragma once\n\n#ifndef LZ_SIZE_HPP\n#define LZ_SIZE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <type_traits>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Gets the unsigned size of a iterable or iterable if it has a .size() method. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * std::cout << lz::size(vec) << '\\n'; // prints 5\n * ```\n *\n * @param i The iterable to get the size from.\n * @return The size of the iterable.\n */\ntemplate<class Iterable>\nLZ_NODISCARD constexpr auto size(const Iterable& i) noexcept(noexcept(i.size())) -> decltype(i.size()) {\n    return i.size();\n}\n\n/**\n * @brief Gets the unsigned size of a container or iterable if it has a .size() method. Example:\n * ```cpp\n * int arr[] = { 1, 2, 3, 4, 5 };\n * std::cout << lz::size(arr) << '\\n'; // prints 5\n * ```\n *\n * @param c The container to get the size from.\n * @return The size of the container.\n */\ntemplate<class T, detail::size_t N>\nLZ_NODISCARD constexpr detail::size_t size(const T(&c)[N]) noexcept {\n    return static_cast<void>(c), N;\n}\n\n/**\n * @brief Gets the signed size of a container or iterable if it has a .size() method. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * std::cout << lz::ssize(vec) << '\\n'; // prints 5\n * ```\n *\n * @param i The container to get the size from.\n * @return The size of the container.\n */\ntemplate<class Iterable>\nLZ_NODISCARD constexpr auto ssize(const Iterable& i) noexcept(noexcept(\n    static_cast<typename std::common_type<std::ptrdiff_t, typename std::make_signed<decltype(lz::size(i))>::type>::type>(lz::size(i))))\n    -> typename std::common_type<std::ptrdiff_t, typename std::make_signed<decltype(lz::size(i))>::type>::type {\n    using T = typename std::common_type<std::ptrdiff_t, typename std::make_signed<decltype(lz::size(i))>::type>::type;\n    return static_cast<T>(lz::size(i));\n}\n\n/**\n * @brief Gets the signed size of a container or iterable if it has a .size() method. Example:\n * ```cpp\n * int arr[] = { 1, 2, 3, 4, 5 };\n * std::cout << lz::ssize(arr) << '\\n'; // prints 5\n * ```\n *\n * @return The size of the container.\n */\ntemplate<class T, detail::size_t N>\nLZ_NODISCARD constexpr detail::ptrdiff_t ssize(const T(&)[N]) noexcept {\n    return static_cast<detail::ptrdiff_t>(N);\n}\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/procs/to.hpp",
    "content": "#pragma once\n\n#ifndef LZ_PROCS_TO_HPP\n#define LZ_PROCS_TO_HPP\n\n#include <Lz/algorithm/copy.hpp>\n#include <Lz/algorithm/for_each.hpp>\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/enable_if.hpp>\n#include <Lz/detail/traits/first_arg.hpp>\n#include <Lz/detail/traits/is_iterable.hpp>\n#include <Lz/detail/traits/iterator_categories.hpp>\n#include <Lz/detail/traits/remove_ref.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/detail/traits/void.hpp>\n#include <Lz/procs/size.hpp>\n\n#ifdef LZ_HAS_CONCEPTS\n\n#include <Lz/traits/concepts.hpp>\n\n#endif\n\nnamespace lz {\nnamespace detail {\n\ntemplate<class Iterable, class Container, class = void>\nstruct prealloc_container {\n    LZ_CONSTEXPR_CXX_14 void try_reserve(const Iterable&, const Container&) const noexcept {\n    }\n};\n\ntemplate<class Iterable, class Container>\nstruct prealloc_container<Iterable, Container,\n                          void_t<decltype(lz::size(std::declval<Iterable>()), std::declval<Container>().reserve(0))>> {\n    LZ_CONSTEXPR_CXX_20 void try_reserve(const Iterable& iterable, Container& container) const {\n        container.reserve(lz::size(iterable));\n    }\n};\n\n#ifndef LZ_HAS_CXX_17\n\ntemplate<class Container, class = void>\nstruct has_insert_after : std::false_type {};\n\ntemplate<class Container>\nstruct has_insert_after<Container,\n                        void_t<decltype(std::declval<Container>().insert_after(std::declval<typename Container::const_iterator>(),\n                                                                               std::declval<typename Container::value_type>()))>>\n    : std::true_type {};\n\ntemplate<class Container, class = void>\nstruct has_insert : std::false_type {};\n\ntemplate<class Container>\nstruct has_insert<Container, void_t<decltype(std::declval<Container>().insert(std::declval<typename Container::iterator>(),\n                                                                              std::declval<typename Container::value_type>()))>>\n    : std::true_type {};\n\ntemplate<class Container, class = void>\nstruct has_push_back : std::false_type {};\n\ntemplate<class Container>\nstruct has_push_back<Container,\n                     void_t<decltype(std::declval<Container>().push_back(std::declval<typename Container::value_type>()))>>\n    : std::true_type {};\n\ntemplate<class Container, class = void>\nstruct has_push : std::false_type {};\n\ntemplate<class Container>\nstruct has_push<Container, void_t<decltype(std::declval<Container>().push(std::declval<typename Container::value_type>()))>>\n    : std::true_type {};\n\n#else\n\ntemplate<class T, class = void>\ninline constexpr bool has_push_back_v = false;\n\ntemplate<class T>\ninline constexpr bool has_push_back_v<T, void_t<decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))>> =\n    true;\n\ntemplate<class T, class = void>\ninline constexpr bool has_insert_v = false;\n\ntemplate<class T>\ninline constexpr bool has_insert_v<\n    T, void_t<decltype(std::declval<T>().insert(std::declval<typename T::iterator>(), std::declval<typename T::value_type>()))>> =\n    true;\n\ntemplate<class T, class = void>\ninline constexpr bool has_insert_after_v = false;\n\ntemplate<class T>\ninline constexpr bool\n    has_insert_after_v<T, void_t<decltype(std::declval<T>().insert_after(std::declval<typename T::const_iterator>(),\n                                                                         std::declval<typename T::value_type>()))>> = true;\n\ntemplate<class T, class = void>\ninline constexpr bool has_push_v = false;\n\ntemplate<class T>\ninline constexpr bool has_push_v<T, void_t<decltype(std::declval<T>().push(std::declval<typename T::value_type>()))>> = true;\n\n#endif\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`. Can be specialized for custom containers.\n * Example:\n * ```cpp\n * template<class T>\n * class my_container {\n *     void my_inserter(T t) { ... }\n * };\n *\n * template<class T>\n * lz::custom_copier_for<my_container<T>> {\n *     template<class Iterable>\n *     void copy(Iterable&& iterable, my_container<T>& container) {\n *         // Copy the iterable to the container, for example:\n *         // Container is not reserved if it contains a reserve member\n *         for (auto&& i : iterable) {\n *             container.my_inserter(i);\n *         }\n *     }\n * };\n *\n * // or you can use enable if\n * template<class T>\n * lz::custom_copier_for<my_container<T>, std::enable_if_t_t<...>> {\n *     // Same as above\n * };\n * ```\n */\nLZ_MODULE_EXPORT template<class Container, class = void>\nstruct custom_copier_for {\n    template<class Iterable>\n    LZ_CONSTEXPR_CXX_14 void copy(Iterable&& iterable, Container& container) const {\n        lz::copy(std::forward<Iterable>(iterable), container.begin());\n    }\n};\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class Iterable, class Container>\nLZ_CONSTEXPR_CXX_20 void copy_to_container(Iterable&& iterable, Container& container) {\n    if constexpr (has_push_back_v<Container>) {\n        prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n        lz::copy(std::forward<Iterable>(iterable), std::back_inserter(container));\n    }\n    else if constexpr (has_insert_v<Container>) {\n        prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n        lz::copy(std::forward<Iterable>(iterable), std::inserter(container, container.begin()));\n    }\n    else if constexpr (has_push_v<Container>) {\n        using ref = ref_iterable_t<Iterable>;\n        prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n        lz::for_each(std::forward<Iterable>(iterable), [&container](ref value) { container.push(value); });\n    }\n    else if constexpr (has_insert_after_v<Container>) {\n        using ref = ref_iterable_t<Iterable>;\n        prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n        auto it = container.before_begin();\n        lz::for_each(std::forward<Iterable>(iterable),\n                     [&container, it](ref value) mutable { it = container.insert_after(it, value); });\n    }\n    else {\n        custom_copier_for<Container>{}.copy(std::forward<Iterable>(iterable), container);\n    }\n}\n\n#else\n\n// Container has:\n// - push_back (use push_back)\n// - insert\n// - has_insert_after\ntemplate<class Iterable, class Container>\nLZ_CONSTEXPR_CXX_20 enable_if_t<has_push_back<Container>::value && has_insert<Container>::value &&\n                                has_insert_after<Container>::value && !has_push<Container>::value>\ncopy_to_container(Iterable&& iterable, Container& container) {\n    prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n    lz::copy(std::forward<Iterable>(iterable), std::back_inserter(container));\n}\n\n// Container has:\n// - push_back (use push_back)\n// - insert\ntemplate<class Iterable, class Container>\nLZ_CONSTEXPR_CXX_20 enable_if_t<has_push_back<Container>::value && has_insert<Container>::value &&\n                                !has_insert_after<Container>::value && !has_push<Container>::value>\ncopy_to_container(Iterable&& iterable, Container& container) {\n    prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n    lz::copy(std::forward<Iterable>(iterable), std::back_inserter(container));\n}\n\n// Container has:\n// - insert (use insert)\n// - insert_after\ntemplate<class Iterable, class Container>\nLZ_CONSTEXPR_CXX_20 enable_if_t<!has_push_back<Container>::value && has_insert<Container>::value &&\n                                has_insert_after<Container>::value && !has_push<Container>::value>\ncopy_to_container(Iterable&& iterable, Container& container) {\n    prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n    lz::copy(std::forward<Iterable>(iterable), std::inserter(container, container.begin()));\n}\n\n// Container has:\n// - insert_after (use insert_after)\ntemplate<class Iterable, class Container>\nLZ_CONSTEXPR_CXX_20 enable_if_t<!has_push_back<Container>::value && !has_insert<Container>::value &&\n                                has_insert_after<Container>::value && !has_push<Container>::value>\ncopy_to_container(Iterable&& iterable, Container& container) {\n    using ref = ref_iterable_t<Iterable>;\n    prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n    auto it = container.before_begin();\n    lz::for_each(std::forward<Iterable>(iterable),\n                 [&container, it](ref value) mutable { it = container.insert_after(it, value); });\n}\n\n// Container has:\n// - insert (use insert)\ntemplate<class Iterable, class Container>\nLZ_CONSTEXPR_CXX_20 enable_if_t<!has_push_back<Container>::value && has_insert<Container>::value &&\n                                !has_insert_after<Container>::value && !has_push<Container>::value>\ncopy_to_container(Iterable&& iterable, Container& container) {\n    prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n    lz::copy(std::forward<Iterable>(iterable), std::inserter(container, container.begin()));\n}\n\n// Container has:\n// - push (use push)\ntemplate<class Iterable, class Container>\nLZ_CONSTEXPR_CXX_20 enable_if_t<!has_push_back<Container>::value && !has_insert<Container>::value &&\n                                !has_insert_after<Container>::value && has_push<Container>::value>\ncopy_to_container(Iterable&& iterable, Container& container) {\n    using ref = ref_iterable_t<Iterable>;\n    prealloc_container<Iterable, Container>{}.try_reserve(iterable, container);\n    lz::for_each(std::forward<Iterable>(iterable), [&container](ref value) { container.push(value); });\n}\n\n// Last resort: Container has no push_back, insert, insert_after, or push, use custom copier\ntemplate<class Iterable, class Container>\nLZ_CONSTEXPR_CXX_20 enable_if_t<!has_push_back<Container>::value && !has_insert<Container>::value &&\n                                !has_insert_after<Container>::value && !has_push<Container>::value>\ncopy_to_container(Iterable&& iterable, Container& container) {\n    custom_copier_for<Container>{}.copy(std::forward<Iterable>(iterable), container);\n}\n\n#endif // LZ_HAS_CXX_17\n\ntemplate<class Container>\nstruct container_constructor {\n#ifdef LZ_HAS_CXX_17\n\n    template<class Iterable, class... Args>\n    [[nodiscard]] static constexpr Container construct(Iterable&& iterable, Args&&... args) {\n        using it = iter_t<Iterable>;\n        if constexpr (std::is_constructible<Container, Iterable, remove_cvref_t<Args>...>::value) {\n            return Container{ std::forward<Iterable>(iterable), std::forward<Args>(args)... };\n        }\n        else if constexpr (std::is_constructible_v<Container, it, sentinel_t<Iterable>, remove_cvref_t<Args>...>) {\n            return Container{ detail::begin(iterable), detail::end(iterable), std::forward<Args>(args)... };\n        }\n        else if constexpr (is_ra_v<it> && std::is_constructible_v<Container, it, it, remove_cvref_t<Args>...>) {\n            auto iter = detail::begin(iterable);\n            return Container{ iter, iter + (detail::end(iterable) - detail::begin(iterable)), std::forward<Args>(args)... };\n        }\n        else {\n            Container container{ std::forward<Args>(args)... };\n            copy_to_container(std::forward<Iterable>(iterable), container);\n            return container;\n        }\n    }\n\n#else\n\n    template<class Iterable, class... Args>\n    using constructible_from_it_sent =\n        std::is_constructible<Container, iter_t<Iterable>, sentinel_t<Iterable>, remove_cvref_t<Args>...>;\n\n    template<class Iterable, class... Args>\n    using constructible_from_it_it =\n        std::is_constructible<Container, iter_t<Iterable>, iter_t<Iterable>, remove_cvref_t<Args>...>;\n\n    template<class Iterable, class... Args>\n    using constructible_from_iterable = std::is_constructible<Container, Iterable, remove_cvref_t<Args>...>;\n\n    template<class Iterable, class... Args>\n    LZ_NODISCARD static constexpr enable_if_t<constructible_from_iterable<Iterable, Args...>::value, Container>\n    construct(Iterable&& iterable, Args&&... args) {\n        return Container{ std::forward<Iterable>(iterable), std::forward<Args>(args)... };\n    }\n\n    template<class Iterable, class... Args>\n    LZ_NODISCARD static constexpr enable_if_t<\n        !constructible_from_iterable<Iterable, Args...>::value && constructible_from_it_sent<Iterable, Args...>::value, Container>\n    construct(Iterable&& iterable, Args&&... args) {\n        return Container{ detail::begin(iterable), detail::end(iterable), std::forward<Args>(args)... };\n    }\n\n    template<class Iterable, class... Args>\n    LZ_NODISCARD static LZ_CONSTEXPR_CXX_14\n        enable_if_t<!constructible_from_iterable<Iterable, Args...>::value &&\n                        !constructible_from_it_sent<Iterable, Args...>::value &&\n                        constructible_from_it_it<Iterable, Args...>::value && is_ra<iter_t<Iterable>>::value,\n                    Container>\n        construct(Iterable&& iterable, Args&&... args) {\n        auto it = detail::begin(iterable);\n        return Container{ it, it + (detail::end(iterable) - detail::begin(iterable)), std::forward<Args>(args)... };\n    }\n\n    template<class Iterable, class... Args>\n    LZ_NODISCARD static LZ_CONSTEXPR_CXX_14\n        enable_if_t<!constructible_from_iterable<Iterable, Args...>::value &&\n                        !constructible_from_it_sent<Iterable, Args...>::value &&\n                        (!constructible_from_it_it<Iterable, Args...>::value || !is_ra<iter_t<Iterable>>::value),\n                    Container>\n        construct(Iterable&& iterable, Args&&... args) {\n        Container container{ std::forward<Args>(args)... };\n        copy_to_container(std::forward<Iterable>(iterable), container);\n        return container;\n    }\n\n#endif\n};\n\ntemplate<class Container>\nstruct to_adaptor {\n    using adaptor = to_adaptor<Container>;\n\n    template<class Iterable, class... Args>\n    LZ_NODISCARD constexpr Container operator()(Iterable&& iterable, Args&&... args) const {\n        return container_constructor<Container>::construct(std::forward<Iterable>(iterable), std::forward<Args>(args)...);\n    }\n};\n\ntemplate<template<class...> class Container>\nstruct template_combiner {\n    using adaptor = template_combiner<Container>;\n\n    template<class Iterable, class... Args>\n    LZ_NODISCARD constexpr Container<val_iterable_t<Iterable>, typename std::decay<Args>::type...>\n    operator()(Iterable&& iterable, Args&&... args) const {\n        using C = Container<val_iterable_t<Iterable>, typename std::decay<Args>::type...>;\n        return to_adaptor<C>{}(std::forward<Iterable>(iterable), std::forward<Args>(args)...);\n    }\n};\n} // namespace detail\n} // namespace lz\n\nLZ_MODULE_EXPORT namespace lz {\n\n    // clang-format off\n\n#ifdef LZ_HAS_CONCEPTS\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`. Can be used in a pipe expression.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto list = vec | lz::to<std::list>(); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list>>(std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list<int>>(); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list<int>>(std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // etc...\n * ```\n * @attention Containers like `std::array `cannot be passed as `to<std::array>` since it requires a size. Instead just use\n * `to<std::array<T, N>>(iterable)` or `iterable | to<std::array<T, N>>()`\n * @tparam Container The container to convert to\n * @param args Args passed directly to the constructor of Container, except begin and end\n * @return The `Container`\n */\ntemplate<class Container, class... Args>\n[[nodiscard]] constexpr detail::fn_args_holder<detail::to_adaptor<Container>, std::decay_t<Args>...>\nto(Args&&... args)\n    requires(!lz::iterable<detail::first_arg_t<Args...>>)\n{\n    using Closure = detail::fn_args_holder<detail::to_adaptor<Container>, std::decay_t<Args>...>;\n    return Closure{ std::forward<Args>(args)... };\n}\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`. Can be used in a pipe expression.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto list = vec | lz::to<std::list>(); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list>>(std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list<int>>(); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list<int>>(std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // etc...\n * ```\n * @attention Containers like `std::array `cannot be passed as `to<std::array>` since it requires a size. Instead just use\n * `to<std::array<T, N>>(iterable)` or `iterable | to<std::array<T, N>>()`\n * @tparam Container The container to convert to\n * @param args Args passed directly to the constructor of Container, except begin and end\n * @return The `Container`\n */\ntemplate<template<class...> class Container, class... Args>\n[[nodiscard]] constexpr detail::fn_args_holder<detail::template_combiner<Container>, std::decay_t<Args>...>\nto(Args&&... args)\n    requires(!lz::iterable<detail::first_arg_t<Args...>>)\n{\n    using Closure = detail::fn_args_holder<detail::template_combiner<Container>, std::decay_t<Args>...>;\n    return Closure{ std::forward<Args>(args)... };\n}\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto list = lz::to<std::list>(vec); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list>(vec, std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list<int>>(vec); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list<int>>(vec, std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // etc...\n * ```\n * In case you have a custom container, you can specialize `lz::custom_copier_for` to copy the elements to your container.\n * This is useful if you have a custom container that requires a specific way of copying elements. You will need to specialize\n * `lz::custom_copier_for` for your custom container if all of the following are true:\n * - Your custom container does not have a constructor matching:\n *   - `your_container(iterable, args...)`\n *   - `your_container(begin, end, args...)` where begin and end may or may not be the same type\n *   - `your_container(begin, begin + size, args...)` (only if the iterable is random access)\n * - Your custom container does not have a `push_back` method\n * - Your custom container does not have an `insert` method\n * - Your custom container does not have an `insert_after` method (implicitly also requires `before_begin`)\n * - Your custom container does not have a `push` method\n * - Your custom container is not std::array\n * ```cpp\n * template<class T>\n * class custom_container {\n *    std::vector<T> _vec;\n *  public:\n *      void reserve(size_t size) {\n *         _vec.reserve(size);\n *      }\n *\n *      std::vector<T>& vec() {\n *         return _vec;\n *      }\n * };\n *\n * // Specialize `lz::custom_copier_for` for your custom container\n * template<class T>\n * struct lz::custom_copier_for<custom_container<T>> {\n *    template<class Iterable>\n *    void copy(Iterable&& iterable, custom_container<T>& container) const {\n *      // Copy the contents of the iterable to the container. Container is not reserved\n *      // if it contains a reserve member\n *      lz::copy(std::forward<Iterable>(iterable), std::back_inserter(container.vec()));\n *    }\n * ```\n * @attention Containers like `std::array `cannot be passed as `to<std::array>` since it requires a size. Instead just use\n * `to<std::array<T, N>>(iterable)` or `iterable | to<std::array<T, N>>()`\n * @tparam Container The container to convert to\n * @param iterable The iterable to convert to a container\n * @param args Args passed directly to the constructor of Container, except begin and end\n * @return The `Container`\n */\ntemplate<class Container, class... Args, class Iterable>\n[[nodiscard]] constexpr Container to(Iterable&& iterable, Args&&... args)\n    requires(lz::iterable<Iterable>)\n{\n    return detail::to_adaptor<Container>{}(std::forward<Iterable>(iterable), std::forward<Args>(args)...);\n}\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto list = lz::to<std::list>(vec); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list>(vec, std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list<int>>(vec); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list<int>>(vec, std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // etc...\n * ```\n * In case you have a custom container, you can specialize `lz::custom_copier_for` to copy the elements to your container.\n * This is useful if you have a custom container that requires a specific way of copying elements. You will need to specialize\n * `lz::custom_copier_for` for your custom container if all of the following are true:\n * - Your custom container does not have a constructor matching:\n *   - `your_container(iterable, args...)`\n *   - `your_container(begin, end, args...)` where begin and end may or may not be the same type\n *   - `your_container(begin, begin + size, args...)` (only if the iterable is random access)\n * - Your custom container does not have a `push_back` method\n * - Your custom container does not have an `insert` method\n * - Your custom container does not have an `insert_after` method (implicitly also requires `before_begin`)\n * - Your custom container does not have a `push` method\n * - Your custom container is not std::array\n * ```cpp\n * template<class T>\n * class custom_container {\n *    std::vector<T> _vec;\n *  public:\n *      void reserve(size_t size) {\n *         _vec.reserve(size);\n *      }\n *\n *      std::vector<T>& vec() {\n *         return _vec;\n *      }\n * };\n *\n * // Specialize `lz::custom_copier_for` for your custom container\n * template<class T>\n * struct lz::custom_copier_for<custom_container<T>> {\n *    template<class Iterable>\n *    void copy(Iterable&& iterable, custom_container<T>& container) const {\n *      // Copy the contents of the iterable to the container. Container is not reserved\n *      // if it contains a reserve member\n *      lz::copy(std::forward<Iterable>(iterable), std::back_inserter(container.vec()));\n *    }\n * ```\n * @attention Containers like `std::array `cannot be passed as `to<std::array>` since it requires a size. Instead just use\n * `to<std::array<T, N>>(iterable)` or `iterable | to<std::array<T, N>>()`\n * @tparam Container The container to convert to\n * @param iterable The iterable to convert to a container\n * @param args Args passed directly to the constructor of Container, except begin and end\n * @return The `Container`\n */\ntemplate<template<class...> class Container, class... Args, class Iterable>\n[[nodiscard]] constexpr Container<detail::val_iterable_t<Iterable>, Args...>\nto(Iterable&& iterable, Args&&... args)\n    requires(lz::iterable<Iterable>)\n{\n    using Cont = Container<detail::val_iterable_t<Iterable>, std::decay_t<Args>...>;\n    return to<Cont>(std::forward<Iterable>(iterable), std::forward<Args>(args)...);\n}\n\n#else\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`. Can be used in a pipe expression.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto list = vec | lz::to<std::list>(); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list>>(std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list<int>>(); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list<int>>(std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // etc...\n * ```\n * @attention Containers like `std::array `cannot be passed as `to<std::array>` since it requires a size. Instead just use\n * `to<std::array<T, N>>(iterable)` or `iterable | to<std::array<T, N>>()`\n * @tparam Container The container to convert to\n * @param args Args passed directly to the constructor of Container, except begin and end\n * @return The `Container`\n */\ntemplate<class Container, class... Args>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14\ndetail::enable_if_t<!detail::is_iterable<detail::first_arg_t<Args...>>::value,\n    detail::fn_args_holder<detail::to_adaptor<Container>, typename std::decay<Args>::type...>>\nto(Args&&... args) {\n    using Closure = detail::fn_args_holder<detail::to_adaptor<Container>, typename std::decay<Args>::type...>;\n    return Closure{ std::forward<Args>(args)... };\n}\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`. Can be used in a pipe expression.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto list = vec | lz::to<std::list>(); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list>>(std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list<int>>(); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = vec | lz::to<std::list<int>>(std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // etc...\n * ```\n * @attention Containers like `std::array `cannot be passed as `to<std::array>` since it requires a size. Instead just use\n * `to<std::array<T, N>>(iterable)` or `iterable | to<std::array<T, N>>()`\n * @tparam Container The container to convert to\n * @param args Args passed directly to the constructor of Container, except begin and end\n * @return The `Container`\n */\ntemplate<template<class...> class Container, class... Args>\nLZ_NODISCARD LZ_CONSTEXPR_CXX_14\ndetail::enable_if_t<!detail::is_iterable<detail::first_arg_t<Args...>>::value, \n    detail::fn_args_holder<detail::template_combiner<Container>,  typename std::decay<Args>::type...>> \nto(Args&&... args) {\n    using Closure = detail::fn_args_holder<detail::template_combiner<Container>,  typename std::decay<Args>::type...>;\n    return Closure{ std::forward<Args>(args)... };\n}\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto list = lz::to<std::list>(vec); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list>(vec, std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list<int>>(vec); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list<int>>(vec, std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // etc...\n * ```\n * In case you have a custom container, you can specialize `lz::custom_copier_for` to copy the elements to your container.\n * This is useful if you have a custom container that requires a specific way of copying elements. You will need to specialize\n * `lz::custom_copier_for` for your custom container if all of the following are true:\n * - Your custom container does not have a constructor matching:\n *   - `your_container(iterable, args...)`\n *   - `your_container(begin, end, args...)` where begin and end may or may not be the same type\n *   - `your_container(begin, begin + size, args...)` (only if the iterable is random access)\n * - Your custom container does not have a `push_back` method\n * - Your custom container does not have an `insert` method\n * - Your custom container does not have an `insert_after` method (implicitly also requires `before_begin`)\n * - Your custom container does not have a `push` method\n * - Your custom container is not std::array\n * ```cpp\n * template<class T>\n * class custom_container {\n *    std::vector<T> _vec;\n *  public:\n *      void reserve(size_t size) {\n *         _vec.reserve(size);\n *      }\n *\n *      std::vector<T>& vec() {\n *         return _vec;\n *      }\n * };\n *\n * // Specialize `lz::custom_copier_for` for your custom container\n * template<class T>\n * struct lz::custom_copier_for<custom_container<T>> {\n *    template<class Iterable>\n *    void copy(Iterable&& iterable, custom_container<T>& container) const {\n *      // Copy the contents of the iterable to the container. Container is not reserved\n *      // if it contains a reserve member\n *      lz::copy(std::forward<Iterable>(iterable), std::back_inserter(container.vec()));\n *    }\n * ```\n * @attention Containers like `std::array `cannot be passed as `to<std::array>` since it requires a size. Instead just use\n * `to<std::array<T, N>>(iterable)` or `iterable | to<std::array<T, N>>()`\n * @tparam Container The container to convert to\n * @param iterable The iterable to convert to a container\n * @param args Args passed directly to the constructor of Container, except begin and end\n * @return The `Container`\n */\ntemplate<class Container, class... Args, class Iterable>\nLZ_NODISCARD constexpr detail::enable_if_t<detail::is_iterable<Iterable>::value, Container>\nto(Iterable&& iterable, Args&&... args) {\n    return detail::to_adaptor<Container>{}(std::forward<Iterable>(iterable), std::forward<Args>(args)...);\n}\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`.\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto list = lz::to<std::list>(vec); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list>(vec, std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list<int>>(vec); // { 1, 2, 3, 4, 5 }\n * // or\n * auto list = lz::to<std::list<int>>(vec, std::allocator<int>{}); // { 1, 2, 3, 4, 5 }\n * // etc...\n * ```\n * In case you have a custom container, you can specialize `lz::custom_copier_for` to copy the elements to your container.\n * This is useful if you have a custom container that requires a specific way of copying elements. You will need to specialize\n * `lz::custom_copier_for` for your custom container if all of the following are true:\n * - Your custom container does not have a constructor matching:\n *   - `your_container(iterable, args...)`\n *   - `your_container(begin, end, args...)` where begin and end may or may not be the same type\n *   - `your_container(begin, begin + size, args...)` (only if the iterable is random access)\n * - Your custom container does not have a `push_back` method\n * - Your custom container does not have an `insert` method\n * - Your custom container does not have an `insert_after` method (implicitly also requires `before_begin`)\n * - Your custom container does not have a `push` method\n * - Your custom container is not std::array\n * ```cpp\n * template<class T>\n * class custom_container {\n *    std::vector<T> _vec;\n *  public:\n *      void reserve(size_t size) {\n *         _vec.reserve(size);\n *      }\n *\n *      std::vector<T>& vec() {\n *         return _vec;\n *      }\n * };\n *\n * // Specialize `lz::custom_copier_for` for your custom container\n * template<class T>\n * struct lz::custom_copier_for<custom_container<T>> {\n *    template<class Iterable>\n *    void copy(Iterable&& iterable, custom_container<T>& container) const {\n *      // Copy the contents of the iterable to the container. Container is not reserved\n *      // if it contains a reserve member\n *      lz::copy(std::forward<Iterable>(iterable), std::back_inserter(container.vec()));\n *    }\n * ```\n * @attention Containers like `std::array `cannot be passed as `to<std::array>` since it requires a size. Instead just use\n * `to<std::array<T, N>>(iterable)` or `iterable | to<std::array<T, N>>()`\n * @tparam Container The container to convert to\n * @param iterable The iterable to convert to a container\n * @param args Args passed directly to the constructor of Container, except begin and end\n * @return The `Container`\n */\ntemplate<template<class...> class Container, class... Args, class Iterable>\nLZ_NODISCARD constexpr detail::enable_if_t<detail::is_iterable<Iterable>::value, Container<detail::val_iterable_t<Iterable>, Args...>>\nto(Iterable&& iterable, Args&&... args) {\n    using Cont = Container<detail::val_iterable_t<Iterable>, typename std::decay<Args>::type...>;\n    return to<Cont>(std::forward<Iterable>(iterable), std::forward<Args>(args)...);\n}\n\n#endif\n\n/**\n * @brief Converts an iterable to a container, given template parameter `Container`. Can be specialized for custom containers.\n * Example:\n * ```cpp\n * template<class T>\n * class my_container {\n *     void my_inserter(T t) { ... }\n * };\n *\n * template<class T>\n * lz::custom_copier_for<my_container<T>> {\n *     template<class Iterable>\n *     void copy(Iterable&& iterable, my_container<T>& container) {\n *         // Copy the iterable to the container, for example:\n *         // Container is not reserved if it contains a reserve member\n *         for (auto&& i : iterable) {\n *             container.my_inserter(i);\n *         }\n *     }\n * };\n *\n * // or you can use enable if\n * template<class T>\n * lz::custom_copier_for<my_container<T>, std::enable_if_t<...>> {\n *     // Same as above\n * };\n * ```\n */\nusing detail::custom_copier_for;\n\n} // namespace lz\n\n#endif // LZ_PROCS_TO_HPP\n"
  },
  {
    "path": "include/Lz/random.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_RANDOM_HPP\r\n#define LZ_RANDOM_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/random.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Creates n random numbers in the range [min, max]. It contains a .size() method, is random access and has a sentinel. The\r\n * callable contains various amount of overloads. Example:\r\n * ```cpp\r\n * // overload 1. Uses std::uniform_real_distribution<double> as distribution, a seed length of 8 random numbers (using\r\n * // std::random_device) and a mt19937 engine.\r\n * auto random = lz::random(0., 1., 5); // random = { 0.1, 0.2, 0.3, 0.4, 0.5 } (random * numbers)\r\n *\r\n * // overload 2 Uses std::uniform_int_distribution<int> as distribution, a seed length of 8 random numbers (using\r\n * // std::random_device) and a mt19937 engine.\r\n * auto random = lz::random(0, 10, 5); // random = { 1, 2, 3, 4, 5 } (random numbers)\r\n *\r\n * // overload 3. Uses a custom distribution, a custom engine and a custom amount of random numbers.\r\n * std::mt19937 gen;\r\n * std::uniform_real_distribution<double> dist(0., 1.);\r\n * auto random = lz::random(dist, gen, 5); // random = { 0.1, 0.2, 0.3, 0.4, 0.5 } (random * numbers)\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::random_adaptor<true> random{};\r\n\r\n/**\r\n * @brief Creates n random numbers in the range [min, max]. It contains a .size() method, is random access and does NOT have a\r\n * sentinel. The callable contains various amount of overloads. Example:\r\n * ```cpp\r\n * // overload 1. Uses std::uniform_real_distribution<double> as distribution, a seed length of 8 random numbers (using\r\n * // std::random_device) and a mt19937 engine.\r\n * auto random = lz::common_random(0., 1., 5); // random = { 0.1, 0.2, 0.3, 0.4, 0.5 } (random * numbers)\r\n *\r\n * // overload 2 Uses std::uniform_int_distribution<int> as distribution, a seed length of 8 random numbers (using\r\n * // std::random_device) and a mt19937 engine.\r\n * auto random = lz::common_random(0, 10, 5); // random = { 1, 2, 3, 4, 5 } (random numbers)\r\n *\r\n * // overload 3. Uses a custom distribution, a custom engine and a custom amount of random numbers.\r\n * std::mt19937 gen;\r\n * std::uniform_real_distribution<double> dist(0., 1.);\r\n * auto random = lz::common_random(dist, gen, 5); // random = { 0.1, 0.2, 0.3, 0.4, 0.5 } (random * numbers)\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::random_adaptor<false> common_random{};\r\n\r\n/**\r\n * @brief Common random iterable helper alias.\r\n * @tparam Arithmetic The arithmetic type of the random numbers.\r\n * @tparam Distribution The distribution type used to generate the random numbers.\r\n * @tparam Generator The random number generator type.\r\n * ```cpp\r\n * std::poisson_distribution<> distribution(0, 1);\r\n * std::mt19937 generator;\r\n * using iterable = lz::common_random_iterable<int, std::poisson_distribution<>, std::mt19937>;\r\n * iterable = lz::common_random(distribution, generator, 10);\r\n * ```\r\n */\r\ntemplate<class Arithmetic, class Distribution, class Generator>\r\nusing common_random_iterable = detail::random_iterable<Arithmetic, Distribution, Generator, false>;\r\n\r\n/**\r\n * @brief Random iterable helper alias.\r\n * @tparam Arithmetic The arithmetic type of the random numbers.\r\n * @tparam Distribution The distribution type used to generate the random numbers.\r\n * @tparam Generator The random number generator type.\r\n * ```cpp\r\n * std::poisson_distribution<> distribution(0, 1);\r\n * std::mt19937 generator;\r\n * using iterable = lz::random_iterable<int, std::poisson_distribution<>, std::mt19937>;\r\n * iterable = lz::random(distribution, generator, 10);\r\n * ```\r\n */\r\ntemplate<class Arithmetic, class Distribution, class Generator>\r\nusing random_iterable = detail::random_iterable<Arithmetic, Distribution, Generator, true>;\r\n\r\n/**\r\n * @brief The default random iterable helper alias. Uses std::mt19937 as the default generator and std::uniform_*_distribution as\r\n * the distribution.\r\n *\r\n * @tparam Arithmetic The arithmetic type of the random numbers.\r\n * ```cpp\r\n * lz::default_random_iterable<double> random = lz::random(0., 1., 10);\r\n * lz::default_random_iterable<int> random = lz::random(0, 10, 10);\r\n * ```\r\n */\r\ntemplate<class Arithmetic>\r\nusing default_random_iterable = decltype(std::declval<detail::random_adaptor<true>>()(Arithmetic{}, Arithmetic{}, size_t{}));\r\n\r\n/**\r\n * @brief The default common random iterable helper alias. Uses std::mt19937 as the default generator and\r\n * std::uniform_*_distribution as the distribution.\r\n *\r\n * @tparam Arithmetic The arithmetic type of the random numbers.\r\n * ```cpp\r\n * lz::default_random_iterable<double> random = lz::common_random(0., 1., 10);\r\n * lz::default_random_iterable<int> random = lz::common_random(0, 10, 10);\r\n * ```\r\n */\r\ntemplate<class Arithmetic>\r\nusing common_default_random_iterable =\r\n    decltype(std::declval<detail::random_adaptor<false>>()(Arithmetic{}, Arithmetic{}, size_t{}));\r\n\r\n} // namespace lz\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/Lz/range.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_RANGE_HPP\r\n#define LZ_RANGE_HPP\r\n\r\n#include <Lz/detail/adaptors/range.hpp>\r\n#include <Lz/procs/to.hpp>\r\n\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Creates n amount of numbers starting from 0 (or specified otherwise). It is a forward iterable. It contains a .size()\r\n * method and is random access. Example:\r\n * ```\r\n * // This will create a range from 0 to 4\r\n * auto range = lz::range(5); // {0, 1, 2, 3, 4} (T = int)\r\n * // or, with first param start, second param end, third param step\r\n * auto range = lz::range(0.0, 1.0, 0.1); // {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9} (T = double)\r\n * // so this will :\r\n * auto range = lz::range(0.0, 1.0, 0.2); // {0.0, 0.2, 0.4, 0.6, 0.8} (T = double)\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::range_adaptor range{};\r\n\r\n/**\r\n * @brief Range helper alias type for range iterables with step.\r\n * @tparam Arithmetic The arithmetic type to use for the range.\r\n * ```cpp\r\n * lz::stepwise_range_iterable<int> r = lz::range(0, 10, 1);\r\n * ```\r\n */\r\ntemplate<class Arithmetic>\r\nusing stepwise_range_iterable = detail::range_iterable<Arithmetic, true>;\r\n\r\n/**\r\n * @brief Range helper alias type for range iterables without step.\r\n * @tparam Arithmetic The arithmetic type to use for the range.\r\n * ```cpp\r\n * lz::range_iterable<int> r = lz::range(0, 10);\r\n * ```\r\n */\r\ntemplate<class Arithmetic>\r\nusing range_iterable = detail::range_iterable<Arithmetic, false>;\r\n\r\n} // namespace lz\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/Lz/regex_split.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REGEX_SPLIT_HPP\n#define LZ_REGEX_SPLIT_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/regex_split.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Splits a string based on a regex. The regex must be by reference. The `begin()` and `end()` types are different, but\n * `end()` is not an 'actual' sentinel. Rather, its the same type as its input iterator `end()` type. For std::regex, this\n * means `end() == std::regex_token_iterator<>{}` and `begin() = lz::regex_split_iterator`. It does not contain a .size()\n * method and its iterator category is forward. Example:\n * ```cpp\n * std::regex r1(R\"(\\s+)\");\n * std::string s = \"    Hello, world! How are you?\";\n * auto splitter = lz::regex_split(s, r1); // { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" }\n * // or\n * auto splitter = s | lz::regex_split(r1); // { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" }\n * // you can also use your own regex iterators, as std::regex isn't that performant, example:\n * auto begin = std::sregex_token_iterator(s.begin(), s.end(), r1, -1); // -1 means it will skip the matches (which is\n * // exactly what we want), rather than returning the matches\n * auto end = std::sregex_token_iterator<>();\n * auto splitter = lz::regex_split(begin, end); // { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::regex_split_adaptor regex_split{};\n\n/**\n * @brief Regex split iterable helper alias.\n *\n * @tparam RegexTokenIter Type of the regex token iterator.\n * @tparam RegexTokenSentinel Type of the regex token sentinel, defaults to the same type as `RegexTokenIter`.\n * ```cpp\n * std::regex r1(R\"(\\s+)\");\n * std::string s = \"    Hello, world! How are you?\";\n * lz::regex_split_iterable<std::sregex_token_iterator> splitter = lz::regex_split(s, r1);\n * ```\n */\ntemplate<class RegexTokenIter, class RegexTokenSentinel = RegexTokenIter>\nusing regex_split_iterable = detail::regex_split_iterable<RegexTokenIter, RegexTokenSentinel>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/repeat.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REPEAT_HPP\n#define LZ_REPEAT_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/repeat.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Returns an element n (or infinite) times. It returns a sentinel as its end() iterator. It contains a .size() method\n * which is equal to the amount of times the element is repeated (if it is not infinite). Its iterator category is random\n * access. Will return the same reference type as the reference type passed. Example:\n * ```cpp\n * auto repeater = lz::repeat(20, 5); // {20, 20, 20, 20, 20}\n * // infinite variant\n * auto repeater = lz::repeat(20); // {20, 20, 20, ...}\n * auto val = 3;\n * auto repeater =  lz::repeat(val, 5); // {3, 3, 3, 3, 3}, by reference\n * auto repeater =  lz::repeat(std::as_const(val)); // {3, 3, 3, ...}, by const reference\n * \n * ```\n */\nLZ_INLINE_VAR constexpr detail::repeat_adaptor repeat{};\n\n/**\n * @brief Repeat infinite helper alias type.\n * @tparam T The type of the element to repeat.\n * ```cpp\n * lz::repeat_iterable_inf<int> iterable = lz::repeat(42);\n * ```\n */\ntemplate<class T>\nusing repeat_iterable_inf = detail::repeat_iterable<T, true>;\n\n/**\n * @brief Repeat finite helper alias type.\n * @tparam T The type of the element to repeat.\n * ```cpp\n * lz::repeat_iterable<int> iterable = lz::repeat(42, 3);\n * int val = 3;\n * lz::repeat_iterable_inf<int&> iterable = lz::repeat(val); // by reference\n * ```\n */\ntemplate<class T>\nusing repeat_iterable = detail::repeat_iterable<T, false>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/reverse.hpp",
    "content": "#pragma once\n\n#ifndef LZ_REVERSE_HPP\n#define LZ_REVERSE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/reverse.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Reverses the iterable. Contains a size method if the iterable has a size method. Example:\n * ```cpp\n * std::vector<int> v = { 1, 2, 3, 4, 5 };\n * auto reversed = lz::reverse(v); // { 5, 4, 3, 2, 1 }\n * // or\n * auto reversed = v | lz::reverse; // { 5, 4, 3, 2, 1 }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::reverse_adaptor<false> reverse{};\n\n/**\n * @brief Reverses the iterable with a caching mechanism. Normally, the std::reverse_iterator uses an operator-- in its operator*\n * to access the previous element, which can be inefficient for some iterables such as iterables that traverses multiple elements\n * in its operator-- (such as `lz::filter` or bidirectional `lz::take_every`). Say filter_iterator::operator-- traverses 10\n * elements to get to the previous element, then this means 20 elements will be traversed when operator-- and operator* are called\n * in a row. This iterable prevents that by using a caching mechanism.\n * ```cpp\n * std::vector<int> v = // very large vector ranging from 0 to 10.000.000\n * auto reversed_slow = v | lz::filter([](int i) { return i < 3; }) | lz::reverse; // Traverses 20.000.000 items.\n * auto reversed = v | lz::filter([](int i) { return i < 3; }) | lz::cached_reverse; // Traverses only 10.000.000 items.\n * ```\n */\nLZ_INLINE_VAR constexpr detail::reverse_adaptor<true> cached_reverse{};\n\n/**\n * @brief Reverse iterable helper alias.\n * @tparam Iterable The type of the iterable to reverse.\n * ```cpp\n * std::vector<int> v = { 1, 2, 3, 4, 5 };\n * lz::reverse_iterable<std::vector<int>> reversed = lz::reverse(v);\n * ```\n */\ntemplate<class Iterable>\nusing reverse_iterable = detail::reverse_iterable<Iterable, false>;\n\n/**\n * @brief Cached reverse iterable helper alias.\n * @tparam Iterable The type of the iterable to reverse.\n * ```cpp\n * std::vector<int> v = { 1, 2, 3, 4, 5 };\n * lz::cached_reverse_iterable<std::vector<int>> reversed = lz::cached_reverse(v);\n * ```\n */\ntemplate<class Iterable>\nusing cached_reverse_iterable = detail::reverse_iterable<Iterable, true>;\n\n} // namespace lz\n\n#endif // LZ_REVERSE_HPP\n"
  },
  {
    "path": "include/Lz/rotate.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ROTATE_HPP\n#define LZ_ROTATE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/rotate.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Rotates the input iterable by n elements. If `n` is greater than or equal to its size then the iterable will be empty.\n * Contains a .size() method if the input iterable also has a .size() method. Its iterator category is the same as the input\n * iterable. If the input iterable is forward or has a sentinel, it will return the end type of its input iterable, rather than a\n * rotate_iterator/default_sentinel_t. If start == size(iterable) then the iterable is empty. If n > iterable.size() then calling\n * begin()/end() is undefined. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto rotated = lz::rotate(vec, 2); // rotated = { 3, 4, 5, 1, 2 }\n * auto rotated = lz::rotate(vec, 50); // rotated = {}\n *\n * // or, in case input iterable is not forward:\n * auto str = lz::c_string(\"Hello, World!\");\n * auto rotated = lz::rotate(str, 7); // rotated = \"World!Hello, \"\n * // rotated.end() will be a sentinel, rather than an actual iterator, as rotated str is forward\n * ```\n */\nLZ_INLINE_VAR constexpr detail::rotate_adaptor rotate{};\n\n/**\n * @brief Rotate iterable helper alias.\n * @tparam Iterable Type of the iterable to rotate.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * lz::rotate_iterable<std::vector<int>> rotated = lz::rotate(vec, 2);\n * ```\n */\ntemplate<class Iterable>\nusing rotate_iterable = detail::rotate_iterable<Iterable>;\n\n} // namespace lz\n\n#endif // LZ_ROTATE_HPP\n"
  },
  {
    "path": "include/Lz/slice.hpp",
    "content": "#pragma once\n\n#ifndef LZ_SLICE_HPP\n#define LZ_SLICE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/slice.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief This adaptor is used to slice an iterable from `from` to `to`. The iterator category is the same as the input\n * iterator category. Its end() function will return the same iterator as its input iterable or a default_sentinel_t if the input\n * iterable is forward. If its input iterable has a .size() method, then this iterable will also have a .size() method.Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n * auto slice = lz::slice(vec, 2, 6); // slice = { 3, 4, 5, 6 }\n * // or\n * auto slice = vec | lz::slice(2, 6); // slice = { 3, 4, 5, 6 }\n * ```\n */\nLZ_INLINE_VAR constexpr detail::slice_adaptor slice{};\n\n/**\n * @brief Slice iterable helper alias.\n * @tparam Iterable The type of the iterable to slice.\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n * lz::slice_iterable<std::vector<int>> slice_vec = lz::slice(vec, 2, 6);\n * ```\n */\ntemplate<class Iterable>\nusing slice_iterable = detail::slice_iterable<Iterable>;\n\n} // namespace lz\n\n#endif // LZ_SLICE_HPP\n"
  },
  {
    "path": "include/Lz/split.hpp",
    "content": "#pragma once\n\n#ifndef LZ_STRING_SPLITTER_HPP\n#define LZ_STRING_SPLITTER_HPP\n\n#include <Lz/detail/adaptors/split.hpp>\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/procs/chain.hpp>\n#include <Lz/util/string_view.hpp>\n#include <string>\n\nLZ_MODULE_EXPORT namespace lz {\n\n#ifdef LZ_HAS_CXX_11\n\n/**\n * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n * the iterable does not contain a .size() method. The value type is equal to that of `ValueType`. `ValueType` constructor\n * must meet one of the following requirements: `ValueType(Iterator, Iterator)`,\n * `ValueType(<decltype(std::adressof(Iterator))>, size_t)`. The distance between the iterators are calculated using\n * std::distance, if the second constructor is used. Example:\n * ```cpp\n * std::string to_split = \"H d\";\n * auto splitter = to_split | lz::t_split<std::vector<char>>{}(\" \"); // { vector{'H'}, vector{'d'} }\n * // or\n * auto splitter = lz::t_split<std::vector<char>>{}(to_split, \" \"); // { vector{'H'}, vector{'d'} }\n *\n * // or\n * auto splitter = lz::t_split<std::vector<char>>{}(to_split, ' '); // { vector{'H'}, vector{'d'} }\n * // or\n * auto splitter = to_split | lz::t_split<std::vector<char>>{}(' '); // { vector{'H'}, vector{'d'} }\n * ```\n * @tparam ValueType The value type of the iterator to return, for example `std::vector<int>`.\n */\ntemplate<class ValueType>\nusing t_split = detail::split_adaptor<ValueType>;\n\n#else\n\n/**\n * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and\n * the iterable does not contain a .size() method. The value type is equal to that of `ValueType`. `ValueType` constructor\n * must meet one of the following requirements: `ValueType(Iterator, Iterator)`,\n * `ValueType(<decltype(std::adressof(Iterator))>, size_t)`. The distance between the iterators are calculated using\n * std::distance, if the second constructor is used. Example:\n * ```cpp\n * std::string to_split = \"H d\";\n * auto splitter = to_split | lz::t_split<std::vector<char>>(\" \"); // { vector{'H'}, vector{'d'} }\n * // or\n * auto splitter = lz::t_split<std::vector<char>>(to_split, \" \"); // { vector{'H'}, vector{'d'} }\n *\n * // or\n * auto splitter = lz::t_split<std::vector<char>>(to_split, ' '); // { vector{'H'}, vector{'d'} }\n * // or\n * auto splitter = to_split | lz::t_split<std::vector<char>>(' '); // { vector{'H'}, vector{'d'} }\n * ```\n * @tparam ValueType The value type of the iterator to return, for example `std::vector<int>`.\n */\ntemplate<class ValueType>\nLZ_INLINE_VAR constexpr detail::split_adaptor<ValueType> t_split{};\n\n#endif\n\n/**\n * @brief Splits an iterable on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and the\n * iterable does not contain a .size() method. Example:\n * ```cpp\n * // Careful: split_values must be passed by reference, because it holds a reference to the values.\n * std::array<int, 4> arr = { 1, 2, 3, 4 };\n * auto split_values = {1, 2};\n * auto splitted = lz::split(arr, split_values); // {{1}, {4}}\n * // auto splitted = lz::split(arr, {1, 2}); // Dangling reference, do not do this\n * // or\n * auto splitted = arr | lz::split(split_valiues); // {{1}, {4}}\n * // auto splitted = arr | lz::split({1, 2}); // Dangling reference, do not do this\n *\n * // or\n * auto splitted = lz::split(arr, 2); // {{1}, {3, 4}}\n * // or\n * auto splitted = arr | lz::split(2); // {{1}, {3, 4}}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::split_adaptor<void> split{};\n\n/**\n * @brief Splits a string on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and the\n * iterable does not contain a .size() method. Returns a std::string to its substrings. Example:\n * ```cpp\n * std::string str = \"Hello, World!\";\n * auto splitted = lz::s_split(str, \", \"); // {\"Hello\", \"World!\"} where value_type is std::string\n * // auto splitted = lz::s_split(str, std::string(\", \")); // Dangling reference, do not do this\n * // or\n * auto splitted = str | lz::s_split(\", \"); // {\"Hello\", \"World!\"} where value_type is std::string\n * auto splitted = str | lz::s_split(lz::string_view(\", \")); // Ok, string_view is copied\n *\n * // or\n * auto splitted = lz::s_split(str, ','); // {\"Hello\", \" World!\"} where value_type is std::string\n *\n * // or\n * auto splitted = str | lz::s_split(',') // {\"Hello\", \" World!\"} where value_type is std::string\n * ```\n */\nLZ_INLINE_VAR constexpr detail::split_adaptor<std::string> s_split{};\n\n/**\n * @brief Splits a string on a delimiter. It returns a forward iterable, its end() method returns a default_sentinel_t and the\n * iterable does not contain a .size() method. Returns a string_view to its substrings. Example:\n * ```cpp\n * std::string str = \"Hello, World!\";\n * auto splitted = lz::sv_split(str, \", \"); // {\"Hello\", \"World!\"} where value_type is a string_view\n * // auto splitted = lz::sv_split(str, std::string(\", \")); // Dangling reference, do not do this\n * // or\n * auto splitted = str | lz::s_split(\", \"); // {\"Hello\", \"World!\"} where value_type is a string_view\n * auto splitted = str | lz::sv_split(lz::string_view(\", \")); // Ok, string_view is copied\n *\n * // or\n * auto splitted = lz::sv_split(str, ','); // {\"Hello\", \" World!\"} where value_type is string_view\n *\n * // or\n * auto splitted = str | lz::sv_split(',') // {\"Hello\", \" World!\"} where value_type is string_view\n * ```\n */\nLZ_INLINE_VAR constexpr detail::split_adaptor<lz::string_view> sv_split{};\n\n/**\n * @brief Split iterable helper alias.\n *\n * @tparam ValueType The value type of the iterator to return, for example `std::vector<int>`.\n * @tparam Iterable The input iterable type to split.\n * @tparam Delimiter The delimiter type to split on. For instance char or a string_view.\n * ```cpp\n * std::string to_split = \"H d\";\n * using split_char_iterable = lz::split_iterable<std::vector<char>, std::string, char>;\n * using split_sv_iterable = lz::split_iterable<std::vector<char>, std::string, lz::string_view>;\n *\n * split_char_iterable splitter = lz::t_split<std::vector<char>>(to_split, ' ');\n * split_sv_iterable splitter_sv = lz::t_split<std::vector<char>>(to_split, \" \");\n * ```\n */\ntemplate<class ValueType, class Iterable, class Delimiter>\nusing split_iterable = detail::split_iterable<ValueType, Iterable, Delimiter>;\n\n/**\n * @brief Split iterable helper alias for single character delimiter. Returns std::basic_string as value type.\n * @tparam Iterable The input iterable type to split.\n * ```cpp\n * std::string to_split = \"H d\";\n * lz::s_single_split_iterable<std::string> splitter = lz::split(to_split, ' ');\n * ```\n */\ntemplate<class Iterable>\nusing s_single_split_iterable =\n    split_iterable<std::basic_string<detail::val_iterable_t<Iterable>>, Iterable, detail::val_iterable_t<Iterable>>;\n\n/**\n * @brief Split iterable helper alias for string_view delimiter. Returns std::basic_string as value type.\n * @tparam Iterable The input iterable type to split.\n * ```cpp\n * std::string to_split = \"H d\";\n * lz::s_multiple_split_iterable<std::string> splitter = lz::split(to_split, \" \");\n * ```\n */\ntemplate<class Iterable>\nusing s_multiple_split_iterable = split_iterable<std::basic_string<detail::val_iterable_t<Iterable>>, Iterable,\n                                                 lz::copied<lz::basic_string_view<detail::val_iterable_t<Iterable>>>>;\n\n/**\n * @brief Split iterable helper alias for single character delimiter. Returns lz::basic_string_view as value type.\n * @tparam Iterable The input iterable type to split.\n * ```cpp\n * std::string to_split = \"H d\";\n * lz::sv_single_split_iterable<std::string> splitter = lz::split(to_split, ' ');\n * ```\n */\ntemplate<class Iterable>\nusing sv_single_split_iterable =\n    split_iterable<lz::basic_string_view<detail::val_iterable_t<Iterable>>, Iterable, detail::val_iterable_t<Iterable>>;\n\n/**\n * @brief Split iterable helper alias for string_view delimiter. Returns lz::basic_string_view as value type.\n * @tparam Iterable The input iterable type to split.\n * ```cpp\n * std::string to_split = \"H d\";\n * lz::sv_multiple_split_iterable<std::string> splitter = lz::split(to_split, \" \");\n * ```\n */\ntemplate<class Iterable>\nusing sv_multiple_split_iterable = split_iterable<lz::basic_string_view<detail::val_iterable_t<Iterable>>, Iterable,\n                                                  lz::copied<lz::basic_string_view<detail::val_iterable_t<Iterable>>>>;\n\n} // namespace lz\n\n#endif // LZ_STRING_SPLITTER_HPP\n"
  },
  {
    "path": "include/Lz/stream.hpp",
    "content": "#pragma once\n\n#ifndef LZ_STREAM_HPP\n#define LZ_STREAM_HPP\n\n#include <Lz/detail/adaptors/fn_args_holder.hpp>\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/procs/chain.hpp> // for operator|\n#include <Lz/traits/lazy_view.hpp>\n#include <ostream>\n#include <string>\n\n#if FMT_VERSION >= 80000\n#endif\n\n// clang-format off\n#if !defined(LZ_STANDALONE)\n  #ifdef __GNUC__\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wall\"\n    #pragma GCC diagnostic ignored \"-Wextra\"\n    #pragma GCC diagnostic ignored \"-Wpedantic\"\n    #pragma GCC diagnostic ignored \"-Wctor-dtor-privacy\"\n    #pragma GCC diagnostic ignored \"-Weffc++\"\n  #endif\n  #include <fmt/format.h>\n  #include <fmt/ostream.h>\n  #include <fmt/ranges.h>\n  #ifdef __GNUC__\n    #pragma GCC diagnostic pop\n  #endif\n#elif defined(LZ_HAS_FORMAT)\n  #include <format>\n#else\n  #include <sstream>\n#endif // !defined(LZ_STANDALONE)\n\n// clang-format on\n\nnamespace lz {\nnamespace detail {\n\nstruct iterable_formatter {\n    using adaptor = iterable_formatter;\n\n#if !defined(LZ_STANDALONE) || defined(LZ_HAS_FORMAT)\n\n    /**\n     * @brief Function that can be used to format an iterable to an output\n     * stream. Only defined if c++ 20 or if using `{fmt}`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 2, 4 };\n     * lz::format(vec, std::cout, \", \", \"{}\"); // prints: 2, 4\n     * lz::format(vec, std::cout, \",\"); // prints: 2,4\n     * std::stringstream oss;\n     * lz::format(vec, oss); // oss contains: 2, 4\n     * ```\n     *\n     * @param iterable Any iterable. May be a container or another iterable.\n     * @param stream The output stream to write to\n     * @param separator The separator to use between elements. Default is \", \"\n     * @param format The format to use for each element. Default is \"{}\"\n     */\n    template<class Iterable>\n    void\n    operator()(const Iterable& iterable, std::ostream& stream, const char* separator = \", \", const char* format = \"{}\") const {\n        auto it = iterable.begin();\n        auto end = iterable.end();\n        if (it == end) {\n            return;\n        }\n\n#ifndef LZ_STANDALONE\n\n#if FMT_VERSION >= 80000\n\n        fmt::print(stream, fmt::runtime(format), *it);\n        for (++it; it != end; ++it) {\n            fmt::print(stream, \"{}\", separator);\n            fmt::print(stream, fmt::runtime(format), *it);\n        }\n\n#else\n\n        fmt::print(stream, format, *it);\n        for (++it; it != end; ++it) {\n            fmt::print(stream, \"{}\", separator);\n            fmt::print(stream, format, *it);\n        }\n\n#endif // FMT_VERSION >= 80000\n\n#else // ^^ LZ_STANDALONE - vv !LZ_STANDALONE\n\n        std::ostreambuf_iterator<char> out_it(stream);\n        std::vformat_to(out_it, format, std::make_format_args(*it));\n\n        for (++it; it != end; ++it) {\n            std::vformat_to(out_it, \"{}\", std::make_format_args(separator));\n            std::vformat_to(out_it, format, std::make_format_args(*it));\n        }\n\n#endif // !LZ_STANDALONE\n    }\n\n#else\n\n    /**\n     * @brief Function that can be used to format an iterable to an output\n     * stream. Only defined if c++ 20 is not defined or not using `{fmt}`.\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 2, 4 };\n     * lz::format(vec, std::cout, \", \"); // prints: 2, 4\n     * lz::format(vec, std::cout, \",\"); // prints: 2,4\n     *\n     * @param separator The separator to use between elements. Default is \", \"\n     */\n    template<class Iterable>\n    void operator()(const Iterable& iterable, std::ostream& stream, const char* separator = \", \") const {\n        auto it = iterable.begin();\n        auto end = iterable.end();\n        if (it == end) {\n            return;\n        }\n        stream << *it;\n        for (++it; it != end; ++it) {\n            stream << separator << *it;\n        }\n    }\n\n#endif // !defined(LZ_STANDALONE) || defined(LZ_HAS_FORMAT)\n\n#if !defined(LZ_STANDALONE) || defined(LZ_HAS_FORMAT)\n\n    /**\n     * @brief Function that can be used with the pipe operator. This overload can be used with C++20's std::format or {fmt}.\n     *\n     * @param separator The separator to use between elements. Default is \", \"\n     * @param format The format to use for each element. Default is \"{}\"\n     * @return A function object that can be used with the pipe operator\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, const char*, const char*>\n    operator()(const char* separator = \", \", const char* format = \"{}\") const {\n        return { separator, format };\n    }\n\n    /**\n     * @brief Function that can be used with the pipe operator. This overload can be used with C++20's std::format or {fmt}.\n     * Example:\n     * ```cpp\n     * std::vector<int> vec = { 2, 4 };\n     * std::string output = vec | lz::format(std::cout, \", \", \"{}\"); // prints: 2, 4\n     * std::string output = vec | lz::format(std::cout, \",\"); // prints: 2,4\n     * std::string output = vec | lz::format(std::cout); // prints: 2, 4\n     * ```\n     *\n     * @param stream The output stream to write to\n     * @param separator The separator to use between elements. Default is \", \"\n     * @return A function object that can be used with the pipe operator\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, std::ostream&, const char*, const char*>\n    operator()(std::ostream& stream, const char* separator = \", \", const char* format = \"{}\") const {\n        return { stream, separator, format };\n    }\n\n#else\n\n    /**\n     * @brief Function that can be used with the pipe operator. It takes a separator. Only defined if c++ 20\n     * is not defined or not using `{fmt}`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 2, 4 };\n     * std::string output = vec | lz::format(\", \"); // prints: 2, 4\n     * std::string output = vec | lz::format; // prints: 2, 4\n     * ```\n     *\n     * @param separator The separator to use between elements. Default is \", \"\n     * @return A function object that can be used with the pipe operator\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, const char*> operator()(const char* separator = \", \") const {\n        return { separator };\n    }\n\n    /**\n     * @brief Function that can be used with the pipe operator. It takes an output stream and a separator. Only defined if c++ 20\n     * is not defined or not using `{fmt}`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 2, 4 };\n     * std::string output = vec | lz::format(std::cout, \", \"); // prints: 2, 4\n     * std::string output = vec | lz::format(std::cout); // prints: 2, 4\n     * ```\n     *\n     * @param stream The output stream to write to\n     * @param separator The separator to use between elements. Default is \", \"\n     * @return A function object that can be used with the pipe operator\n     */\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 fn_args_holder<adaptor, std::ostream&, const char*>\n    operator()(std::ostream& stream, const char* separator = \", \") const {\n        return { stream, separator };\n    }\n\n#endif // !defined(LZ_STANDALONE) || defined(LZ_HAS_FORMAT)\n\n#if !defined(LZ_STANDALONE)\n\n    /**\n     * @brief Converts an iterable to a string, using the given separator and format. This overload can be used with C++20's\n     * std::format or {fmt}. Example:\n     * ```cpp\n     * std::vector<int> vec = { 2, 4 };\n     * std::string output = lz::format(vec, \", \", \"{}\"); // 2, 4\n     * std::string output = lz::format(vec, \",\"); // 2,4\n     * std::string output = lz::format(vec); // 2, 4\n     * ```\n     *\n     * @param iterable Any iterable. May be a container or another iterable.\n     * @param separator The separator to use between elements. Default is \", \"\n     * @param format The format to use for each element. Default is \"{}\"\n     * @return The string representation of the iterable, with the given separator and format.\n     */\n    template<class Iterable>\n    LZ_NODISCARD std::string operator()(const Iterable& iterable, const char* separator = \", \", const char* format = \"{}\") const {\n#if FMT_VERSION >= 80000\n\n        return fmt::format(fmt::runtime(format), fmt::join(iterable, separator));\n\n#else\n\n        return fmt::format(format, fmt::join(iterable, separator));\n\n#endif // FMT_VERSION >= 80000\n    }\n\n#elif defined(LZ_HAS_FORMAT)\n\n    /**\n     * @brief Converts an iterable to a string, using the given separator and format. This overload can be used with C++20's\n     * std::format or {fmt}. Example:\n     * ```cpp\n     * std::vector<int> vec = { 2, 4 };\n     * std::string output = lz::format(vec, \", \", \"{}\"); // 2, 4\n     * std::string output = lz::format(vec, \",\"); // 2,4\n     * std::string output = lz::format(vec); // 2, 4\n     * ```\n     *\n     * @param iterable Any iterable. May be a container or another iterable.\n     * @param separator The separator to use between elements. Default is \", \"\n     * @param format The format to use for each element. Default is \"{}\"\n     * @return The string representation of the iterable, with the given separator and format.\n     */\n    template<class Iterable>\n    LZ_NODISCARD std::string operator()(const Iterable& iterable, const char* separator = \", \", const char* format = \"{}\") const {\n        auto it = iterable.begin();\n        auto end = iterable.end();\n        if (it == end) {\n            return \"\";\n        }\n\n        std::string result;\n        auto back_inserter = std::back_inserter(result);\n\n        std::vformat_to(back_inserter, format, std::make_format_args(*it));\n        auto sep = std::make_format_args(separator);\n\n        for (++it; it != end; ++it) {\n            std::vformat_to(back_inserter, \"{}\", sep);\n            std::vformat_to(back_inserter, format, std::make_format_args(*it));\n        }\n\n        return result;\n    }\n\n#else\n\n    /**\n     * @brief Converts an iterable to a string, using the given separator, using std::stringstream if c++ 20 is not defined or not\n     * using `{fmt}`. Example:\n     * ```cpp\n     * std::vector<int> vec = { 2, 4 };\n     * std::string output = lz::format(vec, \",\"); // 2,4\n     * std::string output = lz::format(vec); // 2, 4\n     * ```\n     *\n     * @param iterable Any iterable. May be a container or another iterable.\n     * @param separator The separator to use between elements. Default is \", \"\n     * @return The string representation of the iterable, with the given separator.\n     */\n    template<class Iterable>\n    LZ_NODISCARD std::string operator()(const Iterable& iterable, const char* separator = \", \") const {\n        std::ostringstream oss;\n        (*this)(iterable, oss, separator);\n        return oss.str();\n    }\n\n#endif // !defined(LZ_STANDALONE)\n};\n} // namespace detail\n} // namespace lz\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Streams any iterable to an output stream. For printing, `std::cout << <lz_iterable>` can also be used. Example:\n * ```cpp\n * std::vector<int> vec = { 2, 4 };\n *\n * // Requires C++20 or {fmt}. If using {fmt}, LZ_STANDALONE must *not* be defined.\n * lz::format(vec, std::cout, \", \", \"{}\"); // prints: 2, 4\n * lz::format(vec, std::cout, \",\"); // prints: 2,4\n * lz::format(vec, std::cout); // prints: 2, 4\n *\n * std::string output = lz::format(vec, \", \", \"{}\"); // 2, 4\n * std::string output = lz::format(vec, \",\"); // 2,4\n * std::string output = lz::format(vec); // 2, 4\n *\n * vec | lz::format(std::cout, \", \", \"{}\"); // prints: 2, 4\n * vec | lz::format(std::cout, \",\"); // prints: 2,4\n * vec | lz::format(std::cout); // prints: 2, 4\n * std::string output = vec | lz::format(\", \", \"{}\"); // 2, 4\n * std::string output = vec | lz::format; // 2, 4\n * std::string output = vec | lz::format(\",\"); // 2,4\n *\n * // Otherwise, if not using {fmt} or C++20, use the following:\n * lz::format(vec, std::cout, \", \"); // prints: 2, 4\n * lz::format(vec, std::cout); // prints: 2, 4\n * std::string output = lz::format(vec, \", \"); // 2, 4\n * std::string output = lz::format(vec); // 2,4\n *\n * // Or use the pipe operator:\n * vec | lz::format(std::cout, \", \"); // prints: 2, 4\n * vec | lz::format(std::cout); // prints: 2, 4\n * std::string output = vec | lz::format(\",\"); // 2,4\n * std::string output = vec | lz::format; // 2, 4\n * ```\n */\nLZ_INLINE_VAR constexpr detail::iterable_formatter format{};\n\n} // namespace lz\n\n#ifdef LZ_HAS_CONCEPTS\n\n/**\n * @brief Streams a `lz` iterable to an output stream. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto filter = lz::filter(vec, [](int i) { return i % 2 == 0; });\n * std::cout << filter; // 2, 4\n * ```\n *\n * @param stream The stream to output to\n * @param iterable The `lz` iterable to output\n */\nLZ_MODULE_EXPORT template<class Iterable>\nstd::ostream& operator<<(std::ostream& stream, const Iterable& iterable)\n    requires(std::is_base_of_v<lz::lazy_view, Iterable>)\n{\n    lz::format(iterable, stream);\n    return stream;\n}\n\n#else\n\n/**\n * @brief Streams a `lz` iterable to an output stream. Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4, 5 };\n * auto filter = lz::filter(vec, [](int i) { return i % 2 == 0; });\n * std::cout << filter; // 2, 4\n * ```\n *\n * @param stream The stream to output to\n * @param iterable The `lz` iterable to output\n */\nLZ_MODULE_EXPORT template<class Iterable>\nlz::detail::enable_if_t<std::is_base_of<lz::lazy_view, Iterable>::value, std::ostream&>\noperator<<(std::ostream& stream, const Iterable& iterable) {\n    lz::format(iterable, stream);\n    return stream;\n}\n\n#endif\n\n#endif\n"
  },
  {
    "path": "include/Lz/take.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_TAKE_HPP\r\n#define LZ_TAKE_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/take.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief This adaptor is used to take the first n elements of an iterable or iterator. The iterator category is the same as\r\n * the input iterator category. Has a .size() method that essentially returns\r\n * `n` or min(n, size(input_iterable)).\r\n\r\n * If the parameter is an iterable:\r\n * - returns a default_sentinel_t if the input iterable is not bidirectional or has a sentinel\r\n * - if `n` is larger than `lz::eager_size(input_iterable)` then the total iterable size is equal to\r\n *   `lz::eager_size(input_iterable)`, thus preventing out of bounds. Traverses the entire iterable while taking the first `n`\r\n *   for non random access iterables.\r\n * - If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is\r\n *   traversed\r\n *   to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you can use\r\n *   `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the iterable.\r\n *   `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its entire size is\r\n *   therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will traverse the iterable\r\n *   once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but will return the cached\r\n *   size instead. The following iterables require a(n) (eagerly)sized iterable:\r\n *   - `lz::chunks`\r\n *   - `lz::enumerate`\r\n *   - `lz::exclude`\r\n *   - `lz::interleave`\r\n *   - `lz::rotate`\r\n *   - `lz::take`\r\n *   - `lz::take_every`\r\n *   - `lz::zip_longest`\r\n *   - `lz::zip`\r\n *\r\n * If the parameter is an iterator:\r\n * - if `n` is larger than the actual size if the iterator then this is undefined behaviour.\r\n * - `lz::eager_size` is not used, so it is not guaranteed that the iterator will not go out of bounds.\r\n * - never returns a sentinel\r\n *\r\n * Example:\r\n * ```cpp\r\n * auto vec = std::vector<int>{1, 2, 3, 4, 5};\r\n * auto res = lz::take(vec, 2); // res = {1, 2}\r\n * // or\r\n * auto res = vec | lz::take(2); // res = {1, 2}\r\n * // or\r\n * auto res = lz::take(vec, 20); // res = {1, 2, 3, 4, 5}\r\n * // or\r\n * auto res = vec | lz::take(20); // res = {1, 2, 3, 4, 5}\r\n * auto res = lz::take(vec.begin(), 2); // res = {1, 2}\r\n * // auto res = lz::take(vec.begin(), 20); // undefined behaviour, as the iterator will go out of bounds\r\n * // vec.begin() | lz::take(2); // Not supported, as piping only works for iterables, not for iterators\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::take_adaptor take{};\r\n\r\n/**\r\n * @brief This is a helper alias that takes an iterable or iterator and returns a take_iterable.\r\n * @tparam IteratorOrIterable The type of the iterable or iterator. \r\n * ```cpp\r\n * auto vec = std::vector<int>{1, 2, 3, 4, 5};\r\n * lz::take_iterable<std::vector<int>> res = lz::take(vec, 2); // res = {1, 2}\r\n * // or\r\n * lz::take_iterable<std::vector<int>::iterator> res = lz::take(vec.begin(), 2); // res = {1, 2}\r\n * ```\r\n */\r\ntemplate<class IteratorOrIterable>\r\nusing take_iterable = detail::take_iterable<IteratorOrIterable>;\r\n\r\n} // namespace lz\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/Lz/take_every.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_EVERY_HPP\n#define LZ_TAKE_EVERY_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/take_every.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Takes every `offset` element from the iterable, starting from `start`. Returns the same iterator category as the input\n * iterable. Contains a size() method if the input iterable is sized.\n * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is traversed\n * to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you can use\n * `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the iterable.\n * `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its entire size is\n * therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will traverse the iterable\n * once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but will return the cached\n * size instead. The following iterables require a(n) (eagerly)sized iterable:\n * - `lz::chunks`\n * - `lz::enumerate`\n * - `lz::exclude`\n * - `lz::interleave`\n * - `lz::rotate`\n * - `lz::take`\n * - `lz::take_every`\n * - `lz::zip_longest`\n * - `lz::zip`\n * Example:\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4 };\n * auto take_every = lz::take_every(vec, 2); // 1, 3\n * auto take_every = lz::take_every(vec, 2, 1); // 2, 4\n *\n * // or\n *\n * auto take_every = vec | lz::take_every(2); // 1, 3\n * auto take_every = vec | lz::take_every(2, 1); // 2, 4\n * ```\n */\nLZ_INLINE_VAR constexpr detail::take_every_adaptor take_every{};\n\n/**\n * @brief Take every iterable helper alias\n * @tparam Iterable Type of the iterable to take every element from\n * ```cpp\n * std::vector<int> vec = { 1, 2, 3, 4 };\n * lz::take_every_iterable<std::vector<int>> take_every = lz::take_every(vec, 2); // 1, 3\n * ```\n */\ntemplate<class Iterable>\nusing take_every_iterable = detail::take_every_iterable<Iterable>;\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/take_while.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TAKE_WHILE_HPP\n#define LZ_TAKE_WHILE_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/take_while.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Takes elements from an iterable while the given predicte returns `true`. Will return a bidirectional iterable if the\n * given iterable is at least bidirectional. Does not contain a size() method and if its input iterable is forward or has a\n * sentinel, will return a default_sentinel_t. Example:\n * ```cpp\n * std::vector<int> vec = {1, 2, 3, 4, 5};\n * auto take_while = lz::take_while(vec, [](int i) { return i < 3; }); // {1, 2}\n * // or\n * auto take_while = vec | lz::take_while([](int i) { return i < 3; }); // {1, 2}\n * ```\n */\nLZ_INLINE_VAR constexpr detail::take_while_adaptor take_while{};\n\n/**\n * @brief Take while iterable helper alias.\n * @tparam Iterable The type of the iterable to take from.\n * @tparam UnaryPredicate The type of the unary predicate to use.\n * ```cpp\n * std::vector<int> vec = {1, 2, 3, 4, 5};\n * using take_while = lz::take_while_iterable<std::vector<int>, std::function<bool(int)>>;\n * take_while take_while_iter = lz::take_while(vec, [](int i) { return i < 3; });\n * ```\n */\ntemplate<class Iterable, class UnaryPredicate>\nusing take_while_iterable = detail::take_while_iterable<Iterable, UnaryPredicate>;\n\n} // namespace lz\n\n#endif // LZ_TAKE_WHILE_HPP\n"
  },
  {
    "path": "include/Lz/traits/concepts.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DETAIL_TRAITS_CONCEPTS_HPP\n#define LZ_DETAIL_TRAITS_CONCEPTS_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\n#ifdef LZ_HAS_CONCEPTS\n\n#include <Lz/detail/procs/begin_end.hpp>\n#include <Lz/procs/size.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\ntemplate<class T>\nconcept sized = requires(T&& c) {\n    { lz::size(c) };\n};\n\ntemplate<class T>\nconcept iterable = requires(T&& c) {\n    { detail::begin(c) };\n    { detail::end(c) };\n};\n\ntemplate<class T>\nconcept adaptor = requires(T) { typename T::adaptor; };\n\n} // namespace lz\n\n#endif // LZ_HAS_CONCEPTS\n\n#endif\n"
  },
  {
    "path": "include/Lz/traits/is_sized.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TRAITS_IS_SIZED_HPP\n#define LZ_TRAITS_IS_SIZED_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/traits/is_sized.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Helper to check whether a type is sized i.e. contains a .size() method. Example:\n * ```cpp\n * std::vector<int> v;\n * static_assert(lz::is_sized<decltype(v)>::value, \"Vector is sized\");\n *\n * auto not_sized = lz::c_string(\"Hello\");\n * static_assert(!lz::is_sized<decltype(not_sized)>::value, \"C string is not sized\"); // C strings are not sized\n * ```\n */\nusing detail::is_sized;\n\n#ifdef LZ_HAS_CXX_17\n/**\n * @brief Helper to check whether a type is sized i.e. contains a .size() method. Example:\n * ```cpp\n * std::vector<int> v;\n * static_assert(lz::is_sized_v<decltype(v)>, \"Vector is sized\");\n *\n * auto not_sized = lz::c_string(\"Hello\");\n * static_assert(!lz::is_sized_v<decltype(not_sized)>, \"C string is not sized\"); // C strings are not sized\n * ```\n */\nusing detail::is_sized_v;\n#endif\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/traits/iter_type.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TRAITS_ITER_TYPE_HPP\n#define LZ_TRAITS_ITER_TYPE_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n#include <Lz/detail/procs/begin_end.hpp>\n#include <utility>\n\nLZ_MODULE_EXPORT namespace lz {\n    \n/**\n * @brief Can be used to get the iterator type of an iterable. Example: `lz::iter_t<std::vector<int>>` will return\n * `std::vector<int>::iterator`.\n *\n * @tparam Iterable The iterable to get the iterator type from.\n */\ntemplate<class Iterable>\nusing iter_t = decltype(detail::begin(std::declval<Iterable&>()));\n\n/**\n * @brief Can be used to get the sentinel type of an iterable. Example: `lz::sentinel_t<std::vector<int>>` will return\n * `std::vector<int>::iterator`.\n * @tparam Iterable The iterable to get the sentinel type from.\n */\ntemplate<class Iterable>\nusing sentinel_t = decltype(detail::end(std::declval<Iterable&>()));\n\n}\n\n#endif\n"
  },
  {
    "path": "include/Lz/traits/lazy_view.hpp",
    "content": "#pragma once\n\n#ifndef LZ_TRAITS_LAZY_VIEW_HPP\n#define LZ_TRAITS_LAZY_VIEW_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\nstruct lazy_view {};\n\n} // namespace lz\n\n#endif // LZ_DETAIL_TRAITS_LAZY_VIEW_HPP\n"
  },
  {
    "path": "include/Lz/traits/traits.hpp",
    "content": "#pragma once\n\n#include \"is_sized.hpp\"\n#include \"iter_type.hpp\"\n#include \"lazy_view.hpp\"\n\n#ifdef LZ_HAS_CXX_20\n#include \"concepts.hpp\"\n#endif\n"
  },
  {
    "path": "include/Lz/unique.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_UNIQUE_HPP\r\n#define LZ_UNIQUE_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/unique.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Makes the input iterable unique. Every element therefore only occurs once. The input iterable must be sorted beforehand.\r\n * This iterator will 'decay' into a bidirectional one if the input iterator is higher than bidirectional. If the input iterable\r\n * is not bidirectional or higher, then the output iterator will be forward, and will also return a sentinel (or if the input\r\n * iterable has a sentinel), rather than an iterator. Will also return a sentienl if the input iterable has as sentinel. This\r\n * method does not contain a .size() method. Example:\r\n * ```cpp\r\n * std::vector<int> vec = { 1, 1, 2, 3, 3, 3, 4, 5, 5 };\r\n * std::sort(vec.begin(), vec.end());\r\n * auto unique = lz::unique(vec);  // second argument is custom comparer (optional)\r\n * // or\r\n * auto unique = vec | lz::unique;\r\n * // custom comparer can be passed as argument as well:\r\n * auto unique = vec | lz::unique(std::less<>{});\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::unique_adaptor unique{};\r\n\r\n/**\r\n * @brief Unique iterable helper alias\r\n * @tparam Iterable Type of the iterable to make unique\r\n * @tparam BinaryPredicate Type of the binary predicate to use for uniqueness\r\n * ```cpp\r\n * std::vector<int> vec = { 1, 1, 2, 3, 3, 3, 4, 5, 5 };\r\n * std::sort(vec.begin(), vec.end());\r\n * lz::unique_iterable<std::vector> vec_unique = lz::unique(vec);\r\n * ```\r\n */\r\ntemplate<class Iterable, class BinaryPredicate = LZ_BIN_OP(less, detail::val_iterable_t<Iterable>)>\r\nusing unique_iterable = detail::unique_iterable<Iterable, BinaryPredicate>;\r\n\r\n} // end namespace lz\r\n\r\n#endif // end LZ_UNIQUE_HPP\r\n"
  },
  {
    "path": "include/Lz/util/default_sentinel.hpp",
    "content": "#pragma once\n\n#ifndef LZ_DEFAULT_SENTINEL_HPP\n#define LZ_DEFAULT_SENTINEL_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\nstruct default_sentinel_t {\n    LZ_NODISCARD friend constexpr bool operator==(default_sentinel_t, default_sentinel_t) noexcept {\n        return true;\n    }\n\n    LZ_NODISCARD friend constexpr bool operator!=(default_sentinel_t, default_sentinel_t) noexcept {\n        return false;\n    }\n\n    LZ_NODISCARD friend constexpr bool operator<(default_sentinel_t, default_sentinel_t) noexcept {\n        return false;\n    }\n\n    LZ_NODISCARD friend constexpr bool operator>(default_sentinel_t, default_sentinel_t) noexcept {\n        return false;\n    }\n\n    LZ_NODISCARD friend constexpr bool operator>=(default_sentinel_t, default_sentinel_t) noexcept {\n        return true;\n    }\n\n    LZ_NODISCARD friend constexpr bool operator<=(default_sentinel_t, default_sentinel_t) noexcept {\n        return true;\n    }\n};\n\n/**\n * @brief Holds a default sentinel value that can be used in iterators. Example:\n * ```cpp\n * auto it = lz::repeat(20, 5).begin();\n * if (it == lz::default_sentinel) {\n *     // do something\n * }\n * ```\n */\nLZ_INLINE_VAR constexpr default_sentinel_t default_sentinel{};\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/util/optional.hpp",
    "content": "#pragma once\n\n#ifndef LZ_OPTIONAL_HPP\n#define LZ_OPTIONAL_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\n#ifdef LZ_HAS_CXX_17\n\n#include <optional>\n\n#else // LZ_HAS_CXX_17\n\n#include <Lz/detail/procs/addressof.hpp>\n#include <Lz/detail/procs/assert.hpp>\n#include <cstdint>\n#include <stdexcept>\n#include <type_traits>\n\n#endif // LZ_HAS_CXX_17\n\nLZ_MODULE_EXPORT namespace lz {\n\n#ifdef LZ_HAS_CXX_17\n\nusing std::optional;\nusing std::nullopt_t;\nusing std::nullopt;\n\n#else\n\nstruct nullopt_t {\n    struct init {};\n    constexpr nullopt_t(init) noexcept {\n    }\n};\n\nconstexpr nullopt_t nullopt{ nullopt_t::init{} };\n\ntemplate<class T>\nclass optional {\n    static_assert(!std::is_array<T>::value && std::is_object<T>::value, \"T may not be an array and must be an object\");\n\n    union {\n        typename std::remove_const<T>::type _value;\n        std::uint8_t _dummy;\n    };\n    bool _has_value{ false };\n\n    template<class U>\n    void construct(U&& obj) noexcept(std::is_nothrow_constructible<T, U>::value) {\n        ::new (static_cast<void*>(detail::addressof(_value))) T(std::forward<U>(obj));\n        _has_value = true;\n    }\n\npublic:\n    template<class U>\n    friend class optional;\n\n    constexpr optional() noexcept : optional{ nullopt } {\n    }\n\n    constexpr optional(nullopt_t) noexcept {\n    }\n\n    constexpr optional(const T& value) noexcept(std::is_nothrow_copy_constructible<T>::value) :\n        _value{ value },\n        _has_value{ true } {\n    }\n\n    constexpr optional(T&& value) noexcept(std::is_move_constructible<T>::value) :\n        _value{ std::move(value) },\n        _has_value{ true } {\n    }\n\n    LZ_CONSTEXPR_CXX_14 optional(optional<T>&& that) noexcept(std::is_nothrow_move_constructible<T>::value) {\n        if (that) {\n            construct(std::move(*that));\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 optional(const optional<T>& that) noexcept(noexcept(construct(*that))) {\n        if (that) {\n            construct(*that);\n        }\n    }\n\n    template<class U>\n    LZ_CONSTEXPR_CXX_14 optional(optional<U>&& that) noexcept(std::is_nothrow_constructible<T, U>::value) {\n        if (that) {\n            construct(std::move(*that));\n        }\n    }\n\n    template<class U>\n    LZ_CONSTEXPR_CXX_14 optional(const optional<U>& that) noexcept(noexcept(construct(*that))) {\n        if (that) {\n            construct(*that);\n        }\n    }\n\n    ~optional() {\n        if (_has_value) {\n            _value.~T();\n        }\n    }\n\n    LZ_CONSTEXPR_CXX_14 optional&\n    operator=(T&& value) noexcept(std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_constructible<T>::value) {\n        if (_has_value) {\n            _value = std::move(value);\n        }\n        else {\n            construct(std::move(value));\n        }\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 optional& operator=(const T& value) noexcept(std::is_nothrow_copy_assignable<T>::value &&\n                                                                     std::is_nothrow_copy_constructible<T>::value) {\n        if (_has_value) {\n            _value = value;\n        }\n        else {\n            construct(value);\n        }\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 optional& operator=(const optional<T>& that) noexcept(std::is_nothrow_copy_assignable<T>::value &&\n                                                                              std::is_nothrow_copy_constructible<T>::value) {\n        if (*this && that) {\n            _value = *that;\n        }\n        else if (!that) {\n            _has_value = false;\n        }\n        else {\n            construct(*that);\n        }\n        return *this;\n    }\n\n    LZ_CONSTEXPR_CXX_14 optional& operator=(optional<T>&& that) noexcept(std::is_nothrow_move_assignable<T>::value &&\n                                                                         std::is_nothrow_move_constructible<T>::value) {\n        if (*this && that) {\n            _value = std::move(*that);\n        }\n        else if (that) {\n            construct(std::move(*that));\n        }\n        else {\n            _has_value = false;\n        }\n        that._has_value = false;\n        return *this;\n    }\n\n    LZ_NODISCARD constexpr bool has_value() const noexcept {\n        return _has_value;\n    }\n\n    LZ_NODISCARD constexpr explicit operator bool() const noexcept {\n        return _has_value;\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 const T& value() const {\n        if (_has_value) {\n            return _value;\n        }\n        throw std::runtime_error(\"Cannot get uninitialized optional\");\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 T& value() {\n        return const_cast<T&>(static_cast<const optional<T>*>(this)->value());\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 T& operator*() noexcept {\n        return const_cast<T&>(static_cast<const optional<T>*>(this)->operator*());\n    }\n\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 const T& operator*() const noexcept {\n        LZ_ASSERT(_has_value, \"Cannot get uninitialized optional\");\n        return _value;\n    }\n\n    template<class U>\n    LZ_NODISCARD constexpr T value_or(U&& v) const& {\n        return bool(*this) ? this->value() : static_cast<T>(std::forward<U>(v));\n    }\n\n    template<class U>\n    LZ_NODISCARD LZ_CONSTEXPR_CXX_14 T value_or(U&& v) && {\n        return bool(*this) ? std::move(this->value()) : static_cast<T>(std::forward<U>(v));\n    }\n};\n\ntemplate<class T, class U>\nconstexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs) noexcept {\n    return bool(lhs) != bool(rhs) ? false : !bool(lhs) ? true : *lhs == *rhs;\n}\n\ntemplate<class T, class U>\nconstexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs) noexcept {\n    return !(lhs == rhs);\n}\n\n#endif // __cpp_lib_optional\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/util/string_view.hpp",
    "content": "#pragma once\n\n#ifndef LZ_STRING_VIEW_HPP\n#define LZ_STRING_VIEW_HPP\n\n#include <Lz/detail/compiler_config.hpp>\n\n#ifdef LZ_HAS_STRING_VIEW\n\n#include <string_view>\n\n#elif !defined(LZ_STANDALONE)\n\n// clang-format off\n#if !defined(LZ_STANDALONE)\n  #ifdef __GNUC__\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Weffc++\"\n    #pragma GCC diagnostic ignored \"-Wswitch-default\"\n  #endif\n    #include <fmt/core.h>\n  #ifdef __GNUC__\n    #pragma GCC diagnostic pop\n  #endif\n#endif\n// clang-format on\n\n#endif\n\nLZ_MODULE_EXPORT namespace lz {\n\n#if defined(LZ_HAS_STRING_VIEW)\n\n/**\n * @brief String view. Will use fmt, std or custom implementation depending on the build configuration.\n */\ntemplate<class C>\nusing basic_string_view = std::basic_string_view<C>;\n\n/**\n * @brief String view. Will use fmt, std or custom implementation depending on the build configuration.\n */\nusing string_view = std::string_view;\n\n#elif !defined(LZ_STANDALONE)\n\n/**\n * @brief String view. Will use fmt, std or custom implementation depending on the build configuration.\n */\ntemplate<class C>\nusing basic_string_view = fmt::basic_string_view<C>;\n\n/**\n * @brief String view. Will use fmt, std or custom implementation depending on the build configuration.\n */\nusing string_view = fmt::string_view;\n\n#else\n\nnamespace detail {\ntemplate<class CharT>\nconstexpr size_t constexpr_str_len(const CharT* str, size_t n = 0) noexcept {\n    return str[n] == static_cast<CharT>(0) ? n : constexpr_str_len(str, n + 1);\n}\n} // namespace detail\n\ntemplate<class CharT>\nclass basic_string_view {\n\npublic:\n    using iterator = const CharT*;\n    using const_iterator = const CharT*;\n    using value_type = CharT;\n\n    constexpr basic_string_view() = default;\n\n    constexpr basic_string_view(const CharT* data, size_t size) noexcept : _data{ data }, _size{ size } {\n    }\n\n    constexpr basic_string_view(const CharT* data) noexcept : basic_string_view(data, detail::constexpr_str_len(data)) {\n    }\n\n    constexpr basic_string_view(const CharT* begin, const CharT* end) noexcept :\n        _data{ begin },\n        _size{ static_cast<size_t>(end - begin) } {\n    }\n\n    constexpr const CharT* data() const noexcept {\n        return _data;\n    }\n\n    constexpr size_t size() const noexcept {\n        return _size;\n    }\n\n    constexpr const CharT* begin() const noexcept {\n        return _data;\n    }\n\n    constexpr const CharT* end() const noexcept {\n        return _data == nullptr ? nullptr : _data + _size;\n    }\n\n    constexpr size_t length() const noexcept {\n        return size();\n    }\n\n    constexpr bool empty() const noexcept {\n        return _size == 0;\n    }\n\nprivate:\n    const CharT* _data{};\n    size_t _size{};\n};\n\n/**\n * @brief String view. Will use fmt, std or custom implementation depending on the build configuration.\n */\nusing string_view = basic_string_view<char>;\n\n#endif\n\n} // namespace lz\n\n#endif\n"
  },
  {
    "path": "include/Lz/util/util.hpp",
    "content": "#pragma once\n\n#include \"string_view.hpp\"\n#include \"default_sentinel.hpp\"\n#include \"optional.hpp\"\n#include \"default_sentinel.hpp\"\n\n"
  },
  {
    "path": "include/Lz/zip.hpp",
    "content": "#pragma once\r\n\r\n#ifndef LZ_ZIP_HPP\r\n#define LZ_ZIP_HPP\r\n\r\n#include <Lz/procs/chain.hpp>\r\n#include <Lz/detail/adaptors/zip.hpp>\r\n\r\nLZ_MODULE_EXPORT namespace lz {\r\n\r\n/**\r\n * @brief Zips two or more iterables together. If the sizes of the iterables are different, the shortest one will be used. It\r\n * contains a size() method if all the iterables have a size() method. It is the same iterator category as the 'weakest' of the\r\n * input iterables. If the weakest is a forward iterator or one of them has a sentinel, then the end() function will return the\r\n * same types as its input iterables.\r\n * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is traversed\r\n * to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you can use\r\n * `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the iterable.\r\n * `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its entire size is\r\n * therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will traverse the iterable\r\n * once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but will return the cached\r\n * size instead. The following iterables require a(n) (eagerly)sized iterable:\r\n * - `lz::chunks`\r\n * - `lz::enumerate`\r\n * - `lz::exclude`\r\n * - `lz::interleave`\r\n * - `lz::rotate`\r\n * - `lz::take`\r\n * - `lz::take_every`\r\n * - `lz::zip_longest`\r\n * - `lz::zip`\r\n * Example:\r\n * ```cpp\r\n * std::vector<int> a = { 1, 2, 3 };\r\n * std::vector<int> b = { 4, 5, 6 };\r\n * std::vector<int> c = { 7, 8, 9 };\r\n * auto zipped = lz::zip(a, b, c); // { (1, 4, 7), (2, 5, 8), (3, 6, 9) }\r\n *\r\n * // or\r\n *\r\n * auto zipped = a | lz::zip(b, c); // { (1, 4, 7), (2, 5, 8), (3, 6, 9) }\r\n *\r\n * std::vector<int> d = { 10 };\r\n * auto zipped2 = lz::zip(a, d); // { (1, 10) }\r\n * ```\r\n */\r\nLZ_INLINE_VAR constexpr detail::zip_adaptor zip{};\r\n\r\n/**\r\n * @brief Zip helper alias.\r\n * @tparam Iterables The iterables to zip together.\r\n * ```cpp\r\n * std::vector<int> a = { 1, 2, 3 };\r\n * std::vector<int> b = { 4, 5 };\r\n * lz::zip_iterable<std::vector<int>, std::vector<int>> zipped = lz::zip(a, b);\r\n * ```\r\n */\r\ntemplate<class... Iterables>\r\nusing zip_iterable = detail::zip_iterable<Iterables...>;\r\n\r\n} // namespace lz\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/Lz/zip_longest.hpp",
    "content": "#pragma once\n\n#ifndef LZ_ZIP_LONGEST_HPP\n#define LZ_ZIP_LONGEST_HPP\n\n#include <Lz/procs/chain.hpp>\n#include <Lz/detail/adaptors/zip_longest.hpp>\n\nLZ_MODULE_EXPORT namespace lz {\n\n/**\n * @brief Zips two or more iterables together. If one of the iterables is shorter than the others, it will return an empty\n * optional instead of a non empty optional. The optional contains a tuple of `std::reference_wrapper`s to the elements itself if\n * it is not empty. Contains a size() method if all the iterables are sized. Will return the size of the largest iterable. Its\n * iterator category is the same as its 'weakest' input iterables. Returns a sentinel if one of the iterables has a sentinel.\n *\n * If the input iterable is exactly bidirectional and not sized (like `lz::filter` for example), the entire sequence is traversed\n * to get its end size (using `lz::eager_size`); this can be inefficient. To prevent this traversal alltogether, you can use\n * `lz::iter_decay` defined in `<Lz/iter_tools.hpp>` or you can use `lz::cache_size` to cache the size of the iterable.\n * `lz::iter_decay` can decay the iterable into a forward one and since forward iterators cannot go backward, its entire size is\n * therefore also not needed to create an iterator from its end() function. `lz::cache_size` however will traverse the iterable\n * once and cache the size, so that subsequent calls to `end()` will not traverse the iterable again, but will return the cached\n * size instead. The following iterables require a(n) (eagerly)sized iterable:\n * - `lz::chunks`\n * - `lz::enumerate`\n * - `lz::exclude`\n * - `lz::interleave`\n * - `lz::rotate`\n * - `lz::take`\n * - `lz::take_every`\n * - `lz::zip_longest`\n * - `lz::zip`\n\n * Example:\n * ```cpp\n * std::vector<int> a = { 1, 2, 3 };\n * std::vector<int> b = { 4, 5 };\n * auto zipped = lz::zip_longest(a, b); // {(1, 4), (2, 5), (3, lz::nullopt)}\n * // or\n * auto zipped = a | lz::zip_longest(b); // {(1, 4), (2, 5), (3, lz::nullopt)}\n *\n * auto filter = lz::filter(a, [](int i) { return i % 2 == 0; });\n * auto zipped = lz::zip_longest(a, filter); // {(1, lz::nullopt), (2, 2), (3, lz::nullopt)}\n * // zipped will be bidirectional. However, the entire sequence of `filter` will be traversed in order to get the size of the\n * // iterable. For `a` however, `a.size()` is used.\n * ```\n */\nLZ_INLINE_VAR constexpr detail::zip_longest_adaptor zip_longest{};\n\n/**\n * @brief Zip longest helper alias.\n * @tparam Iterables The iterables to zip together.\n * ```cpp\n * std::vector<int> a = { 1, 2, 3 };\n * std::vector<int> b = { 4, 5 };\n * lz::zip_longest_iterable<std::vector<int>, std::vector<int>> zipped = lz::zip_longest(a, b);\n * ```\n */\ntemplate<class... Iterables>\nusing zip_longest_iterable = detail::zip_longest_iterable<Iterables...>;\n\n} // namespace lz\n\n#endif // LZ_ZIP_LONGEST_HPP\n"
  },
  {
    "path": "src/lz.cppm",
    "content": "module;\n\n#include <algorithm>\n#include <cstddef>\n#include <format>\n#include <functional>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <numeric>\n#include <optional>\n#include <ostream>\n#include <random>\n#include <regex>\n#include <string_view>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <variant>\n\n// clang-format off\n\n#ifndef LZ_STANDALONE\n  #include <fmt/format.h>\n  #include <fmt/ostream.h>\n#endif\n\n// clang-format on\n\nexport module lz;\n\n#ifndef LZ_MODULE_EXPORT\n#define LZ_MODULE_EXPORT export\n#define LZ_MODULE_EXPORT_SCOPE_BEGIN export {\n#define LZ_MODULE_EXPORT_SCOPE_END }\n#endif\n\n#include \"Lz/algorithm/algorithm.hpp\"\n#include \"Lz/any_iterable.hpp\"\n#include \"Lz/c_string.hpp\"\n#include \"Lz/cached_size.hpp\"\n#include \"Lz/cartesian_product.hpp\"\n#include \"Lz/chunk_if.hpp\"\n#include \"Lz/chunks.hpp\"\n#include \"Lz/common.hpp\"\n#include \"Lz/concatenate.hpp\"\n#include \"Lz/drop.hpp\"\n#include \"Lz/drop_while.hpp\"\n#include \"Lz/enumerate.hpp\"\n#include \"Lz/except.hpp\"\n#include \"Lz/exclude.hpp\"\n#include \"Lz/exclusive_scan.hpp\"\n#include \"Lz/filter.hpp\"\n#include \"Lz/flatten.hpp\"\n#include \"Lz/generate.hpp\"\n#include \"Lz/generate_while.hpp\"\n#include \"Lz/group_by.hpp\"\n#include \"Lz/inclusive_scan.hpp\"\n#include \"Lz/interleave.hpp\"\n#include \"Lz/intersection.hpp\"\n#include \"Lz/iter_tools.hpp\"\n#include \"Lz/join_where.hpp\"\n#include \"Lz/loop.hpp\"\n#include \"Lz/map.hpp\"\n#include \"Lz/pairwise.hpp\"\n#include \"Lz/procs/procs.hpp\"\n#include \"Lz/random.hpp\"\n#include \"Lz/range.hpp\"\n#include \"Lz/regex_split.hpp\"\n#include \"Lz/repeat.hpp\"\n#include \"Lz/reverse.hpp\"\n#include \"Lz/rotate.hpp\"\n#include \"Lz/slice.hpp\"\n#include \"Lz/split.hpp\"\n#include \"Lz/stream.hpp\"\n#include \"Lz/take.hpp\"\n#include \"Lz/take_every.hpp\"\n#include \"Lz/take_while.hpp\"\n#include \"Lz/traits/traits.hpp\"\n#include \"Lz/unique.hpp\"\n#include \"Lz/util/util.hpp\"\n#include \"Lz/zip_longest.hpp\"\n"
  },
  {
    "path": "tests/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\r\n\r\nproject(LazyTests LANGUAGES CXX)\r\n\r\nset(CPP_LAZY_DOCTEST_VERSION \"2.4.12\" CACHE STRING \"Version of doctest to use for testing\")\r\nInclude(FetchContent)\r\nFetchContent_Declare(doctest \r\n\tURL https://github.com/doctest/doctest/archive/refs/tags/v${CPP_LAZY_DOCTEST_VERSION}.tar.gz\r\n\tDOWNLOAD_EXTRACT_TIMESTAMP TRUE\r\n)\r\nFetchContent_MakeAvailable(doctest)\r\n\r\n# ---- Import root project ----\r\noption(TEST_INSTALLED_VERSION \"Import the library using find_package\" OFF)\r\nif (TEST_INSTALLED_VERSION)\r\n\tfind_package(cpp-lazy REQUIRED CONFIG)\r\nelse ()\r\n\t# Enable warnings from includes\r\n\tset(cpp-lazy_INCLUDE_WITHOUT_SYSTEM ON CACHE INTERNAL \"\")\r\n\r\n\tFetchContent_Declare(cpp-lazy SOURCE_DIR \"${CMAKE_CURRENT_LIST_DIR}/..\")\r\n\tFetchContent_MakeAvailable(cpp-lazy)\r\nendif ()\r\n\r\ninclude(CTest)\r\n\r\nadd_subdirectory(cpp-lazy-ut-helper)\r\n\r\n# ---- Tests ----\r\nadd_executable(tests\r\n\talgorithm.cpp\r\n\tany_iterable.cpp\r\n\tas_iterator.cpp\r\n\tcached_size.cpp\r\n\tcartesian_product.cpp\r\n\tpiping.cpp\r\n\tchunk_if.cpp\r\n\tchunks.cpp\r\n\tcommon.cpp\r\n\tconcatenate.cpp\r\n\tc_string.cpp\r\n\tduplicates.cpp\r\n\tenumerate.cpp\r\n\texcept.cpp\r\n\texclude.cpp\r\n\texclusive_scan.cpp\r\n\tfilter.cpp\r\n\tflatten.cpp\r\n\tgenerate.cpp\r\n\tgenerate_while.cpp\r\n\tgroup_by.cpp\r\n\tinclusive_scan.cpp\r\n\tinit.cpp\r\n\tinterleave.cpp\r\n\tintersection.cpp\r\n\titer_tools.cpp\r\n\titerator.cpp\r\n\tjoin_where.cpp\r\n\tloop.cpp\r\n\tmap.cpp\r\n\tmaybe_owned.cpp\r\n\tpairwise.cpp\r\n\trandom.cpp\r\n\trange.cpp\r\n\tregex_split.cpp\r\n\trepeat.cpp\r\n\treverse.cpp\r\n\trotate.cpp\r\n\tsplit.cpp\r\n\tstandalone.cpp\r\n\tstring_view.cpp\r\n\ttake_every.cpp\r\n\ttake.cpp\r\n\ttake_while.cpp\r\n\tunique.cpp\r\n\tzip_longest.cpp\r\n\tzip.cpp\r\n)\r\n\r\ntarget_link_libraries(tests\r\n\tPRIVATE\r\n\t\tcpp-lazy::cpp-lazy\r\n\t\tcpp-lazy-ut-helper::cpp-lazy-ut-helper\r\n\t\tdoctest::doctest\r\n)\r\nadd_test(NAME tests\tCOMMAND $<TARGET_FILE:tests>)\r\ntarget_compile_options(tests\r\n\tPRIVATE\r\n\t\t$<$<CXX_COMPILER_ID:MSVC>:/W4 /permissive- /WX /diagnostics:caret>\r\n\t\t# -Wmismatched-tags causes internal compiler error for GCC. This in combination with #include <fstream> and #include <sstream> in pch\r\n\t\t$<$<CXX_COMPILER_ID:GNU>:-Wpedantic -Wextra -Wall -Werror -Wshadow -Wconversion -Wcast-qual -Wcomma-subscript -Wctor-dtor-privacy -Wdeprecated-copy-dtor -Wdouble-promotion -Wduplicated-branches -Wduplicated-cond -Wenum-conversion -Wextra-semi -Wfloat-equal -Wformat-overflow=2 -Wformat-signedness -Wformat=2 -Wlogical-op -Wmultichar -Wnon-virtual-dtor -Woverloaded-virtual -Wpointer-arith -Wrange-loop-construct -Wrestrict -Wstrict-null-sentinel -Wsuggest-attribute=format -Wsuggest-attribute=malloc -Wuninitialized -Wvla -Wvolatile -Wwrite-strings -Wunused-result -Wunused-but-set-variable -Wmissing-declarations -Winvalid-pch -Weffc++ -Wdisabled-optimization -Wsign-conversion -Wcast-align -Wmissing-include-dirs -Wswitch-default -Wredundant-decls -Wundef -Wold-style-cast -Wstrict-overflow=2 -Wsuggest-override -Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast -Wvector-operation-performance -Walloca -Wduplicated-branches -Wcast-align=strict>\r\n\r\n\t\t$<$<CXX_COMPILER_ID:Clang,AppleClang>:-Wpedantic -Wextra -Wall -Werror -pedantic -pedantic-errors -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-c++98-compat-bind-to-temporary-copy -Wno-c++98-compat-local-type-template-args -Wno-unsafe-buffer-usage -Wno-padded -Wno-documentation-unknown-command -Wno-switch-enum>\r\n)\r\n"
  },
  {
    "path": "tests/algorithm.cpp",
    "content": "#include <Lz/algorithm/algorithm.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/range.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/stream.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\ntemplate<class T>\nclass custom_container {\n    std::vector<T> _vec{};\n    std::size_t _expected_capacity{};\n\npublic:\n    std::size_t expected_capacity() const {\n        return _expected_capacity;\n    }\n\n    custom_container(std::size_t expected_capacity) : _expected_capacity(expected_capacity) {\n    }\n\n    void reserve(std::size_t size) {\n        _vec.reserve(size);\n    }\n\n    std::size_t vec_capacity() const {\n        return _vec.capacity();\n    }\n\n    std::vector<T>& vec() {\n        return _vec;\n    }\n};\n\ntemplate<class T>\nstruct lz::custom_copier_for<custom_container<T>> {\n    template<class Iterable>\n    void copy(Iterable&& iterable, custom_container<T>& container) const {\n        container.reserve(lz::eager_size(iterable));\n        REQUIRE(container.vec().empty());\n        lz::copy(std::forward<Iterable>(iterable), std::back_inserter(container.vec()));\n    }\n};\n\nTEST_CASE(\"Formatting\") {\n    SUBCASE(\"Non-empty fmt\") {\n        std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n\n        auto str = arr | lz::format;\n        REQUIRE(str == \"1, 2, 3, 4, 5\");\n        REQUIRE(lz::format(arr) == \"1, 2, 3, 4, 5\");\n\n        str = arr | lz::format(\", \", \"{}\");\n        REQUIRE(str == \"1, 2, 3, 4, 5\");\n        REQUIRE(lz::format(arr, \", \", \"{}\") == \"1, 2, 3, 4, 5\");\n\n        str = arr | lz::format(\", \", \"{:02}\");\n        REQUIRE(str == \"01, 02, 03, 04, 05\");\n        REQUIRE(lz::format(arr, \", \", \"{:02}\") == \"01, 02, 03, 04, 05\");\n\n        str = arr | lz::format(\",\", \"{:02}\");\n        REQUIRE(str == \"01,02,03,04,05\");\n        REQUIRE(lz::format(arr, \",\", \"{:02}\") == \"01,02,03,04,05\");\n\n        std::streambuf* old_cout = std::cout.rdbuf();\n        std::ostringstream oss;\n        std::cout.rdbuf(oss.rdbuf());\n\n        lz::format(arr, std::cout);\n        REQUIRE(oss.str() == \"1, 2, 3, 4, 5\");\n        oss.str(\"\");\n\n        lz::format(arr, std::cout, \",\");\n        REQUIRE(oss.str() == \"1,2,3,4,5\");\n        oss.str(\"\");\n\n        lz::format(arr, std::cout, \", \", \"{:02}\");\n        REQUIRE(oss.str() == \"01, 02, 03, 04, 05\");\n        oss.str(\"\");\n\n        std::cout.rdbuf(old_cout);\n    }\n\n    SUBCASE(\"Empty fmt\") {\n        std::array<int, 0> arr = {};\n\n        auto str = arr | lz::format;\n        REQUIRE(str == \"\");\n        REQUIRE(lz::format(arr) == \"\");\n\n        str = arr | lz::format(\",\", \"{}\");\n        REQUIRE(str == \"\");\n        REQUIRE(lz::format(arr, \",\", \"{}\") == \"\");\n\n        str = arr | lz::format(\",\", \"{:02}\");\n        REQUIRE(str == \"\");\n        REQUIRE(lz::format(arr, \",\", \"{:02}\") == \"\");\n\n        str = arr | lz::format(\",\", \"{:02}\");\n        REQUIRE(str == \"\");\n        REQUIRE(lz::format(arr, \",\", \"{:02}\") == \"\");\n    }\n\n    SUBCASE(\"One element fmt\") {\n        std::array<int, 1> arr = { 1 };\n\n        auto str = arr | lz::format(\", \", \"{}\");\n        REQUIRE(str == \"1\");\n        REQUIRE(lz::format(arr, \", \", \"{}\") == \"1\");\n\n        str = arr | lz::format(\", \", \"{:02}\");\n        REQUIRE(str == \"01\");\n        REQUIRE(lz::format(arr, \", \", \"{:02}\") == \"01\");\n    }\n\n    SUBCASE(\"Non empty ostream\") {\n        std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n        lz::basic_iterable<std::array<int, 5>::iterator> iterable(arr);\n        std::ostringstream oss;\n        oss << iterable;\n        REQUIRE(oss.str() == \"1, 2, 3, 4, 5\");\n\n        oss.str(\"\");\n\n        oss << iterable;\n        REQUIRE(oss.str() == \"1, 2, 3, 4, 5\");\n    }\n\n    SUBCASE(\"Empty ostream\") {\n        std::array<int, 0> arr = {};\n        lz::basic_iterable<std::array<int, 0>::iterator> iterable(arr);\n        std::ostringstream oss;\n        oss << iterable;\n        REQUIRE(oss.str() == \"\");\n    }\n\n    SUBCASE(\"One element ostream\") {\n        std::array<int, 1> arr = { 1 };\n        lz::basic_iterable<std::array<int, 1>::iterator> iterable(arr.begin(), arr.end());\n        std::ostringstream oss;\n        oss << iterable;\n        REQUIRE(oss.str() == \"1\");\n    }\n}\n\nTEST_CASE(\"Size\") {\n    SUBCASE(\"lz::size\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        static_assert(std::is_unsigned<decltype(lz::size(vec))>::value, \"lz::size should return an unsigned type\");\n        REQUIRE(lz::size(vec) == 5);\n\n        int arr[] = { 1, 2, 3, 4, 5 };\n        static_assert(std::is_unsigned<decltype(lz::size(arr))>::value, \"lz::size should return an unsigned type\");\n        REQUIRE(lz::size(arr) == 5);\n    }\n\n    SUBCASE(\"lz::ssize\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        static_assert(std::is_signed<decltype(lz::ssize(vec))>::value, \"lz::ssize should return a signed type\");\n        REQUIRE(lz::ssize(vec) == 5);\n\n        int arr[] = { 1, 2, 3, 4, 5 };\n        static_assert(std::is_signed<decltype(lz::ssize(arr))>::value, \"lz::ssize should return a signed type\");\n        REQUIRE(lz::ssize(arr) == 5);\n    }\n\n    SUBCASE(\"Eager size random access\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::eager_size(vec) == 5);\n    }\n\n    SUBCASE(\"Eager size bidirectional\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::eager_size(lst) == 5);\n    }\n\n    SUBCASE(\"Eager size forward\") {\n        std::forward_list<int> flst = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::eager_size(flst) == 5);\n    }\n\n    SUBCASE(\"Eager size sentinel\") {\n        auto c_str = lz::c_string(\"Hello\");\n        REQUIRE(lz::eager_size(c_str) == 5);\n    }\n}\n\nTEST_CASE(\"To iterable\") {\n    SUBCASE(\"To iterable random access\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        lz::basic_iterable<std::vector<int>::iterator> iterable(vec.begin(), vec.end());\n        REQUIRE(lz::size(iterable) == 5);\n    }\n\n    SUBCASE(\"To iterable forward\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5 };\n        lz::sized_iterable<std::list<int>::iterator> iterable(lst);\n        REQUIRE(lz::size(iterable) == 5);\n    }\n\n    SUBCASE(\"To iterable begin end random access\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        lz::basic_iterable<std::vector<int>::iterator> iterable(vec.begin(), vec.end());\n        REQUIRE(lz::size(iterable) == 5);\n    }\n\n    SUBCASE(\"To iterable begin end bidirectional\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5 };\n        lz::basic_iterable<std::list<int>::iterator> iterable(lst.begin(), lst.end());\n        static_assert(!lz::is_sized<decltype(iterable)::iterator>::value, \"Should not be a sized iterator\");\n    }\n}\n\nTEST_CASE(\"To container\") {\n    SUBCASE(\"To cpp array same iterator pair\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto arr = lz::to<std::array<int, 5>>(vec);\n        REQUIRE(lz::equal(arr, vec));\n        arr = vec | lz::to<std::array<int, 5>>();\n        REQUIRE(lz::equal(arr, vec));\n    }\n\n    SUBCASE(\"To std list same iterator pair\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto lst = lz::to<std::list<int>>(vec);\n        REQUIRE(lz::equal(lst, vec));\n        lst = vec | lz::to<std::list<int>>();\n        REQUIRE(lz::equal(lst, vec));\n    }\n\n    SUBCASE(\"To vector same iterator pair\") {\n        const std::vector<int> v1 = { 1, 2, 3, 4, 5 };\n        auto v2 = lz::to<std::vector<int>>(v1);\n        REQUIRE(lz::equal(v1, v2));\n        v2 = v1 | lz::to<std::vector<int>>();\n        REQUIRE(lz::equal(v1, v2));\n    }\n\n    SUBCASE(\"To forward list same iterator pair\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto flst = lz::to<std::forward_list<int>>(vec);\n        REQUIRE(lz::equal(flst, vec));\n        flst = vec | lz::to<std::forward_list<int>>();\n        REQUIRE(lz::equal(flst, vec));\n    }\n\n    SUBCASE(\"To queue same iterator pair\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto queue = lz::to<std::queue<int>>(vec);\n        std::size_t i = 0;\n        for (; !queue.empty(); queue.pop(), ++i) {\n            REQUIRE(queue.front() == vec[i]);\n        }\n        queue = vec | lz::to<std::queue<int>>();\n        for (i = 0; !queue.empty(); queue.pop(), ++i) {\n            REQUIRE(queue.front() == vec[i]);\n        }\n    }\n\n    SUBCASE(\"To deque same iterator pair\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto deque = lz::to<std::deque<int>>(vec);\n        REQUIRE(lz::equal(deque, vec));\n        deque = vec | lz::to<std::deque<int>>();\n        REQUIRE(lz::equal(deque, vec));\n    }\n\n    SUBCASE(\"To set same iterator pair\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto set = lz::to<std::set<int>>(vec);\n        REQUIRE(set.size() == vec.size());\n        for (std::size_t i = 0; i < vec.size(); ++i) {\n            REQUIRE(set.find(vec[i]) != set.end());\n        }\n        set = vec | lz::to<std::set<int>>();\n        REQUIRE(set.size() == vec.size());\n        for (std::size_t i = 0; i < vec.size(); ++i) {\n            REQUIRE(set.find(vec[i]) != set.end());\n        }\n    }\n\n    SUBCASE(\"To unordered set same iterator pair\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto set = lz::to<std::unordered_set<int>>(vec);\n        REQUIRE(set.size() == vec.size());\n        for (std::size_t i = 0; i < vec.size(); ++i) {\n            REQUIRE(set.find(vec[i]) != set.end());\n        }\n        set = vec | lz::to<std::unordered_set<int>>();\n        REQUIRE(set.size() == vec.size());\n        for (std::size_t i = 0; i < vec.size(); ++i) {\n            REQUIRE(set.find(vec[i]) != set.end());\n        }\n    }\n\n    SUBCASE(\"To custom container same iterator pair\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto container = lz::to<custom_container<int>>(vec, vec.size());\n        REQUIRE(lz::equal(container.vec(), vec));\n        container = vec | lz::to<custom_container<int>>(vec.size());\n        REQUIRE(lz::equal(container.vec(), vec));\n    }\n\n    SUBCASE(\"To cpp array sentinel iterator pair\") {\n        auto c_str = lz::c_string(\"Hello\");\n        auto arr = lz::to<std::array<char, 5>>(c_str);\n        REQUIRE(lz::equal(arr, c_str));\n        arr = c_str | lz::to<std::array<char, 5>>();\n        REQUIRE(lz::equal(arr, c_str));\n    }\n\n    SUBCASE(\"To std list sentinel iterator pair\") {\n        auto c_str = lz::c_string(\"Hello\");\n        auto lst = lz::to<std::list<char>>(c_str);\n        REQUIRE(lz::equal(lst, c_str));\n        lst = c_str | lz::to<std::list<char>>();\n        REQUIRE(lz::equal(lst, c_str));\n    }\n\n    SUBCASE(\"To vector sentinel iterator pair\") {\n        auto c_str = lz::c_string(\"Hello\");\n        auto vec = lz::to<std::vector<char>>(c_str);\n        REQUIRE(lz::equal(vec, c_str));\n        vec = c_str | lz::to<std::vector<char>>();\n        REQUIRE(lz::equal(vec, c_str));\n    }\n\n    SUBCASE(\"To forward list sentinel iterator pair\") {\n        auto c_str = lz::c_string(\"Hello\");\n        auto flst = lz::to<std::forward_list<char>>(c_str);\n        REQUIRE(lz::equal(flst, c_str));\n        flst = c_str | lz::to<std::forward_list<char>>();\n        REQUIRE(lz::equal(flst, c_str));\n    }\n\n    SUBCASE(\"To queue sentinel iterator pair\") {\n        auto c_str = lz::c_string(\"Hello\");\n        auto queue = lz::to<std::queue<char>>(c_str);\n        std::size_t i = 0;\n        for (; !queue.empty(); queue.pop(), ++i) {\n            REQUIRE(queue.front() == *std::next(c_str.begin(), static_cast<std::ptrdiff_t>(i)));\n        }\n        queue = c_str | lz::to<std::queue<char>>();\n        i = 0;\n        for (; !queue.empty(); queue.pop(), ++i) {\n            REQUIRE(queue.front() == *std::next(c_str.begin(), static_cast<std::ptrdiff_t>(i)));\n        }\n    }\n\n    SUBCASE(\"To deque sentinel iterator pair\") {\n        auto c_str = lz::c_string(\"Hello\");\n        auto deque = lz::to<std::deque<char>>(c_str);\n        REQUIRE(lz::equal(deque, c_str));\n        deque = c_str | lz::to<std::deque<char>>();\n        REQUIRE(lz::equal(deque, c_str));\n    }\n\n    SUBCASE(\"To set sentinel iterator pair\") {\n        auto c_str = lz::c_string(\"Helo\");\n        auto set = lz::to<std::set<char>>(c_str);\n        REQUIRE(set.size() == lz::eager_size(c_str));\n        lz::for_each(c_str, [&set](char c) { REQUIRE(set.find(c) != set.end()); });\n        set = c_str | lz::to<std::set<char>>();\n        REQUIRE(set.size() == lz::eager_size(c_str));\n        lz::for_each(c_str, [&set](char c) { REQUIRE(set.find(c) != set.end()); });\n    }\n\n    SUBCASE(\"To unordered set sentinel iterator pair\") {\n        auto c_str = lz::c_string(\"Helo\");\n        auto set = lz::to<std::unordered_set<char>>(c_str);\n        REQUIRE(set.size() == lz::eager_size(c_str));\n        lz::for_each(c_str, [&set](char c) { REQUIRE(set.find(c) != set.end()); });\n        set = c_str | lz::to<std::unordered_set<char>>();\n        REQUIRE(set.size() == lz::eager_size(c_str));\n        lz::for_each(c_str, [&set](char c) { REQUIRE(set.find(c) != set.end()); });\n    }\n\n    SUBCASE(\"To custom container same iterator pair\") {\n        const auto c_str = lz::c_string(\"Hello\");\n        auto container = lz::to<custom_container<int>>(c_str, std::size_t{ 0 });\n        REQUIRE(lz::equal(container.vec(), c_str));\n        container = c_str | lz::to<custom_container<int>>(std::size_t{ 0 });\n        REQUIRE(lz::equal(container.vec(), c_str));\n    }\n}\n\nTEST_CASE(\"Empty\") {\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::empty(iterable));\n    }\n\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::empty(iterable));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::empty(iterable));\n    }\n}\n\nTEST_CASE(\"Has one\") {\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::has_one(iterable));\n    }\n\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::has_one(iterable));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::has_one(iterable));\n    }\n}\n\nTEST_CASE(\"Has many\") {\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::has_many(iterable));\n    }\n\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::has_many(iterable));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::has_many(iterable));\n    }\n}\n\nTEST_CASE(\"Peek\") {\n    SUBCASE(\"Two elements ref\") {\n        auto iterable = lz::c_string(\"He\");\n        static_assert(std::is_same<decltype(lz::peek(iterable)), lz::optional<std::reference_wrapper<const char>>>::value, \"\");\n\n        REQUIRE(lz::peek(iterable).has_value());\n        REQUIRE(lz::peek(iterable).value() == 'e');\n    }\n\n    SUBCASE(\"One element ref\") {\n        auto iterable = lz::c_string(\"H\");\n        static_assert(std::is_same<decltype(lz::peek(iterable)), lz::optional<std::reference_wrapper<const char>>>::value, \"\");\n\n        REQUIRE_FALSE(lz::peek(iterable).has_value());\n    }\n\n    SUBCASE(\"Empty ref\") {\n        auto iterable = lz::c_string(\"\");\n        static_assert(std::is_same<decltype(lz::peek(iterable)), lz::optional<std::reference_wrapper<const char>>>::value, \"\");\n\n        REQUIRE_FALSE(lz::peek(iterable).has_value());\n    }\n\n    SUBCASE(\"Two elements val\") {\n        auto iterable = lz::c_string(\"He\") | lz::map([](char c) { return c; });\n        static_assert(std::is_same<decltype(lz::peek(iterable)), lz::optional<char>>::value, \"\");\n\n        REQUIRE(lz::peek(iterable).has_value());\n        REQUIRE(lz::peek(iterable).value() == 'e');\n    }\n\n    SUBCASE(\"One element val\") {\n        auto iterable = lz::c_string(\"H\") | lz::map([](char c) { return c; });\n        static_assert(std::is_same<decltype(lz::peek(iterable)), lz::optional<char>>::value, \"\");\n\n        REQUIRE_FALSE(lz::peek(iterable).has_value());\n    }\n\n    SUBCASE(\"Empty val\") {\n        auto iterable = lz::c_string(\"\") | lz::map([](char c) { return c; });\n        static_assert(std::is_same<decltype(lz::peek(iterable)), lz::optional<char>>::value, \"\");\n\n        REQUIRE_FALSE(lz::peek(iterable).has_value());\n    }\n}\n\nTEST_CASE(\"Front\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::front(iterable) == 'H');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::front(iterable) == 'H');\n    }\n}\n\nTEST_CASE(\"Back\") {\n    SUBCASE(\"ra\") {\n        std::vector<int> vec = { 1, 2, 3 };\n        REQUIRE(lz::back(vec) == 3);\n        vec = { 1 };\n        REQUIRE(lz::back(vec) == 1);\n    }\n\n    SUBCASE(\"With sentinel\") {\n        auto repeater = lz::repeat(20, 5);\n        REQUIRE(lz::back(repeater) == 20);\n        repeater = lz::repeat(42, 1);\n        REQUIRE(lz::back(repeater) == 42);\n    }\n\n    SUBCASE(\"With c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::back(iterable) == 'o');\n\n        iterable = lz::c_string(\"A\");\n        REQUIRE(lz::back(iterable) == 'A');\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::list<int> lst = { 1, 2, 3 };\n        REQUIRE(lz::back(lst) == 3);\n        lst = { 1 };\n        REQUIRE(lz::back(lst) == 1);\n    }\n}\n\nTEST_CASE(\"Front or\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::front_or(iterable, 'a') == 'H');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::front_or(iterable, 'a') == 'H');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::front_or(iterable, 'a') == 'a');\n    }\n}\n\nTEST_CASE(\"Back or\") {\n    SUBCASE(\"With non-empty\") {\n        std::vector<int> vec = { 1, 2, 3 };\n        REQUIRE(lz::back_or(vec, 0) == 3);\n    }\n\n    SUBCASE(\"With one element\") {\n        std::vector<int> vec = { 1 };\n        REQUIRE(lz::back_or(vec, 0) == 1);\n    }\n\n    SUBCASE(\"With empty\") {\n        std::vector<int> vec = {};\n        REQUIRE(lz::back_or(vec, 0) == 0);\n    }\n}\n\nTEST_CASE(\"Distance\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::distance(iterable.begin(), iterable.end()) == 5);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::distance(iterable.begin(), iterable.end()) == 1);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::distance(iterable.begin(), iterable.end()) == 0);\n    }\n}\n\nTEST_CASE(\"Nth\") {\n    SUBCASE(\"No sentinel\") {\n        auto lst = lz::range(10) | lz::to<std::list>();\n        for (size_t i : lz::range(size_t{ 10 })) {\n            REQUIRE(*lz::nth(lst, static_cast<std::ptrdiff_t>(i)) == static_cast<std::ptrdiff_t>(i));\n        }\n    }\n\n    SUBCASE(\"Sentinel\") {\n        auto c_str = lz::c_string(\"abcdefghij\");\n        for (size_t i : lz::range(size_t{ 10 })) {\n            REQUIRE(*lz::nth(c_str, static_cast<std::ptrdiff_t>(i)) == 'a' + static_cast<char>(i));\n        }\n    }\n}\n\nTEST_CASE(\"Accumulate\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const wchar_t* str = L\"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::accumulate(iterable, wchar_t{}) == L'H' + L'e' + L'l' + L'l' + L'o');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::accumulate(iterable, 0, std::plus<int>()) == 72);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::accumulate(iterable, 0, std::plus<int>()) == 0);\n    }\n}\n\nTEST_CASE(\"Max element\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(*lz::max_element(iterable) == 'o');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(*lz::max_element(iterable) == 'H');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::max_element(iterable) == iterable.end());\n    }\n}\n\nTEST_CASE(\"Min element\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(*lz::min_element(iterable) == 'H');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(*lz::min_element(iterable) == 'H');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::min_element(iterable) == iterable.end());\n    }\n}\n\nTEST_CASE(\"Find if\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_if(iterable, [](char c) { return c == 'l'; });\n        REQUIRE(lz::distance(iterable.begin(), pos) == 2);\n        REQUIRE(*pos == 'l');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_if(iterable, [](char c) { return c == 'H'; });\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n        REQUIRE(*pos == 'H');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_if(iterable, [](char c) { return c == 'H'; });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_if(iterable, [](char c) { return c == 'a'; });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 5);\n    }\n}\n\nTEST_CASE(\"Find\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find(iterable, 'l');\n        REQUIRE(lz::distance(iterable.begin(), pos) == 2);\n        REQUIRE(*pos == 'l');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find(iterable, 'H');\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n        REQUIRE(*pos == 'H');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find(iterable, 'H');\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find(iterable, 'a');\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 5);\n    }\n}\n\nTEST_CASE(\"Find last\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Helloo\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last(iterable, 'o');\n        REQUIRE(*pos == 'o');\n        REQUIRE(lz::distance(iterable.begin(), pos) == 5);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last(iterable, 'H');\n        REQUIRE(*pos == 'H');\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last(iterable, 'H');\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last(iterable, 'a');\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 5);\n    }\n}\n\nTEST_CASE(\"Find last random access\") {\n    SUBCASE(\"With non-empty\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 5 };\n        lz::detail::reverse_iterable<std::vector<int>, false> rev{ vec };\n        auto pos = lz::find_last_if(vec, [](int i) { return i == 5; });\n        REQUIRE(*pos == 5);\n        REQUIRE(lz::distance(vec.begin(), pos) == 5);\n    }\n\n    SUBCASE(\"With one element\") {\n        std::vector<int> vec = { 1 };\n        auto pos = lz::find_last_if(vec, [](int i) { return i == 1; });\n        REQUIRE(*pos == 1);\n        REQUIRE(lz::distance(vec.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"With empty \") {\n        std::vector<int> vec;\n        auto pos = lz::find_last_if(vec, [](int i) { return i == 1; });\n        REQUIRE(pos == vec.end());\n        REQUIRE(lz::distance(vec.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"Not found\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto pos = lz::find_last_if(vec, [](int i) { return i == 6; });\n        REQUIRE(pos == vec.end());\n        REQUIRE(lz::distance(vec.begin(), pos) == 5);\n    }\n}\n\nTEST_CASE(\"Find last if\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Helloo\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last_if(iterable, [](char c) { return c == 'o'; });\n        REQUIRE(*pos == 'o');\n        REQUIRE(lz::distance(iterable.begin(), pos) == 5);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last_if(iterable, [](char c) { return c == 'H'; });\n        REQUIRE(*pos == 'H');\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last_if(iterable, [](char c) { return c == 'H'; });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last_if(iterable, [](char c) { return c == 'a'; });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 5);\n    }\n}\n\nTEST_CASE(\"Find last if random access\") {\n    SUBCASE(\"With non-empty \") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 5 };\n        auto pos = lz::find_last_if(vec, [](int i) { return i == 5; });\n        REQUIRE(*pos == 5);\n        REQUIRE(lz::distance(vec.begin(), pos) == 5);\n    }\n\n    SUBCASE(\"With one element\") {\n        std::vector<int> vec = { 1 };\n        auto pos = lz::find_last_if(vec, [](int i) { return i == 1; });\n        REQUIRE(*pos == 1);\n        REQUIRE(lz::distance(vec.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"With empty\") {\n        std::vector<int> vec;\n        auto pos = lz::find_last_if(vec, [](int i) { return i == 1; });\n        REQUIRE(pos == vec.end());\n        REQUIRE(lz::distance(vec.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"Not found\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto pos = lz::find_last_if(vec, [](int i) { return i == 6; });\n        REQUIRE(pos == vec.end());\n        REQUIRE(lz::distance(vec.begin(), pos) == 5);\n    }\n}\n\nTEST_CASE(\"Search\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        const char* search = \"lo\";\n        auto searchIterable = lz::c_string(search);\n        auto it = lz::search(iterable, searchIterable);\n        REQUIRE(*it.first == 'l');\n        REQUIRE(it.second == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it.first) == 3);\n        REQUIRE(lz::distance(iterable.begin(), it.second) == 5);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        const char* search = \"H\";\n        auto searchIterable = lz::c_string(search);\n        auto it = lz::search(iterable, searchIterable);\n        REQUIRE(*it.first == 'H');\n        REQUIRE(it.second == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it.first) == 0);\n        REQUIRE(lz::distance(iterable.begin(), it.second) == 1);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        const char* search = \"H\";\n        auto searchIterable = lz::c_string(search);\n        auto it = lz::search(iterable, searchIterable);\n        REQUIRE(it.first == iterable.end());\n        REQUIRE(it.second == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it.first) == 0);\n        REQUIRE(lz::distance(iterable.begin(), it.second) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        const char* search = \"a\";\n        auto searchIterable = lz::c_string(search);\n        auto it = lz::search(iterable, searchIterable);\n        REQUIRE(it.first == iterable.end());\n        REQUIRE(it.second == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it.first) == 5);\n        REQUIRE(lz::distance(iterable.begin(), it.second) == 5);\n    }\n}\n\nTEST_CASE(\"Search random access\") {\n    SUBCASE(\"With non-empty\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n        std::vector<int> search = { 5, 6 };\n        auto it = lz::search(vec, search);\n        REQUIRE(*it.first == 5);\n        REQUIRE(*it.second == 7);\n        REQUIRE(lz::distance(vec.begin(), it.first) == 4);\n        REQUIRE(lz::distance(vec.begin(), it.second) == 6);\n        REQUIRE(std::distance(it.first, it.second) == 2);\n    }\n\n    SUBCASE(\"With one element\") {\n        std::vector<int> vec = { 1 };\n        std::vector<int> search = { 1 };\n        auto it = lz::search(vec, search);\n        REQUIRE(*it.first == 1);\n        REQUIRE(it.second == vec.end());\n        REQUIRE(lz::distance(vec.begin(), it.first) == 0);\n        REQUIRE(lz::distance(vec.begin(), it.second) == 1);\n    }\n\n    SUBCASE(\"With empty 1\") {\n        std::vector<int> vec = {};\n        std::vector<int> search = { 1, 2, 3, 4, 5 };\n        auto it = lz::search(vec, search);\n        REQUIRE(it.first == vec.end());\n        REQUIRE(it.second == vec.end());\n        REQUIRE(lz::distance(vec.begin(), it.first) == 0);\n        REQUIRE(lz::distance(vec.begin(), it.second) == 0);\n    }\n\n    SUBCASE(\"With empty 2\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        std::vector<int> search = {};\n        auto it = lz::search(vec, search);\n        REQUIRE(it.first == vec.begin());\n        REQUIRE(it.second == vec.begin());\n        REQUIRE(lz::distance(vec.begin(), it.first) == 0);\n        REQUIRE(lz::distance(vec.begin(), it.second) == 0);\n    }\n\n    SUBCASE(\"With empty 3\") {\n        std::vector<int> vec = {};\n        std::vector<int> search = {};\n        auto it = lz::search(vec, search);\n        REQUIRE(it.first == vec.end());\n        REQUIRE(it.second == vec.end());\n        REQUIRE(lz::distance(vec.begin(), it.first) == 0);\n        REQUIRE(lz::distance(vec.begin(), it.second) == 0);\n    }\n\n    SUBCASE(\"Not found\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        std::vector<int> search = { 5, 6 };\n        auto it = lz::search(vec, search);\n        REQUIRE(it.first == vec.end());\n        REQUIRE(it.second == vec.end());\n        REQUIRE(lz::distance(vec.begin(), it.first) == 5);\n        REQUIRE(lz::distance(vec.begin(), it.second) == 5);\n    }\n}\n\nTEST_CASE(\"Find if not\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_if_not(iterable, [](char c) { return c == 'H'; });\n        REQUIRE(*pos == 'e');\n        REQUIRE(lz::distance(iterable.begin(), pos) == 1);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_if_not(iterable, [](char c) { return c == 'H'; });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 1);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_if_not(iterable, [](char c) { return c == 'H'; });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_if_not(iterable, [](char c) { return lz::contains(lz::c_string(\"Hello\"), c); });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 5);\n    }\n}\n\nTEST_CASE(\"Find last if not\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last_if_not(iterable, [](char c) { return c == 'o'; });\n        REQUIRE(*pos == 'l');\n        REQUIRE(lz::distance(iterable.begin(), pos) == 3);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last_if_not(iterable, [](char c) { return c == 'H'; });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 1);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last_if_not(iterable, [](char c) { return c == 'H'; });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto pos = lz::find_last_if_not(iterable, [](char c) { return lz::contains(lz::c_string(\"Hello\"), c); });\n        REQUIRE(pos == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), pos) == 5);\n    }\n}\n\nTEST_CASE(\"Find last if not random access\") {\n    SUBCASE(\"With non-empty\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n        auto pos = lz::find_last_if_not(vec, [](int i) { return i == 5; });\n        REQUIRE(*pos == 8);\n        REQUIRE(lz::distance(vec.begin(), pos) == 7);\n    }\n\n    SUBCASE(\"With one element\") {\n        std::vector<int> vec = { 1 };\n        auto pos = lz::find_last_if_not(vec, [](int i) { return i != 1; });\n        REQUIRE(*pos == *vec.begin());\n        REQUIRE(lz::distance(vec.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"With empty\") {\n        std::vector<int> vec = {};\n        auto pos = lz::find_last_if_not(vec, [](int i) { return i == 1; });\n        REQUIRE(pos == vec.end());\n        REQUIRE(lz::distance(vec.begin(), pos) == 0);\n    }\n\n    SUBCASE(\"Not found\") {\n        std::vector<int> vec = { 5, 5, 5 };\n        auto pos = lz::find_last_if_not(vec, [](int i) { return i == 5; });\n        REQUIRE(pos == vec.end());\n        REQUIRE(lz::distance(vec.begin(), pos) == 3);\n    }\n}\n\nTEST_CASE(\"Find or default\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_or_default(iterable, 'o', 'a') == 'o');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_or_default(iterable, 'H', 'a') == 'H');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_or_default(iterable, 'H', char{}) == char{});\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_or_default(iterable, 'a', 'b') == 'b');\n    }\n}\n\nTEST_CASE(\"Find or default if\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_or_default_if(iterable, [](char c) { return c == 'o'; }, 'a') == 'o');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_or_default_if(iterable, [](char c) { return c == 'H'; }, 'a') == 'H');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_or_default_if(iterable, [](char c) { return c == 'H'; }, char{}) == char{});\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_or_default_if(iterable, [](char c) { return c == 'a'; }, 'b') == 'b');\n    }\n}\n\nTEST_CASE(\"Find last or default\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default(iterable, 'o', 'a') == 'o');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default(iterable, 'H', 'a') == 'H');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default(iterable, 'H', char{}) == char{});\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default(iterable, 'a', 'b') == 'b');\n    }\n}\n\nTEST_CASE(\"Find last or default random access\") {\n    SUBCASE(\"With non-empty\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n        REQUIRE(lz::find_last_or_default(vec, 5, 0) == 5);\n    }\n\n    SUBCASE(\"With one element\") {\n        std::vector<int> vec = { 1 };\n        REQUIRE(lz::find_last_or_default(vec, 1, 0) == 1);\n    }\n\n    SUBCASE(\"With empty\") {\n        std::vector<int> vec = {};\n        REQUIRE(lz::find_last_or_default(vec, 1, int{}) == 0);\n    }\n\n    SUBCASE(\"Not found\") {\n        std::vector<int> vec = { 1, 2, 3, 4 };\n        REQUIRE(lz::find_last_or_default(vec, 5, 0) == 0);\n    }\n}\n\nTEST_CASE(\"Find last or default if\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_if(iterable, [](char c) { return c == 'o'; }, 'a') == 'o');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_if(iterable, [](char c) { return c == 'H'; }, 'a') == 'H');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_if(iterable, [](char c) { return c == 'H'; }, char{}) == char{});\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_if(iterable, [](char c) { return c == 'a'; }, 'b') == 'b');\n    }\n}\n\nTEST_CASE(\"Find last or default if random access\") {\n    SUBCASE(\"With non-empty\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n        REQUIRE(lz::find_last_or_default_if(vec, [](int i) { return i == 5; }, 0) == 5);\n    }\n\n    SUBCASE(\"With one element\") {\n        std::vector<int> vec = { 1 };\n        REQUIRE(lz::find_last_or_default_if(vec, [](int i) { return i == 1; }, 0) == 1);\n    }\n\n    SUBCASE(\"With empty\") {\n        std::vector<int> vec = {};\n        REQUIRE(lz::find_last_or_default_if(vec, [](int i) { return i == 1; }, int{}) == 0);\n    }\n\n    SUBCASE(\"Not found\") {\n        std::vector<int> vec = { 5, 5, 5 };\n        REQUIRE(lz::find_last_or_default_if(vec, [](int i) { return i == 6; }, 0) == 0);\n    }\n}\n\nTEST_CASE(\"Find last or default if not random access\") {\n    SUBCASE(\"With non-empty\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n        REQUIRE(lz::find_last_or_default_if_not(vec, [](int i) { return i == 6; }, 0) == 5);\n    }\n\n    SUBCASE(\"With one element\") {\n        std::vector<int> vec = { 1 };\n        REQUIRE(lz::find_last_or_default_if_not(vec, [](int i) { return i == 1; }, 0) == 0);\n    }\n\n    SUBCASE(\"With empty\") {\n        std::vector<int> vec = {};\n        REQUIRE(lz::find_last_or_default_if_not(vec, [](int i) { return i == 1; }, int{}) == 0);\n    }\n\n    SUBCASE(\"Not found\") {\n        std::vector<int> vec = { 5, 5, 5 };\n        REQUIRE(lz::find_last_or_default_if_not(vec, [](int i) { return i == 5; }, 0) == 0);\n    }\n}\n\nTEST_CASE(\"Find last or default if not\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_if_not(iterable, [](char c) { return c == 'o'; }, 'a') == 'l');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_if_not(iterable, [](char c) { return c == 'H'; }, 'a') == 'a');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_if_not(iterable, [](char c) { return c == 'H'; }, char{}) == char{});\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_if_not(iterable, [](char c) { return c == 'a'; }, 'b') == 'o');\n    }\n}\n\nTEST_CASE(\"Find last or default not\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_not(iterable, 'o', 'a') == 'l');\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_not(iterable, 'H', 'a') == 'a');\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_not(iterable, 'H', char{}) == char{});\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::find_last_or_default_not(iterable, 'a', 'b') == 'o');\n    }\n}\n\nTEST_CASE(\"Find last or default not random access\") {\n    SUBCASE(\"With non-empty\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n        REQUIRE(lz::find_last_or_default_not(vec, 5, 0) == 6);\n    }\n\n    SUBCASE(\"With one element\") {\n        std::vector<int> vec = { 1 };\n        REQUIRE(lz::find_last_or_default_not(vec, 1, 0) == 0);\n    }\n\n    SUBCASE(\"With empty\") {\n        std::vector<int> vec = {};\n        REQUIRE(lz::find_last_or_default_not(vec, 1, int{}) == 0);\n    }\n\n    SUBCASE(\"Not found\") {\n        std::vector<int> vec = { 5, 5, 5 };\n        REQUIRE(lz::find_last_or_default_not(vec, 5, 0) == 0);\n    }\n}\n\nTEST_CASE(\"Index of\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Helloo\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::index_of(iterable, 'o') == 4);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::index_of(iterable, 'H') == 0);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::index_of(iterable, 'H') == lz::npos);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::index_of(iterable, 'a') == lz::npos);\n    }\n}\n\nTEST_CASE(\"Index of if\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::index_of_if(iterable, [](char c) { return c == 'o'; }) == 4);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::index_of_if(iterable, [](char c) { return c == 'H'; }) == 0);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::index_of_if(iterable, [](char c) { return c == 'H'; }) == lz::npos);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::index_of_if(iterable, [](char c) { return c == 'a'; }) == lz::npos);\n    }\n}\n\nTEST_CASE(\"Contains\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::contains(iterable, 'o'));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::contains(iterable, 'H'));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::contains(iterable, 'H'));\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::contains(iterable, 'a'));\n    }\n}\n\nTEST_CASE(\"Starts with\") {\n    SUBCASE(\"With non-empty c-string\") {\n        auto iterable = lz::c_string(\"Hello\");\n        auto iterable2 = lz::c_string(\"He\");\n        REQUIRE(lz::starts_with(iterable, iterable2));\n        iterable2 = lz::c_string(\"H\");\n        REQUIRE(lz::starts_with(iterable, iterable2));\n        iterable2 = lz::c_string(\"Hello\");\n        REQUIRE(lz::starts_with(iterable, iterable2));\n    }\n\n    SUBCASE(\"With one element\") {\n        auto iterable = lz::c_string(\"H\");\n        auto iterable2 = lz::c_string(\"H\");\n        REQUIRE(lz::starts_with(iterable, iterable2));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        auto iterable = lz::c_string(\"\");\n        auto iterable2 = lz::c_string(\"H\");\n        REQUIRE_FALSE(lz::starts_with(iterable, iterable2));\n\n        iterable = lz::c_string(\"H\");\n        iterable2 = lz::c_string(\"\");\n        REQUIRE(lz::starts_with(iterable, iterable2));\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        auto iterable = lz::c_string(\"Hello\");\n        auto iterable2 = lz::c_string(\"a\");\n        REQUIRE_FALSE(lz::starts_with(iterable, iterable2));\n\n        iterable2 = lz::c_string(\"Helloa\");\n        REQUIRE_FALSE(lz::starts_with(iterable, iterable2));\n    }\n\n    SUBCASE(\"With non-empty string\") {\n        auto iterable = std::string(\"Hello\");\n        auto iterable2 = std::string(\"He\");\n        REQUIRE(lz::starts_with(iterable, iterable2));\n    }\n\n    SUBCASE(\"With one element string\") {\n        auto iterable = std::string(\"H\");\n        auto iterable2 = std::string(\"H\");\n        REQUIRE(lz::starts_with(iterable, iterable2));\n    }\n\n    SUBCASE(\"With empty string\") {\n        auto iterable = std::string(\"\");\n        auto iterable2 = std::string(\"H\");\n        REQUIRE_FALSE(lz::starts_with(iterable, iterable2));\n\n        iterable = std::string(\"H\");\n        iterable2 = std::string(\"\");\n        REQUIRE(lz::starts_with(iterable, iterable2));\n    }\n\n    SUBCASE(\"Not found string\") {\n        auto iterable = std::string(\"Hello\");\n        auto iterable2 = std::string(\"a\");\n        REQUIRE_FALSE(lz::starts_with(iterable, iterable2));\n\n        iterable2 = std::string(\"Helloa\");\n        REQUIRE_FALSE(lz::starts_with(iterable, iterable2));\n    }\n}\n\nTEST_CASE(\"Ends with\") {\n    SUBCASE(\"Forward non sized\") {\n        auto iterable = lz::c_string(\"\");\n        auto iterable2 = lz::c_string(\"H\");\n        REQUIRE_FALSE(lz::ends_with(iterable, iterable2));\n\n        iterable2 = lz::c_string(\"\");\n        REQUIRE(lz::ends_with(iterable, iterable2));\n\n        iterable = lz::c_string(\"H\");\n        REQUIRE(lz::ends_with(iterable, iterable2));\n\n        iterable = lz::c_string(\"Hello\");\n        iterable2 = lz::c_string(\"o\");\n        REQUIRE(lz::ends_with(iterable, iterable2));\n        iterable2 = lz::c_string(\"lo\");\n        REQUIRE(lz::ends_with(iterable, iterable2));\n\n        iterable2 = lz::c_string(\"H\");\n        REQUIRE_FALSE(lz::ends_with(iterable, iterable2));\n    }\n\n    SUBCASE(\"Bidirectional sized\") {\n        std::list<char> lst;\n        std::list<char> lst2;\n        REQUIRE(lz::ends_with(lst, lst2));\n        lst = { 'H' };\n        REQUIRE(lz::ends_with(lst, lst2));\n        lst = {};\n        lst2 = { 'H' };\n        REQUIRE_FALSE(lz::ends_with(lst, lst2));\n        lst = { 'A', 'B' };\n        lst2 = { 'B' };\n        REQUIRE(lz::ends_with(lst, lst2));\n    }\n}\n\nTEST_CASE(\"Partition\") {\n    SUBCASE(\"With non-empty c-string\") {\n        char str[] = \"6789012345\";\n        auto iterable = lz::c_string(str);\n        auto partitioned = lz::partition(iterable, [](char c) { return c % 2 == 0; });\n\n        lz::basic_iterable<decltype(iterable.begin())> part1(iterable.begin(), partitioned);\n        lz::basic_iterable<decltype(iterable.begin()), decltype(iterable.end())> part2(partitioned, iterable.end());\n\n        REQUIRE(lz::all_of(part1, [](char c) { return c % 2 == 0; }));\n        REQUIRE(lz::none_of(part2, [](char c) { return c % 2 == 0; }));\n    }\n\n    SUBCASE(\"With one element\") {\n        char str[] = \"6\";\n        auto iterable = str | lz::c_string;\n        auto partitioned = lz::partition(iterable, [](char c) { return c % 2 == 0; });\n\n        lz::basic_iterable<decltype(iterable.begin())> part1(iterable.begin(), partitioned);\n        lz::basic_iterable<decltype(iterable.begin()), decltype(iterable.end())> part2(partitioned, iterable.end());\n\n        REQUIRE(lz::all_of(part1, [](char c) { return c % 2 == 0; }));\n        REQUIRE(lz::none_of(part2, [](char c) { return c % 2 == 0; }));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        char str[] = \"\";\n        auto iterable = lz::c_string(str);\n        auto partitioned = lz::partition(iterable, [](char c) { return c % 2 == 0; });\n\n        lz::basic_iterable<decltype(iterable.begin())> part1(iterable.begin(), partitioned);\n        lz::basic_iterable<decltype(iterable.begin()), decltype(iterable.end())> part2(partitioned, iterable.end());\n\n        REQUIRE(lz::all_of(part1, [](char c) { return c % 2 == 0; }));\n        REQUIRE(lz::none_of(part2, [](char c) { return c % 2 == 0; }));\n    }\n\n    SUBCASE(\"With all even c-string\") {\n        char str[] = \"2468\";\n        auto iterable = lz::c_string(str);\n        auto partitioned = lz::partition(iterable, [](char c) { return c % 2 == 0; });\n\n        lz::basic_iterable<decltype(iterable.begin())> part1(iterable.begin(), partitioned);\n        lz::basic_iterable<decltype(iterable.begin()), decltype(iterable.end())> part2(partitioned, iterable.end());\n\n        REQUIRE(lz::all_of(part1, [](char c) { return c % 2 == 0; }));\n        REQUIRE(lz::none_of(part2, [](char c) { return c % 2 == 0; }));\n    }\n\n    SUBCASE(\"With all odd c-string\") {\n        char str[] = \"13579\";\n        auto iterable = lz::c_string(str);\n        auto partitioned = lz::partition(iterable, [](char c) { return c % 2 == 0; });\n\n        lz::basic_iterable<decltype(iterable.begin())> part1(iterable.begin(), partitioned);\n        lz::basic_iterable<decltype(iterable.begin()), decltype(iterable.end())> part2(partitioned, iterable.end());\n\n        REQUIRE(lz::all_of(part2, [](char c) { return c % 2 != 0; }));\n        REQUIRE(lz::none_of(part1, [](char c) { return c % 2 != 0; }));\n    }\n}\n\nTEST_CASE(\"Mean\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::mean(iterable, std::plus<int>()) == doctest::Approx(('H' + 'e' + 'l' + 'l' + 'o') / 5.0));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::mean(iterable, std::plus<int>()) == doctest::Approx(static_cast<double>('H')));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::mean(iterable, std::plus<int>()) == doctest::Approx(0.0));\n    }\n}\n\nTEST_CASE(\"For each\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each(iterable, [&result](char c) { result += c; });\n        REQUIRE(result == \"Hello\");\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each(iterable, [&result](char c) { result += c; });\n        REQUIRE(result == \"H\");\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each(iterable, [&result](char c) { result += c; });\n        REQUIRE(result.empty());\n    }\n}\n\nTEST_CASE(\"For each while\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each_while(iterable, [&result](char c) {\n            result += c;\n            return c != 'l';\n        });\n        REQUIRE(result == \"Hel\");\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each_while(iterable, [&result](char c) {\n            result += c;\n            return c != 'H';\n        });\n        REQUIRE(result == \"H\");\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each_while(iterable, [&result](char c) {\n            if (c == 'H') {\n                return false;\n            }\n            result += c;\n            return true;\n        });\n        REQUIRE(result.empty());\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each_while(iterable, [&result](char c) {\n            result += c;\n            return c != 'H';\n        });\n        REQUIRE(result.empty());\n    }\n}\n\nTEST_CASE(\"For each while n\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each_while_n(iterable, 5, [&result](char c) {\n            result += c;\n            return c != 'l';\n        });\n        REQUIRE(result == \"Hel\");\n\n        result = \"\";\n        lz::for_each_while_n(iterable, 3, [&result](char c) {\n            result += c;\n            return c != 'o';\n        });\n        REQUIRE(result == \"Hel\");\n\n        result = \"\";\n        lz::for_each_while_n(iterable, 0, [&result](char c) {\n            result += c;\n            return c != 'o';\n        });\n        REQUIRE(result.empty());\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each_while_n(iterable, 1, [&result](char c) {\n            result += c;\n            return c != 'H';\n        });\n        REQUIRE(result == \"H\");\n\n        result = \"\";\n        lz::for_each_while_n(iterable, 0, [&result](char c) {\n            result += c;\n            return c != 'H';\n        });\n        REQUIRE(result.empty());\n\n        result = \"\";\n        lz::for_each_while_n(iterable, 10, [&result](char c) {\n            result += c;\n            return c != 'H';\n        });\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::for_each_while_n(iterable, 0, [&result](char c) {\n            result += c;\n            return c != 'H';\n        });\n        REQUIRE(result.empty());\n\n        result = \"\";\n        lz::for_each_while_n(iterable, 10, [&result](char c) {\n            result += c;\n            return c != 'H';\n        });\n        REQUIRE(result.empty());\n    }\n}\n\nTEST_CASE(\"Copy\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::copy(iterable, std::back_inserter(result));\n        REQUIRE(result == \"Hello\");\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::copy(iterable, std::back_inserter(result));\n        REQUIRE(result == \"H\");\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::copy(iterable, std::back_inserter(result));\n        REQUIRE(result.empty());\n    }\n}\n\nTEST_CASE(\"Transform\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::transform(iterable, std::back_inserter(result), [](char c) { return static_cast<char>(c + 1); });\n        REQUIRE(result == \"Ifmmp\");\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::transform(iterable, std::back_inserter(result), [](char c) { return static_cast<char>(c + 1); });\n        REQUIRE(result == \"I\");\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        std::string result;\n        lz::transform(iterable, std::back_inserter(result), [](char c) { return static_cast<char>(c + 1); });\n        REQUIRE(result.empty());\n    }\n}\n\nTEST_CASE(\"Equal\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        const char* str2 = \"Hello\";\n        auto iterable2 = lz::c_string(str2);\n        REQUIRE(lz::equal(iterable, iterable2));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        const char* str2 = \"H\";\n        auto iterable2 = lz::c_string(str2);\n        REQUIRE(lz::equal(iterable, iterable2));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        const char* str2 = \"\";\n        auto iterable2 = lz::c_string(str2);\n        REQUIRE(lz::equal(iterable, iterable2));\n    }\n\n    SUBCASE(\"Not equal c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        const char* str2 = \"Helloo\";\n        auto iterable2 = lz::c_string(str2);\n        REQUIRE_FALSE(lz::equal(iterable, iterable2));\n    }\n}\n\nTEST_CASE(\"Lower bound\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"aaabcccdeee\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::lower_bound(iterable, 'c');\n        REQUIRE(*it == 'c');\n        REQUIRE(lz::distance(iterable.begin(), it) == 4);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::lower_bound(iterable, 'H');\n        REQUIRE(*it == 'H');\n        REQUIRE(lz::distance(iterable.begin(), it) == 0);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::lower_bound(iterable, 'H');\n        REQUIRE(it == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"aaabcccdeee\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::lower_bound(iterable, 'f');\n        REQUIRE(it == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it) == 11);\n    }\n}\n\nTEST_CASE(\"Upper bound\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"aaabcccdeee\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::upper_bound(iterable, 'c');\n        REQUIRE(*it == 'd');\n        REQUIRE(lz::distance(iterable.begin(), it) == 7);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::upper_bound(iterable, 'H');\n        REQUIRE(it == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it) == 1);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::upper_bound(iterable, 'H');\n        REQUIRE(it == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"aaabcccdeee\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::upper_bound(iterable, 'f');\n        REQUIRE(it == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it) == 11);\n    }\n}\n\nTEST_CASE(\"Binary search\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"aaabcccdeee\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::binary_search(iterable, 'c'));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::binary_search(iterable, 'H'));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::binary_search(iterable, 'H'));\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"aaabcccdeee\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::binary_search(iterable, 'f'));\n    }\n}\n\nTEST_CASE(\"All of\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::all_of(iterable, [](char c) { return c != 'a'; }));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::all_of(iterable, [](char c) { return c == 'H'; }));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::all_of(iterable, [](char c) { return c == 'H'; }));\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::all_of(iterable, [](char c) { return c == 'a'; }));\n    }\n}\n\nTEST_CASE(\"Any of\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::any_of(iterable, [](char c) { return c == 'o'; }));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::any_of(iterable, [](char c) { return c == 'H'; }));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::any_of(iterable, [](char c) { return c == 'H'; }));\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::any_of(iterable, [](char c) { return c == 'a'; }));\n    }\n}\n\nTEST_CASE(\"None of\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::none_of(iterable, [](char c) { return c == 'a'; }));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::none_of(iterable, [](char c) { return c == 'a'; }));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::none_of(iterable, [](char c) { return c == 'a'; }));\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::none_of(iterable, [](char c) { return c == 'o'; }));\n    }\n}\n\nTEST_CASE(\"Adjacent find\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::adjacent_find(iterable);\n        REQUIRE(*it == 'l');\n        REQUIRE(lz::distance(iterable.begin(), it) == 2);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::adjacent_find(iterable);\n        REQUIRE(it == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it) == 1);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::adjacent_find(iterable);\n        REQUIRE(it == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        auto it = lz::adjacent_find(iterable, [](char a, char b) { return a == 'a' && b == 'a'; });\n        REQUIRE(it == iterable.end());\n        REQUIRE(lz::distance(iterable.begin(), it) == 5);\n    }\n}\n\nTEST_CASE(\"Count\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::count(iterable, 'l') == 2);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::count(iterable, 'H') == 1);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::count(iterable, 'H') == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::count(iterable, 'a') == 0);\n    }\n}\n\nTEST_CASE(\"Count if\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::count_if(iterable, [](char c) { return c == 'l'; }) == 2);\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::count_if(iterable, [](char c) { return c == 'H'; }) == 1);\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::count_if(iterable, [](char c) { return c == 'H'; }) == 0);\n    }\n\n    SUBCASE(\"Not found c-string\") {\n        const char* str = \"Hello\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::count_if(iterable, [](char c) { return c == 'a'; }) == 0);\n    }\n}\n\nTEST_CASE(\"Is sorted\") {\n    SUBCASE(\"With non-empty c-string\") {\n        const char* str = \"abcde\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::is_sorted(iterable));\n    }\n\n    SUBCASE(\"With one element\") {\n        const char* str = \"H\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::is_sorted(iterable));\n    }\n\n    SUBCASE(\"With empty c-string\") {\n        const char* str = \"\";\n        auto iterable = lz::c_string(str);\n        REQUIRE(lz::is_sorted(iterable));\n    }\n\n    SUBCASE(\"Not sorted c-string\") {\n        const char* str = \"abcdea\";\n        auto iterable = lz::c_string(str);\n        REQUIRE_FALSE(lz::is_sorted(iterable));\n    }\n}\n"
  },
  {
    "path": "tests/any_iterable.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/any_iterable.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/enumerate.hpp>\n#include <Lz/iter_tools.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/take.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Any iterable with sentinels\") {\n    lz::any_iterable<char, const char&> iterable = \"Hello, World!\" | lz::c_string;\n    auto expected = \"Hello, World!\";\n    REQUIRE(lz::equal(iterable, lz::c_string(expected)));\n}\n\nTEST_CASE(\"Empty or one element any iterable\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> vec;\n        lz::any_iterable<int, int&> iterable = vec;\n        REQUIRE(lz::empty(iterable));\n        REQUIRE_FALSE(lz::has_one(iterable));\n        REQUIRE_FALSE(lz::has_many(iterable));\n    }\n\n    SUBCASE(\"One element\") {\n        std::vector<int> vec = { 1 };\n        lz::any_iterable<int, int&> iterable = vec;\n        REQUIRE_FALSE(lz::empty(iterable));\n        REQUIRE(lz::has_one(iterable));\n        REQUIRE_FALSE(lz::has_many(iterable));\n    }\n}\n\nTEST_CASE(\"Creating a basic any iterable from std::vector, random access iterator\") {\n    std::vector<int> vec = { 1, 2, 3, 4, 5 };\n    lz::any_iterable<int, int&, std::random_access_iterator_tag> view = vec;\n    auto expected = { 1, 2, 3, 4, 5 };\n\n    REQUIRE(lz::equal(view, expected));\n    REQUIRE(lz::equal(view | lz::reverse, expected | lz::reverse));\n\n    test_procs::test_operator_plus(view, expected);\n    test_procs::test_operator_minus(view);\n}\n\nTEST_CASE(\"Creating a basic any iterable from std::list, forward iterator\") {\n    std::list<int> lst = { 1, 2, 3, 4, 5 };\n    lz::any_iterable<int, int&, std::forward_iterator_tag> view = lst;\n    auto expected = { 1, 2, 3, 4, 5 };\n\n    REQUIRE(lz::equal(view, expected));\n}\n\nTEST_CASE(\"Creating a basic any iterable from std::list, bidirectional iterator\") {\n    std::list<int> lst = { 1, 2, 3, 4, 5 };\n    lz::any_iterable<int, int&, std::bidirectional_iterator_tag> view = lst;\n    auto expected = { 1, 2, 3, 4, 5 };\n\n    REQUIRE(lz::equal(view, expected));\n    REQUIRE(lz::equal(view | lz::reverse, expected | lz::reverse));\n}\n\nconst auto eq_fn = [](const std::pair<int, int&> p1, const std::pair<int, int>& p2) {\n    return p1.first == p2.first && p1.second == p2.second;\n};\n\nTEST_CASE(\"Creating a complex any iterable, std::forward_iterator_tag\") {\n    std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n\n    lz::any_iterable<std::pair<int, int>, std::pair<int, int&>, std::forward_iterator_tag> view =\n#ifdef LZ_HAS_CXX_11\n        vec | lz::as<int&>{} | lz::enumerate | lz::take(lz::ssize(vec));\n#else\n        vec | lz::as<int&> | lz::enumerate | lz::take(lz::ssize(vec));\n#endif\n\n    auto expected = { std::make_pair(0, 1), std::make_pair(1, 2), std::make_pair(2, 3),\n                      std::make_pair(3, 4), std::make_pair(4, 5), std::make_pair(5, 6) };\n    REQUIRE(lz::equal(view, expected, eq_fn));\n}\n\nTEST_CASE(\"Creating a complex any iterable, std::bidirectional_iterator_tag\") {\n    std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n\n    lz::any_iterable<std::pair<int, int>, std::pair<int, int&>, std::bidirectional_iterator_tag> view =\n#ifdef LZ_HAS_CXX_11\n        vec | lz::as<int&>{} | lz::enumerate | lz::take(lz::ssize(vec));\n#else\n        vec | lz::as<int&> | lz::enumerate | lz::take(lz::ssize(vec));\n#endif\n\n    auto expected = { std::make_pair(0, 1), std::make_pair(1, 2), std::make_pair(2, 3),\n                      std::make_pair(3, 4), std::make_pair(4, 5), std::make_pair(5, 6) };\n    REQUIRE(lz::equal(view, expected, eq_fn));\n}\n\nTEST_CASE(\"Creating a complex any iterable, std::random_access_iterator_tag\") {\n    std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n\n    lz::any_iterable<std::pair<int, int>, std::pair<int, int&>, std::random_access_iterator_tag> view =\n#ifdef LZ_HAS_CXX_11\n        vec | lz::as<int&>{} | lz::enumerate | lz::take(lz::ssize(vec));\n#else\n        vec | lz::as<int&> | lz::enumerate | lz::take(lz::ssize(vec));\n#endif\n\n    auto expected = { std::make_pair(0, 1), std::make_pair(1, 2), std::make_pair(2, 3),\n                      std::make_pair(3, 4), std::make_pair(4, 5), std::make_pair(5, 6) };\n    REQUIRE(lz::equal(view, expected, eq_fn));\n    REQUIRE(lz::equal(view | lz::reverse, expected | lz::reverse, eq_fn));\n    test_procs::test_operator_plus(view, expected, eq_fn);\n    test_procs::test_operator_minus(view);\n}\n\nTEST_CASE(\"Any iterable with different SBO sizes\") {\n    SUBCASE(\"Both fit\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        static_assert(sizeof(vec.begin()) <= 64, \"\");\n        static_assert(sizeof(vec.end()) <= 64, \"\");\n        lz::any_iterable<int, int&, std::random_access_iterator_tag> view = vec;\n        auto expected = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::equal(view, expected));\n        REQUIRE(lz::equal(view | lz::reverse, expected | lz::reverse));\n        test_procs::test_operator_plus(view, expected);\n        test_procs::test_operator_minus(view);\n    }\n\n    SUBCASE(\"Both do not fit\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n\n        std::array<char, 64> buf{};\n        auto mapper = lz::map(vec, [buf](int& i) -> int& {\n            static_cast<void>(buf);\n            return i;\n        });\n        static_assert(sizeof(mapper.begin()) > 64, \"\");\n        static_assert(sizeof(mapper.end()) > 64, \"\");\n\n        lz::any_iterable<int, int&, std::random_access_iterator_tag> view = mapper;\n        auto expected = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::equal(view, expected));\n        REQUIRE(lz::equal(view | lz::reverse, expected | lz::reverse));\n        test_procs::test_operator_plus(view, expected);\n        test_procs::test_operator_minus(view);\n    }\n\n    SUBCASE(\"Iter does not fit, sentinel does fit\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n\n        std::array<char, 64> buf{};\n        // Decay to forward so that filt returns sentinel\n        auto filt = vec | lz::iter_decay(std::forward_iterator_tag{}) | lz::filter([buf](int&) {\n                        static_cast<void>(buf);\n                        return true;\n                    });\n\n        static_assert(sizeof(filt.begin()) > 64, \"\");\n        static_assert(sizeof(filt.end()) == sizeof(lz::default_sentinel_t), \"\");\n\n        lz::any_iterable<int, int&, std::forward_iterator_tag> view = filt;\n        auto expected = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::equal(view, expected));\n    }\n}\n"
  },
  {
    "path": "tests/as_iterator.cpp",
    "content": "#include <Lz/algorithm/equal.hpp>\n#include <Lz/as_iterator.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/pairwise.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/util/string_view.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\n\nTEST_CASE(\"operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        auto cstr = lz::c_string(\"hello, world!\");\n        auto as_it = lz::as_iterator(cstr);\n        auto common = make_sentinel_assign_op_tester(as_it);\n        auto expected = { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!' };\n        REQUIRE(lz::equal(common, expected, [](decltype(*common.begin()) a, char b) { return *a == b; }));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5 };\n        auto sentinelled = make_sized_bidi_sentinelled(lst);\n        auto common = make_sentinel_assign_op_tester(lz::as_iterator(sentinelled));\n        auto expected = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::equal(common, expected, [](decltype(*common.begin()) a, int b) { return *a == b; }));\n        REQUIRE(\n            lz::equal(common | lz::reverse, expected | lz::reverse, [](decltype(*common.begin()) a, int b) { return *a == b; }));\n    }\n\n    SUBCASE(\"random access\") {\n        auto repeat = lz::repeat(20, 5);\n        auto as_it = make_sentinel_assign_op_tester(lz::as_iterator(repeat));\n        auto expected = { 20, 20, 20, 20, 20 };\n        REQUIRE(lz::equal(as_it, expected, [](decltype(*as_it.begin()) a, int b) { return *a == b; }));\n        REQUIRE(\n            lz::equal(as_it | lz::reverse, expected | lz::reverse, [](decltype(*as_it.begin()) a, int b) { return *a == b; }));\n        test_procs::test_operator_minus(as_it);\n        test_procs::test_operator_plus(as_it, expected, [](decltype(*as_it.begin()) a, int b) { return *a == b; });\n    }\n}\n\nTEST_CASE(\"With sentinels\") {\n    SUBCASE(\"Equal to c_string\") {\n        auto cstr = lz::c_string(\"Hello, World!\");\n        lz::string_view expected = \"Hello, World!\";\n        auto it = lz::as_iterator(cstr);\n        using iterator = decltype(*it.begin());\n\n        REQUIRE(lz::equal(it, expected, [](iterator iter, char c) { return *iter == c; }));\n    }\n\n    SUBCASE(\"Equal to random access iterable\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto expected = { 1, 2, 3, 4, 5 };\n        auto it = vec | lz::as_iterator(std::random_access_iterator_tag{});\n        using iterator = decltype(*it.begin());\n        REQUIRE(lz::equal(it, expected, [](iterator iter, int value) { return *iter == value; }));\n\n        lz::as_iterator_iterable<const std::vector<int>, std::forward_iterator_tag> as_iterable =\n            vec | lz::as_iterator(std::forward_iterator_tag{});\n        static_assert(std::is_same<decltype(as_iterable.begin()), decltype(as_iterable.end())>::value,\n                      \"begin and end should return the same type\");\n        static_assert(!lz::detail::is_ra<decltype(as_iterable.begin())>::value,\n                      \"as_iterator_iterable should return a random access iterator\");\n    }\n\n    SUBCASE(\"Operator minus basics\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto it = vec | lz::as_iterator;\n        auto first = it.begin();\n        auto last = it.end();\n        REQUIRE(last - first == 5);\n        REQUIRE(first - last == -5);\n    }\n\n    SUBCASE(\"operator minus with number\") {\n        const std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto it = lz::as_iterator(vec);\n        auto first = it.begin();\n        auto last = it.end();\n        REQUIRE(first + 2 == std::next(first, 2));\n        REQUIRE(last - 2 == std::next(last, -2));\n    }\n}\n"
  },
  {
    "path": "tests/c_string.cpp",
    "content": "#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/util/string_view.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Empty or one element c_string\") {\n    SUBCASE(\"Empty\") {\n        auto cstr = lz::c_string(\"\");\n        REQUIRE(lz::empty(cstr));\n        REQUIRE_FALSE(lz::has_one(cstr));\n        REQUIRE_FALSE(lz::has_many(cstr));\n    }\n\n    SUBCASE(\"One element\") {\n        auto cstr = lz::c_string(\"a\");\n        REQUIRE_FALSE(lz::empty(cstr));\n        REQUIRE(lz::has_one(cstr));\n        REQUIRE_FALSE(lz::has_many(cstr));\n    }\n}\n\nTEST_CASE(\"CString binary operations\") {\n    const char string[] = \"123 456 789\";\n    lz::c_string_iterable<const char> c_string = string | lz::c_string;\n\n    SUBCASE(\"Operator++\") {\n        lz::string_view expected1 = \"123 456 789\";\n        REQUIRE(lz::equal(c_string, expected1));\n    }\n\n    SUBCASE(\"Operator bool\") {\n        REQUIRE(c_string.begin());\n        auto tmp = lz::c_string(\"\");\n        REQUIRE_FALSE(tmp.begin());\n        tmp = lz::c_string_iterable<const char>{};\n        REQUIRE_FALSE(tmp.begin());\n    }\n\n    SUBCASE(\"Operator=(defatult_sentinel_t)\") {\n        const char* s = \"hello, world!\";\n        auto cstr = lz::c_string(s);\n        auto common = make_sentinel_assign_op_tester(cstr);\n        auto expected = { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!' };\n        REQUIRE(lz::equal(common, expected));\n    }\n}\n\nTEST_CASE(\"CString to containers\") {\n    auto str = lz::c_string(\"Hello, World!\");\n\n    SUBCASE(\"To array\") {\n        std::array<char, 14> expected = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' };\n        REQUIRE((str | lz::to<std::array<char, 14>>()) == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        std::vector<char> expected = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' };\n        REQUIRE((str | lz::to<std::vector>()) == expected);\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        std::list<char> expected = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' };\n        REQUIRE((str | lz::to<std::list>()) == expected);\n    }\n\n    SUBCASE(\"To map\") {\n        std::map<char, char> expected = { { 'H', 'H' }, { 'e', 'e' }, { 'l', 'l' }, { 'o', 'o' }, { ',', ',' },\n                                          { ' ', ' ' }, { 'W', 'W' }, { 'r', 'r' }, { 'd', 'd' }, { '!', '!' } };\n        REQUIRE((str | lz::map([](char c) { return std::make_pair(c, c); }) | lz::to<std::map<char, char>>()) == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        std::unordered_map<char, char> expected = { { 'H', 'H' }, { 'e', 'e' }, { 'l', 'l' }, { 'o', 'o' }, { ',', ',' },\n                                                    { ' ', ' ' }, { 'W', 'W' }, { 'r', 'r' }, { 'd', 'd' }, { '!', '!' } };\n        REQUIRE((str | lz::map([](char c) { return std::make_pair(c, c); }) | lz::to<std::unordered_map<char, char>>()) ==\n                expected);\n    }\n}\n"
  },
  {
    "path": "tests/cached_size.cpp",
    "content": "#include <Lz/algorithm/equal.hpp>\n#include <Lz/cached_size.hpp>\n#include <Lz/chunks.hpp>\n#include <Lz/enumerate.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/range.hpp>\n#include <Lz/traits/is_sized.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Correct size\") {\n    auto to_filter = lz::range(10);\n    auto filtered = to_filter | lz::filter([](int i) { return i % 2 == 0; });\n    static_assert(!lz::is_sized<decltype(filtered)>::value, \"Filtered should not be sized\");\n    lz::cached_size_iterable<decltype(filtered)> cached = filtered | lz::cache_size;\n\n    REQUIRE(cached.size() == 5);\n    std::vector<int> expected = { 0, 2, 4, 6, 8 };\n    REQUIRE(lz::equal(cached, expected));\n}\n\nTEST_CASE(\"Correct size i.c.m. with other iterators\") {\n    auto to_filter = lz::range(10);\n    auto filtered = to_filter | lz::filter([](int i) { return i % 2 == 0; });\n\n    auto iterable = filtered | lz::cache_size | lz::chunks(3) | lz::enumerate;\n\n    REQUIRE(iterable.size() == 2);\n    std::vector<std::pair<int, std::vector<int>>> expected{ { 0, { 0, 2, 4 } }, { 1, { 6, 8 } } };\n\n    using r1 = lz::detail::ref_iterable_t<decltype(iterable)>;\n    using r2 = lz::detail::ref_iterable_t<decltype(expected)>;\n\n    REQUIRE(lz::equal(iterable, expected, [](r1 p1, r2 p2) { return p1.first == p2.first && lz::equal(p1.second, p2.second); }));\n}\n"
  },
  {
    "path": "tests/cartesian_product.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/cartesian_product.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/distance.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"with sentinel\") {\n    const char* str = \"abc\";\n    const char* str2 = \"def\";\n    auto cstr1 = lz::c_string(str);\n    auto cstr2 = lz::c_string(str2);\n    auto cart = cstr1 | lz::cartesian_product(cstr2);\n\n    static_assert(!std::is_same<decltype(cart.begin()), decltype(cart.end())>::value, \"Should not be the same\");\n    REQUIRE(static_cast<std::size_t>(lz::distance(cart)) == std::strlen(str) * std::strlen(str2));\n\n    auto expected = { std::make_tuple('a', 'd'), std::make_tuple('a', 'e'), std::make_tuple('a', 'f'),\n                      std::make_tuple('b', 'd'), std::make_tuple('b', 'e'), std::make_tuple('b', 'f'),\n                      std::make_tuple('c', 'd'), std::make_tuple('c', 'e'), std::make_tuple('c', 'f') };\n    REQUIRE(lz::equal(cart, expected));\n}\n\nTEST_CASE(\"Operator=\") {\n    auto expected2 = { std::make_tuple(1, 3), std::make_tuple(1, 4), std::make_tuple(2, 3), std::make_tuple(2, 4) };\n\n    SUBCASE(\"forward\") {\n        std::forward_list<int> a = { 1, 2 };\n        std::forward_list<int> b = { 3, 4 };\n        auto cartesian = lz::cartesian_product(a, b);\n        auto common = make_sentinel_assign_op_tester(cartesian);\n        REQUIRE(lz::equal(common, expected2));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::list<int> a = { 1, 2 };\n        std::list<int> b = { 3, 4 };\n        auto cartesian = lz::cartesian_product(a, b);\n        auto common = make_sentinel_assign_op_tester(make_sized_bidi_sentinelled(cartesian));\n        REQUIRE(lz::equal(common, expected2));\n        REQUIRE(lz::equal(common | lz::reverse, expected2 | lz::reverse));\n    }\n\n    SUBCASE(\"random access\") {\n        auto repeater1 = lz::repeat(1, 2);\n        auto repeater2 = lz::repeat(3, 2);\n        auto cartesian = lz::cartesian_product(repeater1, repeater2);\n        std::vector<std::tuple<int, int>> expected3 = { std::make_tuple(1, 3), std::make_tuple(1, 3), std::make_tuple(1, 3),\n                                                        std::make_tuple(1, 3) };\n        auto ra_op_tester = make_sentinel_assign_op_tester(cartesian);\n        REQUIRE(lz::equal(ra_op_tester, expected3));\n        REQUIRE(lz::equal(ra_op_tester | lz::reverse, expected3 | lz::reverse));\n        test_procs::test_operator_minus(ra_op_tester);\n        test_procs::test_operator_plus(ra_op_tester, expected3);\n    }\n}\n\nTEST_CASE(\"Empty or one element cartesian product\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> vec;\n        lz::cartesian_product_iterable<std::vector<int>, std::vector<int>> cart = lz::cartesian_product(vec, vec);\n        REQUIRE(lz::empty(cart));\n        REQUIRE_FALSE(lz::has_one(cart));\n        REQUIRE_FALSE(lz::has_many(cart));\n        REQUIRE(cart.size() == 0);\n    }\n\n    SUBCASE(\"One element\") {\n        std::vector<int> vec = { 1 };\n        auto cart = lz::cartesian_product(vec, vec);\n        REQUIRE_FALSE(lz::empty(cart));\n        REQUIRE(lz::has_one(cart));\n        REQUIRE_FALSE(lz::has_many(cart));\n        REQUIRE(cart.size() == 1);\n    }\n\n    SUBCASE(\"One element and zero elements combined\") {\n        std::vector<int> vec = { 1 };\n        std::vector<int> vec2;\n        auto cart = lz::cartesian_product(vec, vec2);\n        REQUIRE(lz::empty(cart));\n        REQUIRE_FALSE(lz::has_one(cart));\n        REQUIRE_FALSE(lz::has_many(cart));\n        REQUIRE(cart.size() == 0);\n\n        cart = lz::cartesian_product(vec2, vec);\n        REQUIRE(lz::empty(cart));\n        REQUIRE_FALSE(lz::has_one(cart));\n        REQUIRE_FALSE(lz::has_many(cart));\n        REQUIRE(cart.size() == 0);\n    }\n\n    SUBCASE(\"One element and zero elements combined with sentinels\") {\n        auto cstr = lz::c_string(\"H\");\n        auto cstr2 = lz::c_string(\"\");\n        auto cart = lz::cartesian_product(cstr, cstr2);\n        REQUIRE(lz::empty(cart));\n        REQUIRE_FALSE(lz::has_one(cart));\n        REQUIRE_FALSE(lz::has_many(cart));\n\n        cart = lz::cartesian_product(cstr2, cstr);\n        REQUIRE(lz::empty(cart));\n        REQUIRE_FALSE(lz::has_one(cart));\n        REQUIRE_FALSE(lz::has_many(cart));\n    }\n}\n\nTEST_CASE(\"Cartesian product binary operations\") {\n    std::vector<int> vec = { 1, 2 };\n    std::vector<char> chars = { 'a', 'b', 'c' };\n    std::vector<char> chars2 = { 'a', 'b' };\n    auto cartesian = vec | lz::cartesian_product(chars, chars2);\n\n    REQUIRE(cartesian.size() == vec.size() * chars.size() * chars2.size());\n\n    static_assert(std::is_same<decltype(cartesian.begin()), decltype(cartesian.end())>::value, \"Should be the same\");\n\n    std::vector<std::tuple<int, char, char>> expected = {\n        std::make_tuple(1, 'a', 'a'), std::make_tuple(1, 'a', 'b'), std::make_tuple(1, 'b', 'a'), std::make_tuple(1, 'b', 'b'),\n        std::make_tuple(1, 'c', 'a'), std::make_tuple(1, 'c', 'b'), std::make_tuple(2, 'a', 'a'), std::make_tuple(2, 'a', 'b'),\n        std::make_tuple(2, 'b', 'a'), std::make_tuple(2, 'b', 'b'), std::make_tuple(2, 'c', 'a'), std::make_tuple(2, 'c', 'b')\n    };\n\n    SUBCASE(\"Operator++\") {\n        REQUIRE(lz::equal(cartesian, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        REQUIRE(lz::equal(lz::reverse(cartesian), lz::reverse(expected)));\n    }\n\n    SUBCASE(\"Operator+\") {\n        test_procs::test_operator_plus(cartesian, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(cartesian);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto repeat1 = lz::repeat(1, 3);\n        auto repeat2 = lz::repeat(2, 4);\n        auto cartesian2 = repeat1 | lz::cartesian_product(repeat2);\n        REQUIRE(lz::size(cartesian2) == 12);\n        test_procs::test_operator_minus(cartesian2);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto repeat1 = lz::repeat(1, 3);\n        auto repeat2 = lz::repeat(2, 4);\n        auto cartesian2 = repeat1 | lz::cartesian_product(repeat2);\n        std::vector<std::tuple<int, int>> expected2 = { std::make_tuple(1, 2), std::make_tuple(1, 2), std::make_tuple(1, 2),\n                                                        std::make_tuple(1, 2), std::make_tuple(1, 2), std::make_tuple(1, 2),\n                                                        std::make_tuple(1, 2), std::make_tuple(1, 2), std::make_tuple(1, 2),\n                                                        std::make_tuple(1, 2), std::make_tuple(1, 2), std::make_tuple(1, 2) };\n        test_procs::test_operator_plus(cartesian2, expected2);\n    }\n}\n\nTEST_CASE(\"CartesianProduct to containers\") {\n    std::vector<int> vec = { 1, 2, 3 };\n    std::vector<char> chars = { 'a', 'b', 'c' };\n    std::vector<char> chars2 = { 'a', 'b' };\n    auto cartesian = lz::cartesian_product(vec, chars, chars2);\n\n    SUBCASE(\"To array\") {\n        auto actual = cartesian | lz::to<std::array<std::tuple<int, char, char>, 18>>();\n        std::array<std::tuple<int, char, char>, 18> expected = {\n            std::make_tuple(1, 'a', 'a'), std::make_tuple(1, 'a', 'b'), std::make_tuple(1, 'b', 'a'),\n            std::make_tuple(1, 'b', 'b'), std::make_tuple(1, 'c', 'a'), std::make_tuple(1, 'c', 'b'),\n            std::make_tuple(2, 'a', 'a'), std::make_tuple(2, 'a', 'b'), std::make_tuple(2, 'b', 'a'),\n            std::make_tuple(2, 'b', 'b'), std::make_tuple(2, 'c', 'a'), std::make_tuple(2, 'c', 'b'),\n            std::make_tuple(3, 'a', 'a'), std::make_tuple(3, 'a', 'b'), std::make_tuple(3, 'b', 'a'),\n            std::make_tuple(3, 'b', 'b'), std::make_tuple(3, 'c', 'a'), std::make_tuple(3, 'c', 'b')\n        };\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To vector reversed\") {\n        auto actual = lz::reverse(cartesian) | lz::to<std::vector>();\n        std::vector<std::tuple<int, char, char>> expected = {\n            std::make_tuple(3, 'c', 'b'), std::make_tuple(3, 'c', 'a'), std::make_tuple(3, 'b', 'b'),\n            std::make_tuple(3, 'b', 'a'), std::make_tuple(3, 'a', 'b'), std::make_tuple(3, 'a', 'a'),\n            std::make_tuple(2, 'c', 'b'), std::make_tuple(2, 'c', 'a'), std::make_tuple(2, 'b', 'b'),\n            std::make_tuple(2, 'b', 'a'), std::make_tuple(2, 'a', 'b'), std::make_tuple(2, 'a', 'a'),\n            std::make_tuple(1, 'c', 'b'), std::make_tuple(1, 'c', 'a'), std::make_tuple(1, 'b', 'b'),\n            std::make_tuple(1, 'b', 'a'), std::make_tuple(1, 'a', 'b'), std::make_tuple(1, 'a', 'a')\n        };\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To list\") {\n        auto actual = cartesian | lz::to<std::list>();\n        std::list<std::tuple<int, char, char>> expected = {\n            std::make_tuple(1, 'a', 'a'), std::make_tuple(1, 'a', 'b'), std::make_tuple(1, 'b', 'a'),\n            std::make_tuple(1, 'b', 'b'), std::make_tuple(1, 'c', 'a'), std::make_tuple(1, 'c', 'b'),\n            std::make_tuple(2, 'a', 'a'), std::make_tuple(2, 'a', 'b'), std::make_tuple(2, 'b', 'a'),\n            std::make_tuple(2, 'b', 'b'), std::make_tuple(2, 'c', 'a'), std::make_tuple(2, 'c', 'b'),\n            std::make_tuple(3, 'a', 'a'), std::make_tuple(3, 'a', 'b'), std::make_tuple(3, 'b', 'a'),\n            std::make_tuple(3, 'b', 'b'), std::make_tuple(3, 'c', 'a'), std::make_tuple(3, 'c', 'b'),\n        };\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        auto actual = cartesian | lz::to<std::vector>();\n        std::vector<std::tuple<int, char, char>> expected = {\n            std::make_tuple(1, 'a', 'a'), std::make_tuple(1, 'a', 'b'), std::make_tuple(1, 'b', 'a'),\n            std::make_tuple(1, 'b', 'b'), std::make_tuple(1, 'c', 'a'), std::make_tuple(1, 'c', 'b'),\n            std::make_tuple(2, 'a', 'a'), std::make_tuple(2, 'a', 'b'), std::make_tuple(2, 'b', 'a'),\n            std::make_tuple(2, 'b', 'b'), std::make_tuple(2, 'c', 'a'), std::make_tuple(2, 'c', 'b'),\n            std::make_tuple(3, 'a', 'a'), std::make_tuple(3, 'a', 'b'), std::make_tuple(3, 'b', 'a'),\n            std::make_tuple(3, 'b', 'b'), std::make_tuple(3, 'c', 'a'), std::make_tuple(3, 'c', 'b'),\n        };\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To map\") {\n        using elm_type = std::tuple<int, char, char>;\n        auto actual = cartesian | lz::map([](const elm_type& elm) {\n                          return std::make_pair(std::get<0>(elm), std::make_pair(std::get<1>(elm), std::get<2>(elm)));\n                      }) |\n                      lz::to<std::map<int, std::pair<char, char>>>();\n        std::map<int, std::pair<char, char>> expected = { { 1, { 'a', 'a' } }, { 1, { 'a', 'b' } }, { 1, { 'b', 'a' } },\n                                                          { 1, { 'b', 'b' } }, { 1, { 'c', 'a' } }, { 1, { 'c', 'b' } },\n                                                          { 2, { 'a', 'a' } }, { 2, { 'a', 'b' } }, { 2, { 'b', 'a' } },\n                                                          { 2, { 'b', 'b' } }, { 2, { 'c', 'a' } }, { 2, { 'c', 'b' } },\n                                                          { 3, { 'a', 'a' } }, { 3, { 'a', 'b' } }, { 3, { 'b', 'a' } },\n                                                          { 3, { 'b', 'b' } }, { 3, { 'c', 'a' } }, { 3, { 'c', 'b' } } };\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        using elm_type = std::tuple<int, char, char>;\n        auto actual = cartesian | lz::map([](const elm_type& elm) {\n                          return std::make_pair(std::get<0>(elm), std::make_pair(std::get<1>(elm), std::get<2>(elm)));\n                      }) |\n                      lz::to<std::unordered_map<int, std::pair<char, char>>>();\n        std::unordered_map<int, std::pair<char, char>> expected = {\n            { 1, { 'a', 'a' } }, { 1, { 'a', 'b' } }, { 1, { 'b', 'a' } }, { 1, { 'b', 'b' } }, { 1, { 'c', 'a' } },\n            { 1, { 'c', 'b' } }, { 2, { 'a', 'a' } }, { 2, { 'a', 'b' } }, { 2, { 'b', 'a' } }, { 2, { 'b', 'b' } },\n            { 2, { 'c', 'a' } }, { 2, { 'c', 'b' } }, { 3, { 'a', 'a' } }, { 3, { 'a', 'b' } }, { 3, { 'b', 'a' } },\n            { 3, { 'b', 'b' } }, { 3, { 'c', 'a' } }, { 3, { 'c', 'b' } }\n        };\n        REQUIRE(actual == expected);\n    }\n}\n"
  },
  {
    "path": "tests/chunk_if.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/chunk_if.hpp>\n#include <Lz/procs/to.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Chunk if custom value type\") {\n    auto str = lz::c_string(\";hello;world;\");\n    std::function<bool(char)> func = [](char c) noexcept {\n        return c == ';';\n    };\n#ifdef LZ_HAS_CXX_11\n    lz::t_chunk_if_iterable<std::vector<char>, decltype(str), decltype(func)> chunked =\n        str | lz::t_chunk_if<std::vector<char>>{}(std::move(func));\n#else\n    lz::t_chunk_if_iterable<std::vector<char>, decltype(str), decltype(func)> chunked =\n        str | lz::t_chunk_if<std::vector<char>>(std::move(func));\n#endif\n    std::vector<std::vector<char>> expected = { {}, { 'h', 'e', 'l', 'l', 'o' }, { 'w', 'o', 'r', 'l', 'd' }, {} };\n    REQUIRE(lz::equal(chunked, expected));\n}\n\nTEST_CASE(\"chunk if operator=(default_sentinel)\") {\n    auto fun = [](int a) {\n        return a % 2 == 0;\n    };\n\n    std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n    auto chunked = lz::chunk_if(vec, fun);\n    using value_type = lz::detail::val_iterable_t<decltype(chunked)>;\n    auto common = make_sentinel_assign_op_tester(chunked);\n    std::vector<std::vector<int>> expected = { { 1 }, { 3 }, { 5 }, {} };\n    REQUIRE(lz::equal(common, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n    vec = { 1, 2, 3, 4, 5 };\n    chunked = lz::chunk_if(vec, fun);\n    common = make_sentinel_assign_op_tester(chunked);\n    expected = { { 1 }, { 3 }, { 5 } };\n    REQUIRE(lz::equal(common, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n}\n\nTEST_CASE(\"Chunk if with sentinels\") {\n    auto cstr = lz::c_string(\"hello world; this is a message;;\");\n\n    std::function<bool(char)> predicate = [](char c) noexcept {\n        return c == ';';\n    };\n    lz::chunk_if_iterable<decltype(cstr), decltype(predicate)> chunked = lz::chunk_if(cstr, std::move(predicate));\n\n    std::vector<std::vector<char>> expected = { { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' },\n                                                { ' ', 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 'm', 'e', 's', 's', 'a',\n                                                  'g', 'e' },\n                                                {},\n                                                {} };\n    REQUIRE(lz::equal(chunked, expected,\n                      [](const decltype(chunked)::value_type a, const std::vector<char>& b) { return lz::equal(a, b); }));\n}\n\nTEST_CASE(\"Non string literal test\") {\n    std::function<bool(int)> is_even = [](int i) noexcept {\n        return i % 2 == 0;\n    };\n    std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n    auto chunked = lz::chunk_if(arr, is_even);\n    std::vector<std::vector<int>> expected = { { 1 }, { 3 }, { 5 } };\n    REQUIRE(lz::equal(chunked, expected,\n                      [](const decltype(chunked)::value_type& a, const std::vector<int>& b) { return lz::equal(a, b); }));\n}\n\nTEST_CASE(\"Empty or one element chunk_if\") {\n    SUBCASE(\"Empty\") {\n        std::string s;\n        auto chunked = lz::chunk_if(s, [](char c) { return c == ';'; });\n        REQUIRE(lz::empty(chunked));\n        REQUIRE_FALSE(lz::has_one(chunked));\n        REQUIRE_FALSE(lz::has_many(chunked));\n    }\n\n    SUBCASE(\"One element\") {\n        std::string s = \";\";\n        auto chunked = s | lz::chunk_if([](char c) { return c == ';'; });\n        REQUIRE_FALSE(lz::empty(chunked));\n        REQUIRE_FALSE(lz::has_one(chunked));\n        REQUIRE(lz::has_many(chunked));\n    }\n\n    SUBCASE(\"One element that does not satisfy predicate\") {\n        std::string s = \"h\";\n        auto chunked = lz::chunk_if(s, [](char c) { return c == ';'; });\n        REQUIRE_FALSE(lz::empty(chunked));\n        REQUIRE(lz::has_one(chunked));\n        REQUIRE_FALSE(lz::has_many(chunked));\n    }\n}\n\nTEST_CASE(\"chunk_if variations\") {\n    SUBCASE(\"Ending and starting with delimiter\") {\n        std::string s = \";hello world;; this is a message;;; testing;;\";\n        auto chunked = lz::sv_chunk_if(s, [](const char c) { return c == ';'; });\n        auto vec = chunked | lz::to<std::vector<lz::string_view>>();\n        std::vector<lz::string_view> expected = { \"\", \"hello world\", \"\", \" this is a message\", \"\", \"\", \" testing\", \"\", \"\" };\n        REQUIRE(vec == expected);\n    }\n\n    SUBCASE(\"Ending with two one delimiter\") {\n        std::string s = \"hello world; this is a message;\";\n        auto chunked = lz::sv_chunk_if(s, [](const char c) { return c == ';'; });\n        auto vec = chunked | lz::to<std::vector>();\n        std::vector<std::string> expected = { \"hello world\", \" this is a message\", \"\" };\n        REQUIRE(lz::equal(vec, expected));\n    }\n}\n\nTEST_CASE(\"chunk_if binary operations\") {\n    std::string s = \";hello world;; this is a message;;; testing;;\";\n    auto chunked = lz::sv_chunk_if(s, [](const char c) { return c == ';'; });\n    static_assert(!std::is_same<decltype(chunked.begin()), decltype(chunked.end())>::value, \"Must be sentinel\");\n    REQUIRE(*chunked.begin() == \"\");\n\n    SUBCASE(\"Operator++\") {\n        std::vector<lz::string_view> expected = { \"\", \"hello world\", \"\", \" this is a message\", \"\", \"\", \" testing\", \"\", \"\" };\n        REQUIRE(lz::equal(chunked, expected, [](lz::string_view a, lz::string_view b) { return lz::equal(a, b); }));\n    }\n}\n\nTEST_CASE(\"chunk_if to containers\") {\n    std::string s = \"hello world; this is a message;;\";\n    auto chunked = lz::sv_chunk_if(s, [](const char c) { return c == ';'; });\n\n    SUBCASE(\"To array\") {\n        REQUIRE(lz::distance(chunked) == 4);\n        auto arr = chunked | lz::to<std::array<lz::string_view, 4>>();\n        REQUIRE(arr == decltype(arr){ lz::string_view{ \"hello world\" }, lz::string_view{ \" this is a message\" },\n                                      lz::string_view{}, lz::string_view{} });\n    }\n\n    SUBCASE(\"To vector\") {\n        auto vec = chunked | lz::to<std::vector>();\n        REQUIRE(vec == decltype(vec){ lz::string_view{ \"hello world\" }, lz::string_view{ \" this is a message\" },\n                                      lz::string_view{}, lz::string_view{} });\n    }\n\n    SUBCASE(\"To other container\") {\n        auto list = chunked | lz::to<std::list>();\n        REQUIRE(list == decltype(list){ lz::string_view{ \"hello world\" }, lz::string_view{ \" this is a message\" },\n                                        lz::string_view{}, lz::string_view{} });\n    }\n}\n"
  },
  {
    "path": "tests/chunks.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/chunks.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/distance.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"empty or one element chunks\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> vec;\n        auto chunked = lz::chunks(vec, 3);\n        REQUIRE(lz::empty(chunked));\n        REQUIRE_FALSE(lz::has_one(chunked));\n        REQUIRE_FALSE(lz::has_many(chunked));\n        REQUIRE(chunked.size() == 0);\n    }\n\n    SUBCASE(\"One element\") {\n        std::vector<int> vec = { 1 };\n        auto chunked = lz::chunks(vec, 3);\n        REQUIRE_FALSE(lz::empty(chunked));\n        REQUIRE(lz::has_one(chunked));\n        REQUIRE_FALSE(lz::has_many(chunked));\n        REQUIRE(chunked.size() == 1);\n    }\n}\n\nTEST_CASE(\"Chunks binary operations random access\") {\n    std::vector<int> even_size = { 1, 2, 3, 4, 5, 6, 7, 8 };\n    std::vector<int> uneven_size = { 1, 2, 3, 4, 5, 6, 7 };\n\n    auto uneven_chunksize_uneven_size = lz::chunks(uneven_size, 3);\n    auto even_chunksize_uneven_size = lz::chunks(uneven_size, 2);\n\n    auto uneven_chunksize_even_size = lz::chunks(even_size, 3);\n    auto even_chunksize_even_size = lz::chunks(even_size, 2);\n\n    REQUIRE(lz::ssize(uneven_chunksize_even_size) == lz::distance(uneven_chunksize_even_size));\n    REQUIRE(lz::ssize(even_chunksize_even_size) == lz::distance(even_chunksize_even_size));\n    REQUIRE(lz::ssize(uneven_chunksize_uneven_size) == lz::distance(uneven_chunksize_uneven_size));\n    REQUIRE(lz::ssize(even_chunksize_uneven_size) == lz::distance(even_chunksize_uneven_size));\n\n    static_assert(std::is_same<decltype(uneven_chunksize_even_size.begin()), decltype(uneven_chunksize_even_size.end())>::value,\n                  \"Should not be sentinel\");\n\n    using iterable = typename decltype(uneven_chunksize_even_size)::value_type;\n    const auto equal_fn = [](iterable a, const std::vector<int>& b) {\n        return lz::equal(a, b);\n    };\n\n    SUBCASE(\"Operator++\") {\n        std::vector<std::vector<int>> expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8 } };\n        REQUIRE(lz::equal(uneven_chunksize_even_size, expected, equal_fn));\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };\n        REQUIRE(lz::equal(even_chunksize_even_size, expected, equal_fn));\n\n        expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\n        REQUIRE(lz::equal(uneven_chunksize_uneven_size, expected, equal_fn));\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7 } };\n        REQUIRE(lz::equal(even_chunksize_uneven_size, expected, equal_fn));\n    }\n\n    SUBCASE(\"Operator--\") {\n        std::vector<std::vector<int>> expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8 } };\n        REQUIRE(lz::equal(uneven_chunksize_even_size | lz::cached_reverse, expected | lz::reverse, equal_fn));\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };\n        REQUIRE(lz::equal(even_chunksize_even_size | lz::cached_reverse, expected | lz::reverse, equal_fn));\n\n        expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\n        REQUIRE(lz::equal(uneven_chunksize_uneven_size | lz::cached_reverse, expected | lz::reverse, equal_fn));\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7 } };\n        REQUIRE(lz::equal(even_chunksize_uneven_size | lz::cached_reverse, expected | lz::reverse, equal_fn));\n    }\n\n    SUBCASE(\"Operator+\") {\n        std::vector<std::vector<int>> expected;\n\n        expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8 } };\n        test_procs::test_operator_plus(uneven_chunksize_even_size, expected, equal_fn);\n\n        expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\n        test_procs::test_operator_plus(uneven_chunksize_uneven_size, expected, equal_fn);\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };\n        test_procs::test_operator_plus(even_chunksize_even_size, expected, equal_fn);\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7 } };\n        test_procs::test_operator_plus(even_chunksize_uneven_size, expected, equal_fn);\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(uneven_chunksize_even_size);\n        test_procs::test_operator_minus(even_chunksize_even_size);\n        test_procs::test_operator_minus(uneven_chunksize_uneven_size);\n        test_procs::test_operator_minus(even_chunksize_uneven_size);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto uneven_chunksize_uneven_size_repeat = lz::chunks(lz::repeat(0, 3), 3);\n        auto even_chunksize_uneven_size_repeat = lz::chunks(lz::repeat(0, 3), 2);\n        auto uneven_chunksize_even_size_repeat = lz::chunks(lz::repeat(0, 2), 3);\n        auto even_chunksize_even_size_repeat = lz::chunks(lz::repeat(0, 2), 2);\n\n        test_procs::test_operator_minus(uneven_chunksize_uneven_size_repeat);\n        test_procs::test_operator_minus(even_chunksize_uneven_size_repeat);\n        test_procs::test_operator_minus(uneven_chunksize_even_size_repeat);\n        test_procs::test_operator_minus(even_chunksize_even_size_repeat);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto uneven_chunksize_uneven_size_repeat = lz::chunks(lz::repeat(0, 7), 3);\n        auto even_chunksize_uneven_size_repeat = lz::chunks(lz::repeat(0, 7), 2);\n        auto uneven_chunksize_even_size_repeat = lz::chunks(lz::repeat(0, 6), 3);\n        auto even_chunksize_even_size_repeat = lz::chunks(lz::repeat(0, 4), 2);\n\n        using value_type = typename decltype(uneven_chunksize_even_size_repeat)::value_type;\n        const auto equal_fn2 = [](value_type a, const std::vector<int>& b) {\n            return lz::equal(a, b);\n        };\n\n        std::vector<std::vector<int>> expected;\n\n        expected = { { 0, 0, 0 }, { 0, 0, 0 }, { 0 } };\n        test_procs::test_operator_plus(uneven_chunksize_uneven_size_repeat, expected, equal_fn2);\n\n        expected = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } };\n        test_procs::test_operator_plus(even_chunksize_uneven_size_repeat, expected, equal_fn2);\n\n        expected = { { 0, 0, 0 }, { 0, 0, 0 } };\n        test_procs::test_operator_plus(uneven_chunksize_even_size_repeat, expected, equal_fn2);\n\n        expected = { { 0, 0 }, { 0, 0 } };\n        test_procs::test_operator_plus(even_chunksize_even_size_repeat, expected, equal_fn2);\n    }\n}\n\nTEST_CASE(\"Chunks binary operations bidirectional access\") {\n    std::list<int> even_size = { 1, 2, 3, 4, 5, 6, 7, 8 };\n    std::list<int> uneven_size = { 1, 2, 3, 4, 5, 6, 7 };\n\n    auto uneven_chunksize_uneven_size = lz::chunks(uneven_size, 3);\n    auto even_chunksize_uneven_size = lz::chunks(uneven_size, 2);\n\n    auto uneven_chunksize_even_size = lz::chunks(even_size, 3);\n    auto even_chunksize_even_size = lz::chunks(even_size, 2);\n\n    REQUIRE(lz::ssize(uneven_chunksize_even_size) ==\n            lz::distance(uneven_chunksize_even_size.begin(), uneven_chunksize_even_size.end()));\n    REQUIRE(lz::ssize(even_chunksize_even_size) ==\n            lz::distance(even_chunksize_even_size.begin(), even_chunksize_even_size.end()));\n    REQUIRE(lz::ssize(uneven_chunksize_uneven_size) ==\n            lz::distance(uneven_chunksize_uneven_size.begin(), uneven_chunksize_uneven_size.end()));\n    REQUIRE(lz::ssize(even_chunksize_uneven_size) ==\n            lz::distance(even_chunksize_uneven_size.begin(), even_chunksize_uneven_size.end()));\n\n    static_assert(std::is_same<decltype(uneven_chunksize_even_size.begin()), decltype(uneven_chunksize_even_size.end())>::value,\n                  \"Should not be sentinel\");\n\n    using iterable = typename decltype(uneven_chunksize_even_size)::value_type;\n    const auto equal_fn = [](iterable a, const std::vector<int>& b) {\n        return lz::equal(a, b);\n    };\n\n    SUBCASE(\"Operator++\") {\n        std::vector<std::vector<int>> expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8 } };\n        REQUIRE(lz::equal(uneven_chunksize_even_size, expected, equal_fn));\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };\n        REQUIRE(lz::equal(even_chunksize_even_size, expected, equal_fn));\n\n        expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\n        REQUIRE(lz::equal(uneven_chunksize_uneven_size, expected, equal_fn));\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7 } };\n        REQUIRE(lz::equal(even_chunksize_uneven_size, expected, equal_fn));\n    }\n\n    SUBCASE(\"Operator--\") {\n        std::vector<std::vector<int>> expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8 } };\n        REQUIRE(lz::equal(uneven_chunksize_even_size | lz::cached_reverse, expected | lz::reverse, equal_fn));\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };\n        REQUIRE(lz::equal(even_chunksize_even_size | lz::cached_reverse, expected | lz::reverse, equal_fn));\n\n        expected = { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } };\n        REQUIRE(lz::equal(uneven_chunksize_uneven_size | lz::cached_reverse, expected | lz::reverse, equal_fn));\n\n        expected = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7 } };\n        REQUIRE(lz::equal(even_chunksize_uneven_size | lz::cached_reverse, expected | lz::reverse, equal_fn));\n    }\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    std::vector<std::vector<int>> expected;\n\n    SUBCASE(\"forward\") {\n        std::forward_list<int> fwd = { 1, 2, 3, 4, 5 };\n        auto chunked = lz::chunks(fwd, 2);\n        using value_type = lz::detail::val_iterable_t<decltype(chunked)>;\n        auto common = make_sentinel_assign_op_tester(chunked);\n        expected = { { 1, 2 }, { 3, 4 }, { 5 } };\n        REQUIRE(lz::equal(common, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n\n    SUBCASE(\"random access\") {\n        auto repeater = lz::repeat(20, 5);\n        auto repeated_chunk = make_sentinel_assign_op_tester(lz::chunks(repeater, 2));\n        using value_type_2 = lz::detail::val_iterable_t<decltype(repeated_chunk)>;\n        expected = { { 20, 20 }, { 20, 20 }, { 20 } };\n        REQUIRE(lz::equal(repeated_chunk, expected, [](value_type_2 a, const std::vector<int>& b) { return lz::equal(a, b); }));\n        REQUIRE(lz::equal(repeated_chunk | lz::reverse, expected | lz::reverse,\n                          [](value_type_2 a, const std::vector<int>& b) { return lz::equal(a, b); }));\n        test_procs::test_operator_minus(repeated_chunk);\n        test_procs::test_operator_plus(repeated_chunk, expected,\n                                       [](value_type_2 a, const std::vector<int>& b) { return lz::equal(a, b); });\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5, 6 };\n        auto bidi_sentinel = make_sized_bidi_sentinelled(lst);\n        auto lst_chunked = make_sentinel_assign_op_tester(lz::chunks(bidi_sentinel, 3));\n        using value_type_3 = lz::detail::val_iterable_t<decltype(lst_chunked)>;\n        expected = { { 1, 2, 3 }, { 4, 5, 6 } };\n        REQUIRE(lz::equal(lst_chunked, expected, [](value_type_3 a, const std::vector<int>& b) { return lz::equal(a, b); }));\n        REQUIRE(lz::equal(lst_chunked | lz::reverse, expected | lz::reverse,\n                          [](value_type_3 a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n}\n\nTEST_CASE(\"Chunks with sentinels / fwd\") {\n    auto even_size = lz::c_string(\"12345678\");\n    auto uneven_size = lz::c_string(\"1234567\");\n\n    auto uneven_chunksize_uneven_size = lz::chunks(uneven_size, 3);\n    auto even_chunksize_uneven_size = lz::chunks(uneven_size, 2);\n\n    auto uneven_chunksize_even_size = lz::chunks(even_size, 3);\n    auto even_chunksize_even_size = lz::chunks(even_size, 2);\n\n    static_assert(!std::is_same<decltype(uneven_chunksize_even_size.begin()), decltype(uneven_chunksize_even_size.end())>::value,\n                  \"Should not be sentinel\");\n\n    using iterable = typename decltype(uneven_chunksize_even_size)::value_type;\n    const auto equal_fn = [](iterable a, const std::vector<char>& b) {\n        return lz::equal(a, b);\n    };\n\n    SUBCASE(\"Operator++\") {\n        std::vector<std::vector<char>> expected = { { '1', '2', '3' }, { '4', '5', '6' }, { '7', '8' } };\n        REQUIRE(lz::equal(uneven_chunksize_even_size, expected, equal_fn));\n\n        expected = { { '1', '2' }, { '3', '4' }, { '5', '6' }, { '7', '8' } };\n        REQUIRE(lz::equal(even_chunksize_even_size, expected, equal_fn));\n\n        expected = { { '1', '2', '3' }, { '4', '5', '6' }, { '7' } };\n        REQUIRE(lz::equal(uneven_chunksize_uneven_size, expected, equal_fn));\n\n        expected = { { '1', '2' }, { '3', '4' }, { '5', '6' }, { '7' } };\n        REQUIRE(lz::equal(even_chunksize_uneven_size, expected, equal_fn));\n    }\n}\n\nTEST_CASE(\"Chunks to containers\") {\n    std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8 };\n    auto chunked = lz::chunks(v, 3);\n    using value_type_t = decltype(chunked.begin())::value_type;\n\n    SUBCASE(\"To array\") {\n        auto arrays = chunked | lz::map([](value_type_t chunk) { return chunk | lz::to<std::array<int, 3>>(); }) |\n                      lz::to<std::array<std::array<int, 3>, 3>>();\n\n        std::array<std::array<int, 3>, 3> expected = { std::array<int, 3>{ 1, 2, 3 }, std::array<int, 3>{ 4, 5, 6 },\n                                                       std::array<int, 3>{ 7, 8 } };\n\n        REQUIRE(arrays == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        auto vectors =\n            chunked | lz::map([](value_type_t chunk) { return chunk | lz::to<std::vector>(); }) | lz::to<std::vector>();\n\n        std::vector<std::vector<int>> expected = { std::vector<int>{ 1, 2, 3 }, std::vector<int>{ 4, 5, 6 },\n                                                   std::vector<int>{ 7, 8 } };\n\n        REQUIRE(vectors == expected);\n    }\n\n    SUBCASE(\"To other container using to()\") {\n        auto lists = chunked | lz::map([](value_type_t chunk) { return chunk | lz::to<std::list>(); }) | lz::to<std::list>();\n\n        std::list<std::list<int>> expected = { std::list<int>{ 1, 2, 3 }, std::list<int>{ 4, 5, 6 }, std::list<int>{ 7, 8 } };\n\n        REQUIRE(lists == expected);\n    }\n}\n"
  },
  {
    "path": "tests/common.cpp",
    "content": "#include <Lz/algorithm/equal.hpp>\n#include <Lz/cached_size.hpp>\n#include <Lz/chunk_if.hpp>\n#include <Lz/chunks.hpp>\n#include <Lz/common.hpp>\n#include <Lz/generate.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nclass random_access_non_sized : lz::lazy_view {\n    lz::repeat_iterable<int> _repeater;\n\npublic:\n    random_access_non_sized() = default;\n\n    explicit random_access_non_sized(lz::repeat_iterable<int> rep) : _repeater{ rep } {\n    }\n\n    lz::iter_t<lz::repeat_iterable<int>> begin() const {\n        return _repeater.begin();\n    }\n\n    lz::sentinel_t<lz::repeat_iterable<int>> end() const {\n        return _repeater.end();\n    }\n};\n\nTEST_CASE(\"Common\") {\n    SUBCASE(\"No sentinel\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto&& common = lz::common(vec);\n        static_assert(std::is_same<decltype(common), std::vector<int>&>::value, \"\");\n        REQUIRE(lz::equal(common, vec));\n    }\n\n    SUBCASE(\"forward non sized\") {\n        std::string str = \"hello, world!\";\n        auto chunk = lz::sv_chunk_if(str, [](char c) { return c == ','; });\n        auto common = lz::common(chunk);\n        static_assert(std::is_same<decltype(common), lz::basic_iterable<lz::iter_t<decltype(common)>>>::value, \"\");\n        std::vector<lz::string_view> expected = { \"hello\", \" world!\" };\n        static_assert(!lz::is_sized<decltype(common)>::value, \"\");\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"forward sized\") {\n        auto gen = lz::generate([]() { return 1; }, 3);\n        auto expected = { 1, 1, 1 };\n        auto common = lz::common(gen);\n        static_assert(std::is_same<decltype(common), lz::sized_iterable<lz::iter_t<decltype(gen)>>>::value, \"\");\n        REQUIRE(lz::size(common) == 3);\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"bidirectional non sized\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto filter = make_non_sized_bidi_sentinelled(lz::filter(vec, [](int) { return true; }));\n        auto common = lz::common(filter);\n\n        std::vector<int> expected = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::equal(common, expected));\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"bidirectional sized\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5 };\n        auto chunks = lz::chunks(lst, 2);\n        auto sent = make_sized_bidi_sentinelled(chunks) | lz::cache_size;\n        auto common = lz::common(sent);\n\n        using common_t = decltype(common);\n        static_assert(std::is_same<common_t, lz::sized_iterable<lz::iter_t<common_t>, lz::sentinel_t<common_t>>>::value, \"\");\n        static_assert(lz::is_sized<decltype(common)>::value, \"\");\n        using value_type = decltype(*common.begin());\n\n        std::vector<std::vector<int>> expected = { { 1, 2 }, { 3, 4 }, { 5 } };\n        REQUIRE(lz::size(common) == 3);\n        REQUIRE(lz::equal(common, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse,\n                          [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n\n    SUBCASE(\"random access non sized\") {\n        random_access_non_sized non_sized_vec{ lz::repeat(1, 5) };\n        auto common = lz::common(non_sized_vec);\n        static_assert(std::is_same<decltype(common),\n                                   lz::basic_iterable<lz::iter_t<decltype(common)>, lz::sentinel_t<decltype(common)>>>::value,\n                      \"\");\n        static_assert(lz::is_sized<decltype(common)>::value, \"\"); // because random access\n        REQUIRE(lz::size(common) == 5);\n        auto expected = { 1, 1, 1, 1, 1 };\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"random access sized\") {\n        auto rep = lz::repeat(1, 5);\n        auto common = lz::common(rep);\n        using common_t = decltype(common);\n        static_assert(std::is_same<common_t, lz::basic_iterable<lz::iter_t<common_t>, lz::sentinel_t<common_t>>>::value, \"\");\n        static_assert(lz::is_sized<decltype(common)>::value, \"\");\n        REQUIRE(lz::size(common) == 5);\n        auto expected = { 1, 1, 1, 1, 1 };\n        REQUIRE(lz::equal(common, expected));\n    }\n}\n"
  },
  {
    "path": "tests/concatenate.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/concatenate.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/range.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/util/string_view.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Concatenate with sentinels\") {\n    const char* str = \"hello, world!\";\n    auto cstr = lz::c_string(str);\n\n    std::vector<char> vec = { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!' };\n    lz::concatenate_iterable<decltype(cstr), decltype(vec)> concat = lz::concat(cstr, vec);\n    static_assert(std::is_same<lz::default_sentinel_t, decltype(concat.end())>::value,\n                  \"Sentinel type should be default_sentinel_t\");\n    auto expected = { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!',\n                      'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!' };\n    REQUIRE(lz::equal(concat, expected));\n}\n\nTEST_CASE(\"operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> a = { 1, 2 };\n        std::forward_list<int> b = { 3, 4 };\n        auto concatenated = lz::concat(a, b);\n        auto common = make_sentinel_assign_op_tester(concatenated);\n        auto expected2 = { 1, 2, 3, 4 };\n        REQUIRE(lz::equal(common, expected2));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> a = { 1, 2 };\n        std::list<int> b = { 3, 4 };\n        auto concatenated = lz::concat(a, b);\n        auto bidi_sentinel = make_sized_bidi_sentinelled(concatenated);\n        auto common = make_sentinel_assign_op_tester(bidi_sentinel);\n        auto expected2 = { 1, 2, 3, 4 };\n        REQUIRE(lz::equal(common, expected2));\n        REQUIRE(lz::equal(lz::reverse(common), lz::reverse(expected2)));\n    }\n\n    SUBCASE(\"random access\") {\n        auto repeater1 = lz::repeat(1, 2);\n        auto repeater2 = lz::repeat(3, 2);\n        auto concatenated = lz::concat(repeater1, repeater2);\n        auto common = make_sentinel_assign_op_tester(concatenated);\n        auto expected2 = { 1, 1, 3, 3 };\n        REQUIRE(lz::equal(common, expected2));\n        REQUIRE(lz::equal(lz::reverse(common), lz::reverse(expected2)));\n        test_procs::test_operator_minus(common);\n        test_procs::test_operator_plus(common, expected2);\n    }\n}\n\nTEST_CASE(\"Reference tests\") {\n    std::string a = \"hello \";\n    const std::string b = \"world\";\n\n    auto concat = a | lz::concat(b);\n    REQUIRE(concat.size() == a.size() + b.size());\n    static_assert(std::is_same<decltype(concat.begin()), decltype(concat.end())>::value, \"Should not be sentinel\");\n\n    auto range = lz::range(0);\n    const std::vector<int> vec = { 1, 2, 3 };\n    std::vector<int> vec2 = { 4, 5, 6 };\n    auto concat2 = lz::concat(range, vec, vec2);\n    using t = decltype(*concat2.begin());\n    static_assert(std::is_same<int, t>::value, \"Should be by value\");\n\n    auto concat3 = lz::concat(vec, vec2, range);\n    using t2 = decltype(*concat3.begin());\n    static_assert(std::is_same<int, t2>::value, \"Should be by value\");\n\n    auto concat4 = lz::concat(vec2, vec, range);\n    using t3 = decltype(*concat4.begin());\n    static_assert(std::is_same<int, t3>::value, \"Should be by value\");\n\n    volatile const int arr[] = { 0 };\n    auto concat5 = lz::concat(range, arr);\n    using t4 = decltype(*concat5.begin());\n    static_assert(std::is_same<int, t4>::value, \"Should be by value\");\n\n    int arr2[] = { 0 };\n    auto concat6 = lz::concat(arr2, arr);\n    using t5 = decltype(*concat6.begin());\n    static_assert(std::is_same<const volatile int&, t5>::value, \"Should be by const volatile reference\");\n\n    using t6 = decltype(*concat.begin());\n    static_assert(std::is_same<const char&, t6>::value, \"Should be const\");\n}\n\nTEST_CASE(\"Empty or one element concatenate\") {\n    SUBCASE(\"Empty\") {\n        std::string a;\n        std::string b;\n        auto concat = lz::concat(a, b);\n        REQUIRE(concat.size() == 0);\n        REQUIRE(lz::empty(concat));\n        REQUIRE_FALSE(lz::has_one(concat));\n        REQUIRE_FALSE(lz::has_many(concat));\n    }\n\n    SUBCASE(\"One element 1\") {\n        std::string a = \"h\";\n        std::string b;\n        auto concat = lz::concat(a, b);\n        REQUIRE(concat.size() == 1);\n        REQUIRE_FALSE(lz::empty(concat));\n        REQUIRE(lz::has_one(concat));\n        REQUIRE_FALSE(lz::has_many(concat));\n    }\n\n    SUBCASE(\"One element 2\") {\n        std::string a;\n        std::string b = \"w\";\n        auto concat = lz::concat(a, b);\n        REQUIRE(concat.size() == 1);\n        REQUIRE_FALSE(lz::empty(concat));\n        REQUIRE(lz::has_one(concat));\n        REQUIRE_FALSE(lz::has_many(concat));\n    }\n\n    SUBCASE(\"One element both\") {\n        std::string a = \"h\";\n        std::string b = \"w\";\n        auto concat = lz::concat(a, b);\n        REQUIRE(concat.size() == 2);\n        REQUIRE_FALSE(lz::empty(concat));\n        REQUIRE_FALSE(lz::has_one(concat));\n        REQUIRE(lz::has_many(concat));\n    }\n}\n\nTEST_CASE(\"Concat binary operations\") {\n    std::string a = \"hello \", b = \"world\";\n    auto concat = lz::concat(a, b);\n\n    SUBCASE(\"Operator++\") {\n        lz::string_view expected = \"hello world\";\n        REQUIRE(lz::equal(concat, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        lz::string_view expected = \"hello world\";\n        REQUIRE(lz::equal(lz::reverse(concat), lz::reverse(expected)));\n    }\n\n    SUBCASE(\"Operator== & operator!=\") {\n        auto it = concat.begin();\n        REQUIRE(it == concat.begin());\n        REQUIRE(it != concat.end());\n        REQUIRE(concat.end() != it);\n        REQUIRE(concat.begin() == it);\n        it = concat.end();\n        REQUIRE(it == concat.end());\n        REQUIRE(it != concat.begin());\n        REQUIRE(concat.end() == it);\n        REQUIRE(concat.begin() != it);\n    }\n\n    SUBCASE(\"Operator+\") {\n        lz::string_view expected = \"hello world\";\n        test_procs::test_operator_plus(concat, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(concat);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto rep1 = lz::repeat(42, 5);\n        auto rep2 = lz::repeat(43, 5);\n        auto concat_rep = lz::concat(rep1, rep2);\n        test_procs::test_operator_minus(concat_rep);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto rep1 = lz::repeat(42, 5);\n        auto rep2 = lz::repeat(43, 5);\n        auto concat_rep = lz::concat(rep1, rep2);\n        std::vector<int> expected = { 42, 42, 42, 42, 42, 43, 43, 43, 43, 43 };\n        test_procs::test_operator_plus(concat_rep, expected);\n    }\n}\n\nTEST_CASE(\"Concatenate to containers\") {\n    std::vector<int> v1 = { 1, 2, 3 };\n    std::vector<int> v2 = { 4, 5, 6 };\n    auto concat = lz::concat(v1, v2);\n\n    SUBCASE(\"To array\") {\n        constexpr std::size_t size = 3 + 3;\n        REQUIRE((concat | lz::to<std::array<int, size>>()) == std::array<int, size>{ 1, 2, 3, 4, 5, 6 });\n    }\n\n    SUBCASE(\"To vector\") {\n        REQUIRE((concat | lz::to<std::vector>()) == std::vector<int>{ 1, 2, 3, 4, 5, 6 });\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        REQUIRE((concat | lz::to<std::list<int>>()) == std::list<int>{ 1, 2, 3, 4, 5, 6 });\n    }\n\n    SUBCASE(\"To map\") {\n        auto map = concat | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>();\n        std::map<int, int> expected = { std::make_pair(1, 1), std::make_pair(2, 2), std::make_pair(3, 3),\n                                        std::make_pair(4, 4), std::make_pair(5, 5), std::make_pair(6, 6) };\n        REQUIRE(map == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto map = concat | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>();\n        std::unordered_map<int, int> expected = { std::make_pair(1, 1), std::make_pair(2, 2), std::make_pair(3, 3),\n                                                  std::make_pair(4, 4), std::make_pair(5, 5), std::make_pair(6, 6) };\n        REQUIRE(map == expected);\n    }\n}\n"
  },
  {
    "path": "tests/cpp-lazy-ut-helper/CMakeLists.txt",
    "content": "add_library(cpp-lazy-ut-helper INTERFACE)\nadd_library(cpp-lazy-ut-helper::cpp-lazy-ut-helper ALIAS cpp-lazy-ut-helper)\n\ntarget_include_directories(cpp-lazy-ut-helper\n  INTERFACE\n    \"${CMAKE_CURRENT_LIST_DIR}/include\"\n)\ntarget_link_libraries(cpp-lazy-ut-helper\n  INTERFACE\n    cpp-lazy::cpp-lazy\n    doctest::doctest\n)\ntarget_precompile_headers(cpp-lazy-ut-helper INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/include/cpp-lazy-ut-helper/pch.hpp\")\n"
  },
  {
    "path": "tests/cpp-lazy-ut-helper/include/cpp-lazy-ut-helper/pch.hpp",
    "content": "#pragma once\n\n#include <Lz/detail/compiler_config.hpp>\n#include <algorithm>\n#include <array>\n#include <deque>\n#include <forward_list>\n#include <fstream>\n#include <functional>\n#include <iostream>\n#include <iterator>\n#include <list>\n#include <map>\n#ifndef LZ_HAS_CXX_14\n#include <memory>\n#endif\n#include <numeric>\n#include <queue>\n#include <regex>\n#include <set>\n#include <sstream>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#ifdef LZ_HAS_CXX_17\n#include <string_view>\n#endif\n\n#ifndef LZ_STANDALONE\n#include <fmt/format.h>\n#include <fmt/ostream.h>\n#endif\n\n#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/detail/iterables/reverse.hpp>\n"
  },
  {
    "path": "tests/cpp-lazy-ut-helper/include/cpp-lazy-ut-helper/test_procs.hpp",
    "content": "#pragma once\n\n#ifndef LZ_RA_TEST_PROCS_HPP\n#define LZ_RA_TEST_PROCS_HPP\n\n#include <Lz/stream.hpp>\n#include <doctest/doctest.h>\n#include <Lz/detail/procs/operators.hpp>\n#include <sstream>\n\nnamespace test_procs {\n\ntemplate<class T, class = void>\nstruct has_stream_operator : std::false_type {};\n\ntemplate<class T>\nstruct has_stream_operator<T, lz::detail::void_t<decltype(std::declval<std::ostream&>() << std::declval<T>())>>\n    : std::true_type {};\n\n#ifdef LZ_HAS_CXX_17\n\ntemplate<class T>\nconstexpr bool has_stream_operator_v = has_stream_operator<T>::value;\n\ntemplate<class T, class U>\nauto get_error_expr(const T& lhs, const U& rhs) {\n    if constexpr (has_stream_operator_v<T> && has_stream_operator_v<U>) {\n        std::ostringstream oss;\n        oss << lhs << \" != \" << rhs;\n        return oss.str();\n    }\n    else {\n        static_cast<void>(lhs), static_cast<void>(rhs);\n        return \"? != ?\";\n    }\n}\n\ntemplate<class Iterable>\nvoid test_operator_minus(const Iterable& it) {\n    const auto size = lz::ssize(it);\n    REQUIRE(size >= 0);\n\n    if constexpr (lz::detail::has_sentinel_v<Iterable>) {\n        auto begin = it.begin();\n        auto end = it.end();\n\n        for (std::ptrdiff_t i = 0; i < size; ++i) {\n            INFO(\"With i = \" << i);\n            REQUIRE(end - (begin + i) == size - i);\n            REQUIRE((begin + i) - end == -(size - i));\n        }\n    }\n    else {\n        auto begin = it.begin();\n        auto end = it.end();\n\n        for (std::ptrdiff_t i = 0; i < size; ++i) {\n            INFO(\"With i = \" << i);\n            REQUIRE((end - i) - begin == size - i);\n            REQUIRE(end - (begin + i) == size - i);\n            REQUIRE((begin + i) - end == -(size - i));\n            REQUIRE(begin - (end - i) == -(size - i));\n        }\n\n        for (std::ptrdiff_t i = 0; i < size; ++i) {\n            INFO(\"With i = \" << i);\n            REQUIRE((end - i) - (begin + i) == size - 2 * i);\n            REQUIRE((begin + i) - (end - i) == -(size - 2 * i));\n        }\n    }\n}\n\ntemplate<class Iterable, class ExpectedIterable, class EqCompare = LZ_BIN_OP(equal_to, lz::detail::val_iterable_t<Iterable>)>\nvoid test_operator_plus(const Iterable& it, const ExpectedIterable& expected, EqCompare eq_compare = {}) {\n    REQUIRE(lz::size(it) == lz::size(expected));\n    const auto size = lz::ssize(it);\n    REQUIRE(size >= 0);\n\n    if constexpr (lz::detail::has_sentinel_v<Iterable>) {\n        auto begin = it.begin();\n\n        for (std::ptrdiff_t i = 0; i + 1 < size; ++i) {\n            INFO(\"With i = \" << i << \" with expr: \" << get_error_expr(*(begin + i), *(expected.begin() + i)));\n            REQUIRE(eq_compare(*(begin + i), *(expected.begin() + i)));\n        }\n\n        REQUIRE(begin + size == it.end());\n\n        std::advance(begin, size);\n        REQUIRE(begin + 0 == begin);\n\n        for (std::ptrdiff_t i = 1; i <= size; ++i) {\n            INFO(\"With i = \" << i << \" with expr: \" << get_error_expr(*(begin - i), *(expected.end() - i)));\n            REQUIRE(eq_compare(*(begin - i), *(expected.end() - i)));\n        }\n        REQUIRE(begin - size == it.begin());\n    }\n    else {\n        auto begin = it.begin();\n        auto end = it.end();\n\n        for (std::ptrdiff_t i = 0; i + 1 < size; ++i) {\n            INFO(\"With i = \" << i << \" with expr: \" << get_error_expr(*(begin + i), *(expected.begin() + i)));\n            REQUIRE(eq_compare(*(begin + i), *(expected.begin() + i)));\n        }\n\n        REQUIRE(begin + size == it.end());\n        for (std::ptrdiff_t i = 1; i <= size; ++i) {\n            INFO(\"With i = \" << i << \" with expr: \" << get_error_expr(*(end - i), *(expected.end() - i)));\n            REQUIRE(eq_compare(*(end - i), *(expected.end() - i)));\n        }\n        REQUIRE(end - size == it.begin());\n\n        std::advance(begin, size);\n        std::advance(end, -size);\n        REQUIRE(begin + 0 == begin);\n        REQUIRE(end + 0 == end);\n\n        for (std::ptrdiff_t i = 0; i + 1 < size; ++i) {\n            INFO(\"With i = \" << i << \" with expr: \" << get_error_expr(*(end + i), *(expected.begin() + i)));\n            REQUIRE(eq_compare(*(end + i), *(expected.begin() + i)));\n        }\n\n        REQUIRE(end + size == it.end());\n        for (std::ptrdiff_t i = 1; i <= size; ++i) {\n            INFO(\"With i = \" << i << \" with expr: \" << get_error_expr(*(begin - i), *(expected.end() - i)));\n            REQUIRE(eq_compare(*(begin - i), *(expected.end() - i)));\n        }\n        REQUIRE(begin - size == it.begin());\n    }\n}\n\n#else\n\ntemplate<class T, class U>\nlz::detail::enable_if_t<has_stream_operator<T>::value && has_stream_operator<U>::value>\nget_error_expr(const T& lhs, const U& rhs) {\n    std::ostringstream oss;\n    oss << lhs << \" != \" << rhs;\n    return oss.str();\n}\n\ntemplate<class T, class U>\nlz::detail::enable_if_t<!has_stream_operator<T>::value || !has_stream_operator<U>::value, const char*>\nget_error_expr(const T&, const U&) {\n    return \"? != ?\";\n}\n\ntemplate<class Iterable>\nlz::detail::enable_if_t<!lz::detail::has_sentinel<Iterable>::value> test_operator_minus(const Iterable& it) {\n    auto begin = it.begin();\n    auto end = it.end();\n\n    const auto size = lz::ssize(it);\n    for (std::ptrdiff_t i = 0; i < size; ++i) {\n        INFO(\"With i = \" << i);\n        REQUIRE((end - i) - begin == size - i);\n        REQUIRE(end - (begin + i) == size - i);\n        REQUIRE((begin + i) - end == -(size - i));\n        REQUIRE(begin - (end - i) == -(size - i));\n    }\n\n    for (std::ptrdiff_t i = 0; i < size; ++i) {\n        INFO(\"With i = \" << i);\n        REQUIRE((end - i) - (begin + i) == size - 2 * i);\n        REQUIRE((begin + i) - (end - i) == -(size - 2 * i));\n    }\n}\n\ntemplate<class Iterable>\nlz::detail::enable_if_t<lz::detail::has_sentinel<Iterable>::value> test_operator_minus(const Iterable& it) {\n    auto begin = it.begin();\n    auto end = it.end();\n\n    const auto size = lz::ssize(it);\n    for (std::ptrdiff_t i = 0; i < size; ++i) {\n        INFO(\"With i = \" << i);\n        REQUIRE(end - (begin + i) == size - i);\n        REQUIRE((begin + i) - end == -(size - i));\n    }\n}\n\ntemplate<class Iterable, class ExpectedIterable, class EqCompare = LZ_BIN_OP(equal_to, lz::detail::val_iterable_t<Iterable>)>\nlz::detail::enable_if_t<!lz::detail::has_sentinel<Iterable>::value>\ntest_operator_plus(const Iterable& it, const ExpectedIterable& expected, EqCompare eq_compare = {}) {\n    REQUIRE(lz::size(it) == lz::size(expected));\n\n    auto begin = it.begin();\n    auto end = it.end();\n\n    const auto size = lz::ssize(it);\n    for (std::ptrdiff_t i = 0; i + 1 < size; ++i) {\n        INFO(\"With i = \" << i);\n        REQUIRE(eq_compare(*(begin + i), *(expected.begin() + i)));\n    }\n    REQUIRE(begin + size == it.end());\n    for (std::ptrdiff_t i = 1; i <= size; ++i) {\n        INFO(\"With i = \" << i);\n        REQUIRE(eq_compare(*(end - i), *(expected.end() - i)));\n    }\n    INFO(\"With i = \" << size);\n    REQUIRE(end - size == it.begin());\n\n    std::advance(begin, size);\n    std::advance(end, -size);\n    INFO(\"With i = 0\");\n    REQUIRE(begin + 0 == begin);\n    REQUIRE(end + 0 == end);\n\n    for (std::ptrdiff_t i = 0; i + 1 < size; ++i) {\n        INFO(\"With i = \" << i);\n        REQUIRE(eq_compare(*(end + i), *(expected.begin() + i)));\n    }\n    REQUIRE(end + size == it.end());\n    for (std::ptrdiff_t i = 1; i <= size; ++i) {\n        INFO(\"With i = \" << i);\n        REQUIRE(eq_compare(*(begin - i), *(expected.end() - i)));\n    }\n    REQUIRE(begin - size == it.begin());\n}\n\ntemplate<class Iterable, class ExpectedIterable, class EqCompare = LZ_BIN_OP(equal_to, lz::detail::val_iterable_t<Iterable>)>\nlz::detail::enable_if_t<lz::detail::has_sentinel<Iterable>::value>\ntest_operator_plus(const Iterable& it, const ExpectedIterable& expected, EqCompare eq_compare = {}) {\n    REQUIRE(lz::size(it) == lz::size(expected));\n\n    auto begin = it.begin();\n\n    for (std::ptrdiff_t i = 0; static_cast<std::size_t>(i) + 1 < lz::size(it); ++i) {\n        INFO(\"With i = \" << i);\n        REQUIRE(eq_compare(*(begin + i), *(expected.begin() + i)));\n    }\n    REQUIRE(begin + lz::ssize(it) == it.end());\n\n    std::advance(begin, lz::ssize(it));\n    REQUIRE(begin + 0 == begin);\n\n    for (std::ptrdiff_t i = 1; i <= lz::ssize(it); ++i) {\n        INFO(\"With i = \" << i);\n        REQUIRE(eq_compare(*(begin - i), *(expected.end() - i)));\n    }\n    REQUIRE(begin - lz::ssize(it) == it.begin());\n}\n\n#endif\n} // namespace test_procs\n\n#endif\n"
  },
  {
    "path": "tests/cpp-lazy-ut-helper/include/cpp-lazy-ut-helper/ut_helper.hpp",
    "content": "#pragma once\n\n#ifndef LZ_UT_HELPER_LIB_C_STRING_HPP\n#define LZ_UT_HELPER_LIB_C_STRING_HPP\n\n#include <Lz/detail/traits/strict_iterator_traits.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/procs/eager_size.hpp>\n#include <Lz/traits/is_sized.hpp>\n#include <Lz/traits/lazy_view.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n\n#ifdef LZ_HAS_CONCEPTS\n#include <Lz/traits/concepts.hpp>\n#endif\n\ntemplate<class Iterable, bool EnableSize = true>\nclass bidi_sentinelled : public lz::lazy_view {\n    using iterable = lz::filter_iterable<Iterable, std::function<bool(lz::detail::ref_iterable_t<Iterable>)>>;\n    iterable _iterable{};\n\npublic:\n    template<class I = Iterable, class = lz::detail::enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr bidi_sentinelled() {\n    }\n\n    template<class I>\n    explicit bidi_sentinelled(I&& i) :\n        // clang-format off\n        _iterable{ \n            std::forward<I>(i),\n            std::function<bool(lz::detail::ref_iterable_t<Iterable>)>{\n            [](lz::detail::ref_iterable_t<Iterable>) { \n                return true;\n            }}\n        } // clang-format on\n    {\n    }\n\n    template<bool E = EnableSize>\n    lz::detail::enable_if_t<E, size_t> size() const {\n        return lz::eager_size(_iterable);\n    }\n\n    lz::iter_t<iterable> begin() const {\n        return _iterable.begin();\n    }\n\n    lz::default_sentinel_t end() const {\n        return lz::default_sentinel;\n    }\n};\n\ntemplate<class Iterable>\nbidi_sentinelled<lz::detail::remove_cvref_t<Iterable>, true> make_sized_bidi_sentinelled(Iterable&& iterable) {\n    return bidi_sentinelled<lz::detail::remove_cvref_t<Iterable>, true>(std::forward<Iterable>(iterable));\n}\n\ntemplate<class Iterable>\nbidi_sentinelled<lz::detail::remove_cvref_t<Iterable>, false> make_non_sized_bidi_sentinelled(Iterable&& iterable) {\n    return bidi_sentinelled<lz::detail::remove_cvref_t<Iterable>, false>(std::forward<Iterable>(iterable));\n}\n\ntemplate<class Iterable>\nclass sentinel_assign_op_tester : public lz::lazy_view {\n    lz::maybe_owned<Iterable> _iterable{};\n\npublic:\n    template<class I = Iterable, class = lz::detail::enable_if_t<std::is_default_constructible<I>::value>>\n    constexpr sentinel_assign_op_tester() {\n    }\n\n    template<class I>\n    explicit sentinel_assign_op_tester(I&& iterable) : _iterable{ std::forward<I>(iterable) } {\n    }\n\n    lz::iter_t<Iterable> begin() const {\n        return _iterable.begin();\n    }\n\n    lz::iter_t<Iterable> end() const {\n        auto end = _iterable.begin();\n        end = _iterable.end();\n        return end;\n    }\n\n#ifdef LZ_HAS_CONCEPTS\n\n    size_t size() const\n        requires(lz::sized<Iterable>)\n    {\n        return lz::size(_iterable);\n    }\n\n#else\n\n    template<class T = Iterable>\n    lz::detail::enable_if_t<lz::is_sized<T>::value, size_t> size() const {\n        return lz::size(_iterable);\n    }\n\n#endif\n};\n\ntemplate<class Iterable>\nsentinel_assign_op_tester<lz::detail::remove_cvref_t<Iterable>> make_sentinel_assign_op_tester(Iterable&& iterable) {\n    static_assert(!std::is_same<lz::iter_t<Iterable>, lz::sentinel_t<Iterable>>::value, \"\");\n\n    return sentinel_assign_op_tester<lz::detail::remove_cvref_t<Iterable>>(std::forward<Iterable>(iterable));\n}\n\n#endif // LZ_UT_HELPER_LIB_C_STRING_HPP\n"
  },
  {
    "path": "tests/duplicates.cpp",
    "content": "#include <Lz/algorithm/equal.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/duplicates.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nstruct equal_pair {\n    template<class T1, class T2>\n    constexpr bool operator()(const std::pair<T1, std::size_t>& lhs, const std::pair<T2, std::size_t>& rhs) const {\n        return lhs.first == rhs.first && lhs.second == rhs.second;\n    }\n};\n\nTEST_CASE(\"Equal to different category of iterables\") {\n    SUBCASE(\"With random access iterator\") {\n        std::vector<int> input{ 1, 2, 2, 3, 4, 4, 5 };\n        std::vector<std::pair<int, std::size_t>> expected = { { 1, 1 }, { 2, 2 }, { 3, 1 }, { 4, 2 }, { 5, 1 } };\n\n        lz::duplicates_iterable<std::vector<int>> dupes = lz::duplicates(input);\n        REQUIRE(lz::equal(dupes, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n\n        input = { 1, 2, 3, 4, 5 };\n        expected = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 1 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n\n        input = { 1, 1, 1, 1, 1 };\n        expected = { { 1, 5 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n\n        input = { 1, 1, 2, 3, 3, 4 };\n        expected = { { 1, 2 }, { 2, 1 }, { 3, 2 }, { 4, 1 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n\n        input = { 1, 2, 3, 4, 5, 5 };\n        expected = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 2 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n    }\n\n    SUBCASE(\"With bidi access iterator\") {\n        std::list<int> input{ 1, 2, 2, 3, 4, 4, 5 };\n        std::vector<std::pair<int, std::size_t>> expected = { { 1, 1 }, { 2, 2 }, { 3, 1 }, { 4, 2 }, { 5, 1 } };\n\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n\n        input = { 1, 2, 3, 4, 5 };\n        expected = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 1 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n\n        input = { 1, 1, 1, 1, 1 };\n        expected = { { 1, 5 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n\n        input = { 1, 1, 2, 3, 3, 4 };\n        expected = { { 1, 2 }, { 2, 1 }, { 3, 2 }, { 4, 1 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n\n        input = { 1, 2, 3, 4, 5, 5 };\n        expected = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 2 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n        REQUIRE(lz::equal(input | lz::duplicates | lz::reverse, expected | lz::reverse, equal_pair{}));\n    }\n\n    SUBCASE(\"With (sentinelled) forward only\") {\n        auto input = lz::c_string(\"abbcdde\");\n        std::vector<std::pair<char, std::size_t>> expected = { { 'a', 1 }, { 'b', 2 }, { 'c', 1 }, { 'd', 2 }, { 'e', 1 } };\n\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n\n        input = lz::c_string(\"abcde\");\n        expected = { { 'a', 1 }, { 'b', 1 }, { 'c', 1 }, { 'd', 1 }, { 'e', 1 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n\n        input = lz::c_string(\"aaaaa\");\n        expected = { { 'a', 5 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n\n        input = lz::c_string(\"aabccd\");\n        expected = { { 'a', 2 }, { 'b', 1 }, { 'c', 2 }, { 'd', 1 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n\n        input = lz::c_string(\"abcdee\");\n        expected = { { 'a', 1 }, { 'b', 1 }, { 'c', 1 }, { 'd', 1 }, { 'e', 2 } };\n        REQUIRE(lz::equal(input | lz::duplicates, expected, equal_pair{}));\n    }\n}\n\nTEST_CASE(\"Duplicates operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> fwd = { 1, 1, 2, 2, 3, 4, 4 };\n        auto duplicated = lz::duplicates(fwd);\n\n        auto common = make_sentinel_assign_op_tester(duplicated);\n\n        std::vector<std::pair<int, std::size_t>> expected = { std::make_pair(1, 2), std::make_pair(2, 2), std::make_pair(3, 1),\n                                                              std::make_pair(4, 2) };\n        using reference = decltype(*common.begin());\n        REQUIRE(lz::equal(common, expected, [](reference a, const std::pair<int, size_t>& b) {\n            return a.first == b.first && a.second == b.second;\n        }));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> vec = { 10, 10, 20, 20, 20, 30 };\n        auto common2 = make_sentinel_assign_op_tester(lz::duplicates(make_sized_bidi_sentinelled(vec)));\n        std::vector<std::pair<int, std::size_t>> expected2 = { std::make_pair(10, 2), std::make_pair(20, 3),\n                                                               std::make_pair(30, 1) };\n        using reference2 = decltype(*common2.begin());\n        REQUIRE(lz::equal(common2, expected2, [](reference2 a, const std::pair<int, size_t>& b) {\n            return a.first == b.first && a.second == b.second;\n        }));\n        REQUIRE(lz::equal(lz::reverse(common2), lz::reverse(expected2), [](reference2 a, const std::pair<int, size_t>& b) {\n            return a.first == b.first && a.second == b.second;\n        }));\n    }\n}\n"
  },
  {
    "path": "tests/enumerate.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/enumerate.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/take.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nstruct equal_fn {\n    template<typename T, typename U>\n    bool operator()(const T& a, const U& b) const {\n        INFO(\"Comparing pairs: \" << a.first << \", \" << a.second << \" with \" << b.first << \", \" << b.second);\n        return a.first == b.first && a.second == b.second;\n    }\n};\n\nTEST_CASE(\"Enumerate with sentinels\") {\n    const char* str = \"Hello\";\n    auto c_string = lz::c_string(str);\n    lz::enumerate_iterable<decltype(c_string)> enumerated = lz::enumerate(c_string);\n    static_assert(!std::is_same<decltype(enumerated.begin()), decltype(enumerated.end())>::value,\n                  \"Begin and end should not be the same type\");\n    auto taken = lz::take(enumerated, 3);\n    std::vector<std::pair<int, char>> expected = { { 0, 'H' }, { 1, 'e' }, { 2, 'l' } };\n    REQUIRE(lz::equal(taken, expected, equal_fn{}));\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        auto cstr = lz::c_string(\"abc\");\n        auto enumerated = lz::enumerate(cstr, std::size_t{ 0 });\n        auto common = make_sentinel_assign_op_tester(enumerated);\n        std::vector<std::pair<std::size_t, char>> expected = { std::make_pair(0, 'a'), std::make_pair(1, 'b'),\n                                                               std::make_pair(2, 'c') };\n        using reference = decltype(*common.begin());\n        REQUIRE(\n            lz::equal(common, expected, [](reference a, const std::pair<size_t, char> b) { return a.first == b.first && a.second == b.second; }));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> vec = { 1, 2, 3 };\n        auto bidi = make_sized_bidi_sentinelled(vec);\n        auto enumerated = make_sentinel_assign_op_tester(lz::enumerate(bidi, 1));\n        auto expected = { std::make_pair(1, 1), std::make_pair(2, 2), std::make_pair(3, 3) };\n        using reference = decltype(*enumerated.begin());\n        REQUIRE(lz::equal(enumerated, expected, [](reference a, const std::pair<size_t, int> b) {\n            return static_cast<size_t>(a.first) == b.first && a.second == b.second;\n        }));\n        REQUIRE(lz::equal(lz::reverse(enumerated), lz::reverse(expected), [](reference a, const std::pair<size_t, int> b) {\n            return static_cast<size_t>(a.first) == b.first && a.second == b.second;\n        }));\n    }\n\n    SUBCASE(\"random access\") {\n        auto repeater = lz::repeat(1, 3);\n        auto enumerated = make_sentinel_assign_op_tester(lz::enumerate(repeater, 1));\n        auto expected = { std::make_pair(1, 1), std::make_pair(2, 1), std::make_pair(3, 1) };\n        using reference = decltype(*enumerated.begin());\n        REQUIRE(lz::equal(enumerated, expected, [](reference a, const std::pair<size_t, int> b) {\n            return static_cast<size_t>(a.first) == b.first && a.second == b.second;\n        }));\n        REQUIRE(lz::equal(lz::reverse(enumerated), lz::reverse(expected), [](reference a, const std::pair<size_t, int> b) {\n            return static_cast<size_t>(a.first) == b.first && a.second == b.second;\n        }));\n        test_procs::test_operator_minus(enumerated);\n        test_procs::test_operator_plus(enumerated, expected);\n    }\n}\n\nTEST_CASE(\"Empty or one element enumerate\") {\n    SUBCASE(\"Empty\") {\n        std::string a;\n        auto enumerate = lz::enumerate(a);\n        REQUIRE(lz::empty(enumerate));\n        REQUIRE_FALSE(lz::has_one(enumerate));\n        REQUIRE_FALSE(lz::has_many(enumerate));\n    }\n\n    SUBCASE(\"One element\") {\n        std::string a = \"h\";\n        auto enumerate = lz::enumerate(a);\n        REQUIRE_FALSE(lz::empty(enumerate));\n        REQUIRE(lz::has_one(enumerate));\n        REQUIRE_FALSE(lz::has_many(enumerate));\n    }\n}\n\nTEST_CASE(\"Enumerate binary operations\") {\n    constexpr std::size_t size = 3;\n    std::array<int, size> array = { 1, 2, 3 };\n    auto enumerate = lz::enumerate(array, 2);\n\n    SUBCASE(\"Operator++\") {\n        auto expected = std::vector<std::pair<int, int>>{ { 2, 1 }, { 3, 2 }, { 4, 3 } };\n        REQUIRE(lz::equal(enumerate, expected, equal_fn{}));\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto reverse_expected = std::vector<std::pair<int, int>>{ { 4, 3 }, { 3, 2 }, { 2, 1 } };\n        REQUIRE(lz::equal(lz::reverse(enumerate), reverse_expected, equal_fn{}));\n    }\n\n    SUBCASE(\"Operator== & operator!=\") {\n        auto it = enumerate.begin();\n        REQUIRE(it != enumerate.end());\n        REQUIRE(it == enumerate.begin());\n        REQUIRE(enumerate.begin() == it);\n        REQUIRE(it != enumerate.end());\n        it = enumerate.end();\n        REQUIRE(it == enumerate.end());\n        REQUIRE(it != enumerate.begin());\n        REQUIRE(enumerate.end() == it);\n        REQUIRE(enumerate.begin() != it);\n    }\n\n    SUBCASE(\"Operator+\") {\n        std::vector<std::pair<int, int>> expected = { { 2, 1 }, { 3, 2 }, { 4, 3 } };\n        test_procs::test_operator_plus(enumerate, expected, equal_fn{});\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(enumerate);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto repeat = lz::repeat(1, 5);\n        test_procs::test_operator_minus(repeat | lz::enumerate);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto repeat = lz::repeat(1, 5);\n        std::vector<std::pair<int, int>> expected = { { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 1 }, { 6, 1 } };\n        test_procs::test_operator_plus(repeat | lz::enumerate(2), expected, equal_fn{});\n    }\n}\n\nTEST_CASE(\"Enumerate to containers\") {\n    constexpr std::size_t size = 3;\n    std::array<int, size> array = { 1, 2, 3 };\n    std::vector<int> vec = { 1, 2, 3 };\n\n    SUBCASE(\"To array\") {\n        auto actual_array = array | lz::enumerate | lz::to<std::array<std::pair<int, int>, size>>();\n        auto expected = { std::make_pair(0, 1), std::make_pair(1, 2), std::make_pair(2, 3) };\n        REQUIRE(lz::equal(actual_array, expected, equal_fn{}));\n    }\n\n    SUBCASE(\"To vector\") {\n        auto actual_vec = lz::enumerate(vec) | lz::to<std::vector>();\n        auto expected = { std::make_pair(0, 1), std::make_pair(1, 2), std::make_pair(2, 3) };\n        REQUIRE(lz::equal(actual_vec, expected, equal_fn{}));\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        auto actual_list = lz::enumerate(vec) | lz::to<std::list>();\n        auto expected = { std::make_pair(0, 1), std::make_pair(1, 2), std::make_pair(2, 3) };\n        REQUIRE(lz::equal(actual_list, expected, equal_fn{}));\n    }\n\n    SUBCASE(\"To map\") {\n        auto enumerator = lz::enumerate(array);\n        auto actual = enumerator | lz::map([](const std::pair<int, int> pair) { return std::make_pair(pair.second, pair); }) |\n                      lz::to<std::map<int, std::pair<int, int>>>();\n\n        std::map<int, std::pair<int, int>> expected = {\n            std::make_pair(1, std::make_pair(0, 1)),\n            std::make_pair(2, std::make_pair(1, 2)),\n            std::make_pair(3, std::make_pair(2, 3)),\n        };\n\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto enumerator = lz::enumerate(array);\n        auto actual = enumerator | lz::map([](const std::pair<int, int> pair) { return std::make_pair(pair.second, pair); }) |\n                      lz::to<std::unordered_map<int, std::pair<int, int>>>();\n\n        std::unordered_map<int, std::pair<int, int>> expected = {\n            std::make_pair(1, std::make_pair(0, 1)),\n            std::make_pair(2, std::make_pair(1, 2)),\n            std::make_pair(3, std::make_pair(2, 3)),\n        };\n\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"Bidirectional to container\") {\n        std::list<int> to_enumerate = { 1, 2, 3 };\n        auto enumerated = lz::enumerate(to_enumerate);\n        std::array<std::pair<int, int>, 3> expected = { std::make_pair(0, 1), std::make_pair(1, 2), std::make_pair(2, 3) };\n        using ref_enumerated = lz::detail::ref_iterable_t<decltype(enumerated)>;\n        using ref_expected = lz::detail::ref_iterable_t<decltype(expected)>;\n        REQUIRE(lz::equal(enumerated, expected,\n                          [](ref_enumerated a, ref_expected b) { return a.first == b.first && a.second == b.second; }));\n    }\n}\n"
  },
  {
    "path": "tests/except.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/except.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Except tests with sentinels\") {\n    const char* str = \"Hello, World!\";\n    const char* to_except = \"eo\";\n    auto c_str = lz::c_string(str);\n    auto c_str_to_except = lz::c_string(to_except);\n    lz::except_iterable<decltype(c_str), decltype(c_str_to_except)> except = lz::except(c_str, c_str_to_except);\n    static_assert(!std::is_same<decltype(except.begin()), decltype(except.end())>::value, \"Must be sentinel\");\n    REQUIRE((except | lz::to<std::string>()) == \"Hll, Wrld!\");\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> fwd = { 1, 2, 3, 4, 5 };\n        std::forward_list<int> to_except2 = { 2, 4 };\n        auto excepted = lz::except(fwd, to_except2);\n        auto common = make_sentinel_assign_op_tester(excepted);\n        auto expected = { 1, 3, 5 };\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5 };\n        std::list<int> to_except2 = { 2, 4 };\n        auto lst_sent = make_sized_bidi_sentinelled(lst);\n        auto to_except2_sent = make_sized_bidi_sentinelled(to_except2);\n\n        auto except = lz::except(lst_sent, to_except2_sent);\n        auto common = make_sentinel_assign_op_tester(except);\n        auto expected = { 1, 3, 5 };\n        REQUIRE(lz::equal(common, expected));\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n    }\n}\n\nTEST_CASE(\"Empty or one element except\") {\n    SUBCASE(\"Empty\") {\n        std::string a;\n        std::string b;\n        auto except = lz::except(a, b);\n        REQUIRE(lz::empty(except));\n        REQUIRE_FALSE(lz::has_one(except));\n        REQUIRE_FALSE(lz::has_many(except));\n    }\n\n    SUBCASE(\"One element 1\") {\n        std::string a = \"h\";\n        std::string b;\n        auto except = lz::except(a, b);\n        REQUIRE_FALSE(lz::empty(except));\n        REQUIRE(lz::has_one(except));\n        REQUIRE_FALSE(lz::has_many(except));\n    }\n\n    SUBCASE(\"One element 2\") {\n        std::string a;\n        std::string b = \"w\";\n        auto except = lz::except(a, b);\n        REQUIRE(lz::empty(except));\n        REQUIRE_FALSE(lz::has_one(except));\n        REQUIRE_FALSE(lz::has_many(except));\n    }\n\n    SUBCASE(\"One element both\") {\n        std::string a = \"h\";\n        std::string b = \"w\";\n        auto except = lz::except(a, b);\n        REQUIRE_FALSE(lz::empty(except));\n        REQUIRE(lz::has_one(except));\n        REQUIRE_FALSE(lz::has_many(except));\n    }\n\n    SUBCASE(\"One element both 2\") {\n        std::string a = \"h\";\n        std::string b = \"h\";\n        auto except = lz::except(a, b);\n        REQUIRE(lz::empty(except));\n        REQUIRE_FALSE(lz::has_one(except));\n        REQUIRE_FALSE(lz::has_many(except));\n    }\n}\n\nTEST_CASE(\"Except binary operations\") {\n    std::vector<int> a = { 1, 2, 3, 4, 5, 6 };\n    std::vector<int> b = { 2, 3, 6, 20 };\n\n    auto except = lz::except(a, b);\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 1, 4, 5 };\n        REQUIRE(lz::equal(except, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto expected = { 5, 4, 1 };\n        REQUIRE(lz::equal(except | lz::cached_reverse, expected));\n    }\n}\n\nTEST_CASE(\"Except to containers\") {\n    std::vector<int> a = { 1, 2, 3, 4 };\n    std::vector<int> b = { 1, 3 };\n    auto except = lz::except(a, b);\n\n    SUBCASE(\"To array\") {\n        auto excepted = except | lz::to<std::array<int, 2>>();\n        REQUIRE(excepted == std::array<int, 2>{ 2, 4 });\n    }\n\n    SUBCASE(\"To vector\") {\n        auto excepted = except | lz::to<std::vector>();\n        REQUIRE(excepted == std::vector<int>{ 2, 4 });\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        auto excepted = except | lz::to<std::list>();\n        REQUIRE(excepted == std::list<int>{ 2, 4 });\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = except | lz::map([](const int i) { return std::make_pair(i, i); }) |\n                      lz::to<std::map<int, int>>();\n\n        std::map<int, int> expected = {\n            std::make_pair(2, 2),\n            std::make_pair(4, 4),\n        };\n\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto actual = except | lz::map([](const int i) { return std::make_pair(i, i); }) |\n                      lz::to<std::unordered_map<int, int>>();\n\n        std::unordered_map<int, int> expected = {\n            std::make_pair(2, 2),\n            std::make_pair(4, 4),\n        };\n\n        REQUIRE(actual == expected);\n    }\n}\n"
  },
  {
    "path": "tests/exclude.cpp",
    "content": "#include <Lz/algorithm/equal.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/exclude.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Exclude with sentinels\") {\n    auto cstr = lz::c_string(\"a string to exclude\");\n    lz::exclude_iterable<decltype(cstr)> excluded = lz::exclude(cstr, 3, 5);\n    REQUIRE((excluded | lz::to<std::string>()) == \"a sing to exclude\");\n    static_assert(std::is_same<decltype(excluded.end()), lz::default_sentinel_t>::value, \"Should be default sentinel\");\n}\n\nTEST_CASE(\"Exclude changing and creating elements\") {\n    std::array<int, 10> arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n    auto excluded1 = lz::exclude(arr, 3, 5);\n    static_assert(!std::is_same<decltype(excluded1.end()), lz::default_sentinel_t>::value, \"Should not be default sentinel\");\n    auto excluded2 = lz::exclude(arr, 0, 2);\n    auto excluded3 = lz::exclude(arr, 8, 10);\n\n    std::vector<int> excluded_expected1 = { 1, 2, 3, 6, 7, 8, 9, 10 };\n    REQUIRE(lz::equal(excluded1, excluded_expected1));\n\n    std::vector<int> excluded_expected2 = { 3, 4, 5, 6, 7, 8, 9, 10 };\n    REQUIRE(lz::equal(excluded2, excluded_expected2));\n\n    std::vector<int> excluded_expected3 = { 1, 2, 3, 4, 5, 6, 7, 8 };\n    REQUIRE(lz::equal(excluded3, excluded_expected3));\n\n    SUBCASE(\"Should Exclude out element\") {\n        constexpr auto signed_size = static_cast<std::ptrdiff_t>(arr.size() - 2);\n        REQUIRE(lz::distance(excluded1) == signed_size);\n        REQUIRE(lz::distance(excluded2) == signed_size);\n        REQUIRE(lz::distance(excluded3) == signed_size);\n    }\n\n    SUBCASE(\"Should be by reference\") {\n        static_assert(std::is_same<decltype(*excluded1.begin()), int&>::value, \"Should be int&\");\n    }\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        auto cstr = lz::c_string(\"Hello, World!\");\n        auto excluded = lz::exclude(cstr, 3, 5);\n        auto common = make_sentinel_assign_op_tester(excluded);\n        auto expected = lz::c_string(\"Hel, World!\");\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto vec_sent = make_sized_bidi_sentinelled(vec);\n        auto excluded = lz::exclude(vec_sent, 1, 3);\n        auto common = make_sentinel_assign_op_tester(excluded);\n        auto expected = { 1, 4, 5 };\n        REQUIRE(lz::equal(common, expected));\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"random access\") {\n        auto repeater = lz::repeat(1, 5);\n        auto excluded = make_sentinel_assign_op_tester(lz::exclude(repeater, 1, 3));\n        auto expected = { 1, 1, 1 };\n        REQUIRE(lz::equal(excluded, expected));\n        REQUIRE(lz::equal(excluded | lz::reverse, expected | lz::reverse));\n        test_procs::test_operator_plus(excluded, expected);\n        test_procs::test_operator_minus(excluded);\n    }\n}\nTEST_CASE(\"Exclude binary operations\") {\n    std::array<int, 10> arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n\n    auto excluded1 = arr | lz::exclude(3, 5); // { 1, 2, 3, 6, 7, 8, 9, 10 }\n    auto excluded2 = lz::exclude(arr, 0, 2);  // { 3, 4, 5, 6, 7, 8, 9, 10 }\n    auto excluded3 = lz::exclude(arr, 7, 10); // { 1, 2, 3, 4, 5, 6, 7 }\n\n    SUBCASE(\"Operator++\") {\n        std::vector<int> expected = { 1, 2, 3, 6, 7, 8, 9, 10 };\n        REQUIRE(lz::equal(excluded1, expected));\n\n        expected = { 3, 4, 5, 6, 7, 8, 9, 10 };\n        REQUIRE(lz::equal(excluded2, expected));\n\n        expected = { 1, 2, 3, 4, 5, 6, 7 };\n        REQUIRE(lz::equal(excluded3, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        std::vector<int> expected = { 10, 9, 8, 7, 6, 3, 2, 1 };\n        REQUIRE(lz::equal(lz::reverse(excluded1), expected));\n\n        expected = { 10, 9, 8, 7, 6, 5, 4, 3 };\n        REQUIRE(lz::equal(lz::reverse(excluded2), expected));\n\n        expected = { 7, 6, 5, 4, 3, 2, 1 };\n        REQUIRE(lz::equal(lz::reverse(excluded3), expected));\n    }\n\n    SUBCASE(\"Operator+\") {\n        std::vector<int> expected = { 1, 2, 3, 6, 7, 8, 9, 10 };\n        test_procs::test_operator_plus(excluded1, expected);\n\n        expected = { 3, 4, 5, 6, 7, 8, 9, 10 };\n        test_procs::test_operator_plus(excluded2, expected);\n\n        expected = { 1, 2, 3, 4, 5, 6, 7 };\n        test_procs::test_operator_plus(excluded3, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(excluded1);\n        test_procs::test_operator_minus(excluded2);\n        test_procs::test_operator_minus(excluded3);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto excluded1_sent = lz::exclude(lz::repeat(0, 10), 3, 5);\n        auto excluded2_sent = lz::exclude(lz::repeat(0, 10), 0, 2);\n        auto excluded3_sent = lz::exclude(lz::repeat(0, 10), 8, 10);\n        test_procs::test_operator_minus(excluded1_sent);\n        test_procs::test_operator_minus(excluded2_sent);\n        test_procs::test_operator_minus(excluded3_sent);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto excluded1_sent = lz::exclude(lz::repeat(0, 10), 3, 5);\n        auto excluded2_sent = lz::exclude(lz::repeat(0, 10), 0, 2);\n        auto excluded3_sent = lz::exclude(lz::repeat(0, 10), 8, 10);\n\n        std::vector<int> expected1 = { 0, 0, 0, 0, 0, 0, 0, 0 };\n        test_procs::test_operator_plus(excluded1_sent, expected1);\n\n        expected1 = { 0, 0, 0, 0, 0, 0, 0, 0 };\n        test_procs::test_operator_plus(excluded2_sent, expected1);\n\n        expected1 = { 0, 0, 0, 0, 0, 0, 0, 0 };\n        test_procs::test_operator_plus(excluded3_sent, expected1);\n    }\n}\n\nTEST_CASE(\"Empty or one element exclude\") {\n    SUBCASE(\"Empty\") {\n        std::array<int, 0> arr = {};\n        auto excluded = lz::exclude(arr, 0, 1);\n        REQUIRE(excluded.begin() == excluded.end());\n    }\n\n    SUBCASE(\"One element\") {\n        std::array<int, 1> arr = { 1 };\n        auto excluded = lz::exclude(arr, 0, 1);\n        REQUIRE(excluded.begin() == excluded.end());\n    }\n}\n\nTEST_CASE(\"Exclude to containers\") {\n    std::array<int, 10> arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n\n    auto excluded1 = lz::exclude(arr, 3, 5);\n    auto excluded2 = lz::exclude(arr, 0, 2);\n    auto excluded3 = lz::exclude(arr, 7, 10);\n\n    SUBCASE(\"To array\") {\n        REQUIRE((excluded1 | lz::to<std::array<int, 8>>()) == std::array<int, 8>{ 1, 2, 3, 6, 7, 8, 9, 10 });\n        REQUIRE((excluded2 | lz::to<std::array<int, 8>>()) == std::array<int, 8>{ 3, 4, 5, 6, 7, 8, 9, 10 });\n        REQUIRE((excluded3 | lz::to<std::array<int, 7>>()) == std::array<int, 7>{ 1, 2, 3, 4, 5, 6, 7 });\n    }\n\n    SUBCASE(\"To vector\") {\n        REQUIRE((excluded1 | lz::to<std::vector>()) == std::vector<int>{ 1, 2, 3, 6, 7, 8, 9, 10 });\n        REQUIRE((excluded2 | lz::to<std::vector>()) == std::vector<int>{ 3, 4, 5, 6, 7, 8, 9, 10 });\n        REQUIRE((excluded3 | lz::to<std::vector>()) == std::vector<int>{ 1, 2, 3, 4, 5, 6, 7 });\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        REQUIRE((excluded1 | lz::to<std::list>()) == std::list<int>{ 1, 2, 3, 6, 7, 8, 9, 10 });\n        REQUIRE((excluded2 | lz::to<std::list>()) == std::list<int>{ 3, 4, 5, 6, 7, 8, 9, 10 });\n        REQUIRE((excluded3 | lz::to<std::list>()) == std::list<int>{ 1, 2, 3, 4, 5, 6, 7 });\n    }\n\n    SUBCASE(\"To map\") {\n        REQUIRE((excluded1 | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>()) ==\n                std::map<int, int>{ { 1, 1 }, { 2, 2 }, { 3, 3 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 10, 10 } });\n\n        REQUIRE((excluded2 | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>()) ==\n                std::map<int, int>{ { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 10, 10 } });\n\n        REQUIRE((excluded3 | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>()) ==\n                std::map<int, int>{ { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 } });\n    }\n\n    SUBCASE(\"To unordered map\") {\n        REQUIRE((excluded1 | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>()) ==\n                std::unordered_map<int, int>{ { 1, 1 }, { 2, 2 }, { 3, 3 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 10, 10 } });\n\n        REQUIRE((excluded2 | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>()) ==\n                std::unordered_map<int, int>{ { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 10, 10 } });\n\n        REQUIRE((excluded3 | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>()) ==\n                std::unordered_map<int, int>{ { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 } });\n    }\n}\n"
  },
  {
    "path": "tests/exclusive_scan.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/exclusive_scan.hpp>\n#include <Lz/generate.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Exclusive scan with sentinels\") {\n    int i = 0;\n    auto generator = lz::generate([&i]() { return i++; }, 5);\n    lz::exclusive_scan_iterable<decltype(generator)> scan = lz::exclusive_scan(generator, 0);\n    std::vector<int> expected = { 0, 0, 1, 3, 6, 10 };\n    REQUIRE(lz::equal(scan, expected));\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"Operator=\") {\n        std::vector<int> vec = { 1, 2, 3, 4 };\n        auto exclusive_scan = lz::exclusive_scan(vec);\n        auto common = make_sentinel_assign_op_tester(exclusive_scan);\n        auto expected2 = { 0, 1, 3, 6, 10 };\n        REQUIRE(lz::equal(common, expected2));\n        REQUIRE(lz::size(common) == lz::size(expected2));\n    }\n}\n\nTEST_CASE(\"exclusive_scan basic functionality\") {\n    static constexpr std::size_t arr_size = 8;\n    int arr[arr_size] = { 3, 1, 4, 1, 5, 9, 2, 6 };\n    auto scan = arr | lz::exclusive_scan;\n    REQUIRE(scan.size() == arr_size + 1);\n    auto vec = scan | lz::to<std::vector>();\n    (void)vec;\n    static_assert(!std::is_same<decltype(scan.begin()), decltype(scan.end())>::value, \"Iterators should not be the same type\");\n\n    REQUIRE(*scan.begin() == 0);\n    REQUIRE(static_cast<std::size_t>(lz::distance(scan)) == scan.size());\n    REQUIRE(scan.size() == arr_size + 1);\n    constexpr auto is_same = std::is_same<int&, decltype(*scan.begin())>::value;\n    REQUIRE(is_same);\n}\n\nTEST_CASE(\"Empty or one element exclusive scan\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> empty;\n        auto scan = empty | lz::exclusive_scan(0, LZ_BIN_OP(equal_to, int){});\n        REQUIRE(lz::empty(scan));\n        REQUIRE_FALSE(lz::has_one(scan));\n        REQUIRE_FALSE(lz::has_many(scan));\n        REQUIRE(lz::size(scan) == empty.size());\n    }\n\n    SUBCASE(\"One element\") {\n        std::vector<int> one_element = { 1 };\n        auto scan = lz::exclusive_scan(one_element);\n        REQUIRE_FALSE(lz::empty(scan));\n        REQUIRE_FALSE(lz::has_one(scan));\n        REQUIRE(lz::has_many(scan));\n        REQUIRE(*scan.begin() == 0);\n        REQUIRE(lz::size(scan) == one_element.size() + 1);\n    }\n}\n\nTEST_CASE(\"Exclusive scan binary operations\") {\n    int arr[] = { 3, 1, 4, 1, 5, 9, 2 };\n    auto scan = lz::exclusive_scan(arr, 0);\n    REQUIRE(scan.size() == lz::size(arr) + 1);\n\n    REQUIRE(*scan.begin() == 0);\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 0, 3, 4, 8, 9, 14, 23, 25 };\n        REQUIRE(lz::equal(scan, expected));\n    }\n\n    SUBCASE(\"Operator== & operator!=\") {\n        REQUIRE(scan.begin() != scan.end());\n        auto begin = scan.begin();\n        while (begin != scan.end()) {\n            ++begin;\n        }\n        REQUIRE(begin == scan.end());\n        begin = scan.begin();\n        ++begin;\n        REQUIRE(begin != scan.begin());\n        REQUIRE(begin != scan.end());\n    }\n}\n\nTEST_CASE(\"Exclusive scan to container\") {\n    int to_scan[] = { 2, 5, 6, 4, 87, 8, 45, 7 };\n    auto scanner = lz::exclusive_scan(to_scan, 0);\n    REQUIRE(scanner.size() == lz::size(to_scan) + 1);\n\n    SUBCASE(\"To array\") {\n        std::array<int, 9> expected = { 0, 2, 7, 13, 17, 104, 112, 157, 164 };\n        auto actual = scanner | lz::to<std::array<int, expected.size()>>();\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        std::vector<int> expected = { 0, 2, 7, 13, 17, 104, 112, 157, 164 };\n        auto actual = scanner | lz::to<std::vector>();\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        std::list<int> expected = { 0, 2, 7, 13, 17, 104, 112, 157, 164 };\n        auto actual = scanner | lz::to<std::list>();\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To map\") {\n        std::map<int, int> expected = { { 0, 0 },     { 4, 2 },     { 14, 7 },    { 26, 13 },  { 34, 17 },\n                                        { 208, 104 }, { 224, 112 }, { 314, 157 }, { 328, 164 } };\n        auto actual = scanner | lz::map([](int i) { return std::make_pair(i + i, i); }) | lz::to<std::map<int, int>>();\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        std::unordered_map<int, int> expected = { { 0, 0 },     { 4, 2 },     { 14, 7 },    { 26, 13 },  { 34, 17 },\n                                                  { 208, 104 }, { 224, 112 }, { 314, 157 }, { 328, 164 } };\n        auto actual = scanner | lz::map([](int i) { return std::make_pair(i + i, i); }) | lz::to<std::unordered_map<int, int>>();\n        REQUIRE(expected == actual);\n    }\n}\n"
  },
  {
    "path": "tests/filter.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Filter with sentinels\") {\n    const char* str = \"Hello, World!\";\n    auto c_str = lz::c_string(str);\n    std::function<bool(char)> predicate = [](char c) {\n        return c != 'o';\n    };\n    lz::filter_iterable<decltype(c_str), decltype(predicate)> filter = lz::filter(c_str, std::move(predicate));\n    static_assert(!std::is_same<decltype(filter.begin()), decltype(filter.end())>::value, \"Must be sentinel\");\n    std::vector<char> expected = { 'H', 'e', 'l', 'l', ',', ' ', 'W', 'r', 'l', 'd', '!' };\n    REQUIRE((filter | lz::to<std::vector>()) == expected);\n}\n\nnamespace {\nbool pred(int i) {\n    return i % 2 == 0;\n}\n} // namespace\n\nTEST_CASE(\"operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> lst = { 1, 2, 3, 4, 5 };\n        auto f = lz::filter(lst, pred);\n        auto common = make_sentinel_assign_op_tester(f);\n        auto expected = { 2, 4 };\n        REQUIRE(lz::equal(expected, common));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto vec_sent = make_sized_bidi_sentinelled(vec);\n        auto f = lz::filter(vec_sent, [](int i) { return i % 2 == 0; });\n        auto common = make_sentinel_assign_op_tester(f);\n        auto expected = { 2, 4 };\n        REQUIRE(lz::equal(expected, common));\n        REQUIRE(lz::equal(expected | lz::reverse, common | lz::reverse));\n    }\n}\n\nstruct my_pred {\n    bool operator()(int i) const {\n        return i % 2 == 0;\n    }\n};\n\nTEST_CASE(\"Empty or one element filter\") {\n    SUBCASE(\"Empty\") {\n        std::list<int> empty;\n        auto filter = lz::filter(empty, my_pred{});\n        REQUIRE(lz::empty(filter));\n        REQUIRE_FALSE(lz::has_one(filter));\n        REQUIRE_FALSE(lz::has_many(filter));\n    }\n\n    SUBCASE(\"One element\") {\n        std::list<int> one_element = { 1 };\n        auto filter = lz::filter(one_element, [](int i) { return i != 0; });\n        REQUIRE_FALSE(lz::empty(filter));\n        REQUIRE(lz::has_one(filter));\n        REQUIRE_FALSE(lz::has_many(filter));\n        REQUIRE(*filter.begin() == 1);\n    }\n}\n\nTEST_CASE(\"Filter filters and is by reference\") {\n    constexpr size_t size = 3;\n    std::array<int, size> array{ 1, 2, 3 };\n\n    SUBCASE(\"Should filter out element\") {\n        std::function<bool(int)> f = [](int element) {\n            return element != 3;\n        };\n        auto filter = array | lz::filter(std::move(f));\n        static_assert(std::is_same<decltype(filter.begin()), decltype(filter.end())>::value, \"Must not be sentinel\");\n\n        auto it = filter.begin();\n\n        int expected = array[0];\n        REQUIRE(*it == expected);\n\n        ++it;\n        expected = array[1];\n        REQUIRE(*it == expected);\n\n        ++it;\n        REQUIRE(it == filter.end());\n    }\n\n    SUBCASE(\"Should be by reference\") {\n        auto filter = lz::filter(array, [](int element) { return element != 3; });\n        auto it = filter.begin();\n\n        *it = 50;\n        REQUIRE(array[0] == 50);\n    }\n}\n\nTEST_CASE(\"Filter binary operations\") {\n    constexpr std::size_t size = 3;\n    std::array<int, size> array{ 1, 2, 3 };\n    std::function<bool(int)> f = [](int i) {\n        return i != 3;\n    };\n    auto filter = lz::filter(array, std::move(f));\n    auto it = filter.begin();\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 1, 2 };\n        REQUIRE(lz::equal(filter, expected));\n    }\n\n    SUBCASE(\"Operator== & operator!=\") {\n        REQUIRE(it != filter.end());\n        it = filter.end();\n        REQUIRE(it == filter.end());\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto expected = { 2, 1 };\n        REQUIRE(lz::equal(filter | lz::reverse, expected));\n    }\n}\n\nTEST_CASE(\"Filter to container\") {\n    constexpr std::size_t size = 3;\n    std::array<int, size> array{ 1, 2, 3 };\n\n    SUBCASE(\"To array\") {\n        constexpr std::size_t filter_size = 2;\n        auto filtered = lz::filter(array, [](int i) { return i != 3; }) | lz::to<std::array<int, filter_size>>();\n        std::array<int, filter_size> expected = { 1, 2 };\n        REQUIRE(filtered == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        auto filtered_vec = lz::filter(array, [](int i) { return i != 3; }) | lz::to<std::vector>();\n        std::vector<int> expected = { 1, 2 };\n        REQUIRE(filtered_vec == expected);\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        auto filtered_list = lz::filter(array, [](int i) { return i != 3; }) | lz::to<std::list<int>>();\n        std::list<int> expected = { 1, 2 };\n        REQUIRE(filtered_list == expected);\n    }\n\n    SUBCASE(\"To map\") {\n        auto filtered = lz::filter(array, [](const int i) { return i != 3; });\n        auto actual = filtered | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>();\n        std::map<int, int> expected = { std::make_pair(1, 1), std::make_pair(2, 2) };\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto filtered = lz::filter(array, [](const int i) { return i != 3; });\n        auto actual =\n            filtered | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>();\n        std::unordered_map<int, int> expected = { std::make_pair(1, 1), std::make_pair(2, 2) };\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To reverse container\") {\n        auto filtered = lz::filter(array, [](int i) { return i != 3; });\n        using iter = decltype(filtered)::iterator;\n        using sentinel = decltype(filtered.end());\n\n        auto it_rev = std::reverse_iterator<iter>(filtered.end());\n        auto end_rev = std::reverse_iterator<sentinel>(filtered.begin());\n        std::vector<int> reversed(it_rev, end_rev);\n        std::vector<int> expected = { 2, 1 };\n        REQUIRE(reversed == expected);\n    }\n}\n"
  },
  {
    "path": "tests/flatten.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/common.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/flatten.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Dimensions & sized\") {\n    int arr[3];\n    static_assert(lz::dimensions<decltype(arr)>::value == 1, \"Dimensions of array should be 1\");\n    int arr2[3][3];\n    static_assert(lz::dimensions<decltype(arr2)>::value == 2, \"Dimensions of array should be 2\");\n    const int arr3[3][3][3]{};\n    static_assert(lz::dimensions<decltype(arr3)>::value == 3, \"Dimensions of array should be 3\");\n\n    std::vector<int> vec;\n    static_assert(lz::dimensions<decltype(vec)>::value == 1, \"Dimensions of vector should be 1\");\n    std::vector<std::vector<int>> vec2;\n    static_assert(lz::dimensions<decltype(vec2)>::value == 2, \"Dimensions of vector should be 2\");\n    std::vector<std::vector<std::vector<int>>> vec3;\n    static_assert(lz::dimensions<decltype(vec3)>::value == 3, \"Dimensions of vector should be 3\");\n\n    std::array<int, 3> arr4;\n    static_assert(lz::dimensions<decltype(arr4)>::value == 1, \"Dimensions of array should be 1\");\n    std::array<std::array<int, 3>, 3> arr5;\n    static_assert(lz::dimensions<decltype(arr5)>::value == 2, \"Dimensions of array should be 2\");\n    const std::array<std::array<std::array<int, 3>, 3>, 3> arr6{};\n    static_assert(lz::dimensions<decltype(arr6)>::value == 3, \"Dimensions of array should be 3\");\n\n    auto str = lz::c_string(\"Hello, World!\");\n    static_assert(!lz::detail::is_all_sized<decltype(str)>::value, \"c_string should not be sized\");\n    std::array<decltype(lz::c_string(\"\")), 2> arr_of_cstr = { lz::c_string(\"\"), lz::c_string(\"\") };\n    static_assert(!lz::detail::is_all_sized<decltype(arr_of_cstr)>::value, \"Array of c_string should not all be sized\");\n\n    std::vector<int> v;\n    auto filter = lz::filter(v, [](int) { return true; });\n    static_assert(!lz::detail::is_all_sized<decltype(filter)>::value, \"Filter should not be sized\");\n\n    std::array<std::array<decltype(lz::c_string(\"\")), 1>, 1> arr_of_arr_of_cstr = { { { lz::c_string(\"\") } } };\n    static_assert(!lz::detail::is_all_sized<decltype(arr_of_arr_of_cstr)>::value, \"Array of array of c_string should be sized\");\n}\n\nTEST_CASE(\"Flatten with sentinels\") {\n    using c_string = decltype(lz::c_string(\"\"));\n\n    std::forward_list<c_string> fwd_list = { lz::c_string(\"Hello\"), lz::c_string(\", \"), lz::c_string(\"World\"),\n                                             lz::c_string(\"!\") };\n    lz::flatten_iterable<decltype(fwd_list)> flattened = lz::flatten(fwd_list);\n    static_assert(lz::detail::is_fwd<decltype(flattened.begin())>::value, \"Flattened should be fwd\");\n\n    auto str = flattened | lz::to<std::string>();\n    REQUIRE(str == \"Hello, World!\");\n\n    auto flatten_one = lz::flatten(lz::c_string(\"hello, world\"));\n    REQUIRE((flatten_one | lz::to<std::string>()) == \"hello, world\");\n\n    SUBCASE(\"Operator= 1D\") {\n        auto it = flatten_one.begin();\n        REQUIRE(it == flatten_one.begin());\n        it = flatten_one.end();\n        REQUIRE(it == flatten_one.end());\n    }\n\n    c_string arr[] = { lz::c_string(\"Hello\"), lz::c_string(\", \"), lz::c_string(\"World\"), lz::c_string(\"!\") };\n    auto flattened_array = lz::flatten(arr);\n    static_assert(lz::detail::is_fwd<decltype(flattened_array.begin())>::value, \"Flattened should be fwd\");\n\n    SUBCASE(\"Operator= 2D random access\") {\n        auto it = flattened_array.begin();\n        REQUIRE(it == flattened_array.begin());\n        it = flattened_array.end();\n        REQUIRE(it == flattened_array.end());\n    }\n\n    REQUIRE((flattened_array | lz::to<std::string>()) == \"Hello, World!\");\n\n    std::forward_list<std::vector<int>> vec = { { 1, 2, 3 }, { 4, 5 }, { 6, 7 } };\n    auto flattened_lst = lz::flatten(vec);\n    static_assert(lz::detail::is_fwd<decltype(flattened_lst.begin())>::value, \"Flattened should be fwd\");\n    auto expected = std::vector<int>{ 1, 2, 3, 4, 5, 6, 7 };\n    REQUIRE(lz::equal(flattened_lst, expected));\n\n    SUBCASE(\"Operator= 2D fwd with std vector\") {\n        auto it = flattened_lst.begin();\n        REQUIRE(it == flattened_lst.begin());\n        it = flattened_lst.end();\n        REQUIRE(it == flattened_array.end());\n    }\n}\n\nTEST_CASE(\"operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<std::forward_list<int>> lst = { { 1, 2 }, { 3, 4 }, { 5, 6 } };\n        auto flat = lst | lz::flatten;\n        auto common = make_sentinel_assign_op_tester(flat);\n        auto expected = { 1, 2, 3, 4, 5, 6 };\n        REQUIRE(lz::equal(expected, common));\n\n        std::forward_list<int> lst2 = { 1, 2, 3, 4, 5, 6 };\n        auto flat2 = lst2 | lz::flatten;\n        auto common2 = make_sentinel_assign_op_tester(flat2);\n        REQUIRE(lz::equal(expected, common2));\n\n        std::forward_list<std::forward_list<std::forward_list<int>>> lst3 = { { { 1, 2 }, { 3 } }, { { 4, 5 }, { 6 } } };\n        auto flat3 = lst3 | lz::flatten;\n        auto common3 = make_sentinel_assign_op_tester(flat3);\n        REQUIRE(lz::equal(expected, common3));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<std::vector<int>> vec = { { 1, 2 }, { 3, 4 }, { 5, 6 } };\n        auto vec_sent = make_sized_bidi_sentinelled(vec);\n        auto flat = lz::flatten(vec_sent);\n        auto common = make_sentinel_assign_op_tester(flat);\n        auto expected = { 1, 2, 3, 4, 5, 6 };\n        REQUIRE(lz::equal(expected, common));\n        REQUIRE(lz::equal(expected | lz::reverse, common | lz::reverse));\n\n        std::vector<int> vec2 = { 1, 2, 3, 4, 5, 6 };\n        auto vec2_sent = make_sized_bidi_sentinelled(vec2);\n        auto flat2 = lz::flatten(vec2_sent);\n        auto common2 = make_sentinel_assign_op_tester(flat2);\n        REQUIRE(lz::equal(expected, common2));\n        REQUIRE(lz::equal(expected | lz::reverse, common2 | lz::reverse));\n\n        std::vector<std::vector<std::vector<int>>> vec3 = { { { 1, 2 }, { 3 } }, { { 4, 5 }, { 6 } } };\n        auto vec3_sent = make_sized_bidi_sentinelled(vec3);\n        auto flat3 = lz::flatten(vec3_sent);\n        auto common3 = make_sentinel_assign_op_tester(flat3);\n        REQUIRE(lz::equal(expected, common3));\n        REQUIRE(lz::equal(expected | lz::reverse, common3 | lz::reverse));\n    }\n\n    SUBCASE(\"random access\") {\n        auto repeater = lz::repeat(1, 5);\n        auto flat = make_sentinel_assign_op_tester(lz::flatten(repeater));\n        auto expected = { 1, 1, 1, 1, 1 };\n        REQUIRE(lz::equal(expected, flat));\n        REQUIRE(lz::equal(expected | lz::reverse, flat | lz::reverse));\n        test_procs::test_operator_minus(flat);\n        test_procs::test_operator_plus(flat, expected);\n\n        auto repeater2 = lz::repeat(lz::repeat(1, 2), 3);\n        auto flat2 = make_sentinel_assign_op_tester(lz::flatten(repeater2));\n        auto expected2 = { 1, 1, 1, 1, 1, 1 };\n        REQUIRE(lz::equal(expected2, flat2));\n        REQUIRE(lz::equal(expected2 | lz::reverse, flat2 | lz::reverse));\n        test_procs::test_operator_minus(flat2);\n        test_procs::test_operator_plus(flat2, expected2);\n\n        auto repeater3 = lz::repeat(lz::repeat(lz::repeat(1, 2), 2), 2);\n        auto flat3 = make_sentinel_assign_op_tester(lz::flatten(repeater3));\n        auto expected3 = { 1, 1, 1, 1, 1, 1, 1, 1 };\n        REQUIRE(lz::equal(expected3, flat3));\n        REQUIRE(lz::equal(expected3 | lz::reverse, flat3 | lz::reverse));\n        test_procs::test_operator_minus(flat3);\n    }\n}\n\nTEST_CASE(\"Empty or one element flatten\") {\n    SUBCASE(\"Empty 1D\") {\n        std::vector<std::vector<int>> empty;\n        auto empty_flattened = lz::flatten(empty);\n        REQUIRE(lz::empty(empty_flattened));\n        REQUIRE_FALSE(lz::has_one(empty_flattened));\n        REQUIRE_FALSE(lz::has_many(empty_flattened));\n        REQUIRE(empty_flattened.size() == 0);\n    }\n\n    SUBCASE(\"Empty 2D\") {\n        std::vector<std::vector<int>> empty;\n        auto empty_flattened = lz::flatten(empty);\n        REQUIRE(lz::empty(empty_flattened));\n        REQUIRE_FALSE(lz::has_one(empty_flattened));\n        REQUIRE_FALSE(lz::has_many(empty_flattened));\n        REQUIRE(empty_flattened.size() == 0);\n    }\n\n    SUBCASE(\"Empty 3D\") {\n        std::vector<std::vector<std::vector<int>>> empty;\n        auto empty_flattened = lz::flatten(empty);\n        REQUIRE(lz::empty(empty_flattened));\n        REQUIRE_FALSE(lz::has_one(empty_flattened));\n        REQUIRE_FALSE(lz::has_many(empty_flattened));\n        REQUIRE(empty_flattened.size() == 0);\n    }\n\n    SUBCASE(\"Empty 4D\") {\n        std::vector<std::vector<std::vector<std::vector<int>>>> empty;\n        auto empty_flattened = lz::flatten(empty);\n        REQUIRE(lz::empty(empty_flattened));\n        REQUIRE_FALSE(lz::has_one(empty_flattened));\n        REQUIRE_FALSE(lz::has_many(empty_flattened));\n        REQUIRE(empty_flattened.size() == 0);\n    }\n\n    SUBCASE(\"One element 1D\") {\n        std::vector<int> one_element = { 1 };\n        auto one_elm_flattened = lz::flatten(one_element);\n        REQUIRE_FALSE(lz::empty(one_elm_flattened));\n        REQUIRE(lz::has_one(one_elm_flattened));\n        REQUIRE_FALSE(lz::has_many(one_elm_flattened));\n        REQUIRE(one_elm_flattened.size() == 1);\n    }\n\n    SUBCASE(\"One element 2D\") {\n        std::vector<std::vector<int>> one_element = { { 1 } };\n        auto one_elm_flattened = lz::flatten(one_element);\n        REQUIRE_FALSE(lz::empty(one_elm_flattened));\n        REQUIRE(lz::has_one(one_elm_flattened));\n        REQUIRE_FALSE(lz::has_many(one_elm_flattened));\n        REQUIRE(one_elm_flattened.size() == 1);\n    }\n\n    SUBCASE(\"One element 3D\") {\n        std::vector<std::vector<std::vector<int>>> one_element = { { { 1 } } };\n        auto one_elm_flattened = lz::flatten(one_element);\n        REQUIRE_FALSE(lz::empty(one_elm_flattened));\n        REQUIRE(lz::has_one(one_elm_flattened));\n        REQUIRE_FALSE(lz::has_many(one_elm_flattened));\n        REQUIRE(one_elm_flattened.size() == 1);\n    }\n\n    SUBCASE(\"One element 4D\") {\n        std::vector<std::vector<std::vector<std::vector<int>>>> one_element = { { { { 1 } } } };\n        auto one_elm_flattened = lz::flatten(one_element);\n        REQUIRE_FALSE(lz::empty(one_elm_flattened));\n        REQUIRE(lz::has_one(one_elm_flattened));\n        REQUIRE_FALSE(lz::has_many(one_elm_flattened));\n        REQUIRE(one_elm_flattened.size() == 1);\n    }\n}\n\nnamespace {\n\ntemplate<class FlattenIterable, class ExpectedIterable>\nvoid test_flatten_operators_mm_and_pp(const FlattenIterable& flattened, const ExpectedIterable& expected) {\n    REQUIRE(flattened.size() == lz::size(expected));\n    REQUIRE(lz::equal(flattened, expected));\n    REQUIRE(lz::equal(lz::reverse(flattened), lz::reverse(expected)));\n}\n\n} // namespace\n\nTEST_CASE(\"Should flatten permutations\") {\n    SUBCASE(\"Flatten 1D\") {\n        std::vector<int> vec = { 1, 2, 3, 4 };\n        auto flattened = lz::flatten(vec);\n        REQUIRE((flattened | lz::to<std::vector>()) == std::vector<int>{ 1, 2, 3, 4 });\n        REQUIRE((lz::reverse(flattened) | lz::to<std::vector>()) == std::vector<int>{ 4, 3, 2, 1 });\n        REQUIRE(flattened.size() == 4);\n    }\n\n    SUBCASE(\"Flatten 2D\") {\n        std::vector<int> expected = { 1, 2, 3, 4, 5, 6, 7 };\n\n        std::vector<std::vector<int>> vec = { { 1, 2, 3 }, { 4, 5 }, { 6, 7 } };\n        auto f = lz::flatten(vec);\n\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { { 1, 2, 3 }, {}, { 4, 5 }, { 6, 7 } };\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { { 1, 2, 3 }, { 4, 5 }, {}, { 6, 7 } };\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { { 1, 2, 3 }, { 4, 5 }, { 6, 7 }, {} };\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { { 1, 2, 3 }, {}, {}, { 4, 5, 6, 7 } };\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { {}, { 1, 2, 3 }, { 4, 5, 6, 7 }, {} };\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { {}, {}, {}, { 1, 2, 3, 4, 5, 6, 7 } };\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { { 1, 2, 3, 4, 5, 6, 7 } };\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 } };\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { {}, {}, { 1, 2, 3 }, {}, {}, { 4, 5, 6, 7 }, {}, {} };\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n    }\n\n    SUBCASE(\"Flatten 3D\") {\n        std::vector<int> expected = { 1, 4, 5, 6, 7, 8, 9 };\n\n        std::vector<std::vector<std::vector<int>>> vec = { { { 1 }, { 4, 5 } }, { { 6, 7, 8, 9 } } };\n        auto f = lz::flatten(vec);\n        test_procs::test_operator_plus(f, expected);\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { { { 1 } }, { { 4 } }, { { 5 } }, { { 6 } }, { { 7 } }, { { 8 } }, { { 9 } } };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n\n        vec = { { { 1 }, {}, { 4, 5 } }, { {}, { 6, 7, 8, 9 } } };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n\n        vec = { { { 1 }, {}, { 4, 5 } }, { {}, {}, { 6, 7, 8, 9 } } };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n\n        vec = { { {}, {}, { 1 }, { 4, 5 } }, { {}, { 6, 7, 8, 9 } } };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n\n        vec = { { {}, {}, { 1 }, { 4, 5 } }, { {}, {}, { 6, 7, 8, 9 } } };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n\n        vec = { { {}, {}, { 1 }, { 4, 5 } }, { {}, {}, { 6, 7, 8, 9 }, {}, {} }, {}, {} };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n    }\n\n    SUBCASE(\"Flatten 4D\") {\n        std::vector<int> expected = { 1, 4, 5, 6, 7, 8, 9 };\n\n        std::vector<std::vector<std::vector<std::vector<int>>>> vec = { { { { 1 }, { 4, 5 } } }, { { { 6, 7, 8, 9 } } } };\n        auto f = lz::flatten(vec);\n        test_procs::test_operator_plus(f, expected);\n        test_flatten_operators_mm_and_pp(f, expected);\n        test_procs::test_operator_minus(f);\n\n        vec = { { { { 1 } }, { { 4 }, { 5 }, { 6 }, { 7 }, { 8 }, { 9 } } } };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n\n        vec = { { { { 1 }, {}, { 4, 5 } }, { {}, { 6, 7, 8, 9 } } } };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n\n        vec = { { { {}, {}, { 1 }, { 4, 5 } }, { {}, { 6, 7, 8, 9 } } } };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n\n        vec = { { { {}, {}, { 1 }, { 4, 5 } }, { {}, {}, { 6, 7, 8, 9 } } } };\n        test_procs::test_operator_plus(f, expected);\n        test_procs::test_operator_minus(f);\n        test_flatten_operators_mm_and_pp(f, expected);\n    }\n\n    SUBCASE(\"Flatten with 1D sentinels\") {\n        auto f = lz::flatten(lz::repeat(1, 10));\n        REQUIRE(lz::equal(f, lz::repeat(1, 10)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(1, 10)));\n        std::vector<int> expected = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n\n        expected = {};\n        f = lz::flatten(lz::repeat(1, 0));\n        REQUIRE(lz::equal(f, lz::repeat(1, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(1, 0)));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n    }\n\n    SUBCASE(\"Flatten with 2D sentinels\") {\n        std::vector<int> expected = { 5, 5, 5, 5, 5, 5, 5, 5, 5 };\n        auto f = lz::flatten(lz::repeat(lz::repeat(5, 3), 3));\n\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 3 * 3)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 3 * 3)));\n\n        expected = {};\n        f = lz::flatten(lz::repeat(lz::repeat(5, 3), 0));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 0)));\n\n        f = lz::flatten(lz::repeat(lz::repeat(5, 0), 3));\n        test_procs::test_operator_minus(f);\n        REQUIRE(lz::equal(f, lz::repeat(5, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 0)));\n    }\n\n    SUBCASE(\"Flatten with 3D sentinels\") {\n        auto expected = lz::repeat(5, 3 * 3 * 3) | lz::to<std::vector>();\n        auto f = lz::flatten(lz::repeat(lz::repeat(lz::repeat(5, 3), 3), 3));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 3 * 3 * 3)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 3 * 3 * 3)));\n\n        expected = {};\n        f = lz::flatten(lz::repeat(lz::repeat(lz::repeat(5, 3), 3), 0));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 0)));\n\n        f = lz::flatten(lz::repeat(lz::repeat(lz::repeat(5, 3), 0), 3));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 0)));\n\n        f = lz::flatten(lz::repeat(lz::repeat(lz::repeat(5, 0), 3), 3));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 0)));\n    }\n\n    SUBCASE(\"Flatten with 4D sentinels\") {\n        auto expected = lz::repeat(5, 2 * 2 * 1 * 2) | lz::to<std::vector>();\n        auto f = lz::flatten(lz::repeat(lz::repeat(lz::repeat(lz::repeat(5, 2), 2), 1), 2));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 2 * 2 * 1 * 2)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 2 * 2 * 1 * 2)));\n\n        expected = {};\n        f = lz::flatten(lz::repeat(lz::repeat(lz::repeat(lz::repeat(5, 2), 2), 2), 0));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 0)));\n\n        f = lz::flatten(lz::repeat(lz::repeat(lz::repeat(lz::repeat(5, 2), 2), 0), 2));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 0)));\n\n        f = lz::flatten(lz::repeat(lz::repeat(lz::repeat(lz::repeat(5, 0), 0), 2), 2));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 0)));\n\n        f = lz::flatten(lz::repeat(lz::repeat(lz::repeat(lz::repeat(5, 0), 2), 2), 2));\n        test_procs::test_operator_minus(f);\n        test_procs::test_operator_plus(f, expected);\n        REQUIRE(lz::equal(f, lz::repeat(5, 0)));\n        REQUIRE(lz::equal(f | lz::common | lz::reverse, lz::repeat(5, 0)));\n    }\n\n    SUBCASE(\"Should be by ref\") {\n        std::vector<std::vector<std::vector<int>>> vectors = {\n            { { 0 }, { 1, 2, 3 }, {}, { 4 } }, { {} }, { { 5, 6 }, { 7 }, {} }, { {} }, { {} }\n        };\n        static_assert(std::is_lvalue_reference<decltype(*lz::flatten(vectors).begin())>::value, \"\");\n    }\n}\n\nTEST_CASE(\"Flatten to container\") {\n    std::vector<std::vector<int>> vecs = { { 1, 2, 3 }, { 4, 5 }, {}, { 6, 7 } };\n    auto flattened = lz::flatten(vecs);\n    REQUIRE(flattened.size() == 7);\n\n    SUBCASE(\"To array\") {\n        REQUIRE((flattened | lz::to<std::array<int, 7>>()) == std::array<int, 7>{ 1, 2, 3, 4, 5, 6, 7 });\n    }\n\n    SUBCASE(\"To vector\") {\n        REQUIRE((flattened | lz::to<std::vector>()) == std::vector<int>{ 1, 2, 3, 4, 5, 6, 7 });\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        REQUIRE((flattened | lz::to<std::list<int>>()) == std::list<int>{ 1, 2, 3, 4, 5, 6, 7 });\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = flattened | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>();\n\n        std::map<int, int> expected = {\n            std::make_pair(1, 1), std::make_pair(2, 2), std::make_pair(3, 3), std::make_pair(4, 4),\n            std::make_pair(5, 5), std::make_pair(6, 6), std::make_pair(7, 7),\n        };\n\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto actual =\n            flattened | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>();\n\n        std::unordered_map<int, int> expected = {\n            std::make_pair(1, 1), std::make_pair(2, 2), std::make_pair(3, 3), std::make_pair(4, 4),\n            std::make_pair(5, 5), std::make_pair(6, 6), std::make_pair(7, 7),\n        };\n\n        REQUIRE(expected == actual);\n    }\n}\n\nTEST_CASE(\"Stack allocated flatten\") {\n    std::array<std::array<int, 8>, 4> a{};\n    int n = 0;\n    std::generate(a.begin(), a.end(), [&n]() mutable {\n        std::array<int, 8> arr{};\n        std::generate(arr.begin(), arr.end(), [&n]() mutable { return n++; });\n        return arr;\n    });\n\n    const auto expected = { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,\n                            16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };\n    auto f = lz::flatten(a);\n    test_procs::test_operator_plus(f, expected);\n    test_procs::test_operator_minus(f);\n    test_flatten_operators_mm_and_pp(f, expected);\n}\n"
  },
  {
    "path": "tests/generate.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/generate.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Generate infinite\") {\n    std::function<int()> func = []() {\n        return 0;\n    };\n    lz::generate_iterable_inf<decltype(func)> generator = lz::generate(std::move(func));\n    static_assert(!std::is_same<decltype(generator.begin()), decltype(generator.end())>::value, \"Should be sentinel\");\n    REQUIRE(generator.begin() != generator.end());\n    REQUIRE(generator.begin() != generator.begin());\n\n    auto begin = generator.begin();\n    REQUIRE(*begin == 0);\n    REQUIRE(begin != generator.end());\n    REQUIRE(begin != generator.begin());\n    ++begin;\n    REQUIRE(*begin == 0);\n    REQUIRE(begin != generator.end());\n    REQUIRE(begin != generator.begin());\n\n    SUBCASE(\"Operator=(defatult_sentinel_t)\") {\n        int i = 0;\n        auto gen = lz::generate([&i]() { return i++; }, 5);\n        auto common = make_sentinel_assign_op_tester(gen);\n        auto expected = { 0, 1, 2, 3, 4 };\n        REQUIRE(lz::equal(common, expected));\n        REQUIRE(lz::size(common) == lz::size(expected));\n    }\n}\n\nTEST_CASE(\"Generate changing and creating elements\") {\n    constexpr std::size_t amount = 4;\n    std::size_t counter = 0;\n    auto generator = lz::generate(\n        [&counter]() {\n            auto tmp{ counter++ };\n            return tmp;\n        },\n        amount);\n\n    SUBCASE(\"Should be 0, 1, 2, 3\") {\n        std::size_t expected = 0;\n        lz::for_each(generator, [&expected](std::size_t i) {\n            REQUIRE(i == expected);\n            ++expected;\n        });\n    }\n\n    SUBCASE(\"Operator=\") {\n        auto begin = generator.begin();\n        REQUIRE(begin != generator.end());\n        begin = generator.end();\n        REQUIRE(begin == generator.end());\n    }\n}\n\nTEST_CASE(\"Generate binary operations\") {\n    constexpr std::size_t amount = 4;\n    std::size_t counter = 0;\n    std::function<std::size_t()> func = [&counter]() {\n        auto tmp{ counter++ };\n        return tmp;\n    };\n    lz::generate_iterable<decltype(func)> generator = lz::generate(std::move(func), amount);\n    auto begin = generator.begin();\n\n    SUBCASE(\"Operator++\") {\n        std::size_t expected[] = { 0, 1, 2, 3 };\n        REQUIRE(lz::equal(generator, expected));\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        REQUIRE(begin != generator.end());\n        while (begin != generator.end()) {\n            ++begin;\n        }\n        REQUIRE(begin == generator.end());\n    }\n}\n\nTEST_CASE(\"Empty or one element generate\") {\n    SUBCASE(\"Empty\") {\n        auto generator = lz::generate([]() { return 0; }, 0);\n        REQUIRE(lz::empty(generator));\n        REQUIRE_FALSE(lz::has_one(generator));\n        REQUIRE_FALSE(lz::has_many(generator));\n    }\n\n    SUBCASE(\"One element\") {\n        auto generator = lz::generate([]() { return 0; }, 1);\n        REQUIRE_FALSE(lz::empty(generator));\n        REQUIRE(lz::has_one(generator));\n        REQUIRE_FALSE(lz::has_many(generator));\n    }\n}\n\nTEST_CASE(\"Generate to containers\") {\n    constexpr std::size_t amount = 4;\n    std::size_t counter = 0;\n\n    auto generator = lz::generate(\n        [&counter]() {\n            auto tmp{ counter++ };\n            return tmp;\n        },\n        amount);\n\n    SUBCASE(\"To array\") {\n        auto array = generator | lz::to<std::array<std::size_t, amount>>();\n        std::array<std::size_t, amount> expected = { 0, 1, 2, 3 };\n\n        REQUIRE(array == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        auto vector = generator | lz::to<std::vector>();\n        std::vector<std::size_t> expected = { 0, 1, 2, 3 };\n\n        REQUIRE(vector == expected);\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        auto vector = generator | lz::to<std::list>();\n        std::list<std::size_t> expected = { 0, 1, 2, 3 };\n\n        REQUIRE(vector == expected);\n    }\n\n    SUBCASE(\"To map\") {\n        auto map = generator | lz::map([](const std::size_t elm) { return std::make_pair(elm * 10, elm); }) |\n                   lz::to<std::map<std::size_t, std::size_t>>();\n\n        std::map<std::size_t, std::size_t> expected = { { 0, 0 }, { 10, 1 }, { 20, 2 }, { 30, 3 } };\n\n        REQUIRE(map == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto map = generator | lz::map([](const std::size_t elm) { return std::make_pair(elm * 10, elm); }) |\n                   lz::to<std::unordered_map<std::size_t, std::size_t>>();\n\n        std::unordered_map<std::size_t, std::size_t> expected = { { 0, 0 }, { 10, 1 }, { 20, 2 }, { 30, 3 } };\n\n        REQUIRE(map == expected);\n    }\n}\n"
  },
  {
    "path": "tests/generate_while.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/generate_while.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Generate while changing and creating elements\") {\n    const auto compile_test1 = lz::generate_while([]() { return std::make_pair(false, false); });\n    static_cast<void>(compile_test1);\n\n    const unsigned i2 = 0;\n    const auto compile_test2 = lz::generate_while([&i2]() {\n        static_cast<void>(i2);\n        return std::make_pair(false, false);\n    });\n    static_cast<void>(compile_test2);\n    static_assert(!std::is_same<decltype(compile_test1.begin()), decltype(compile_test1.end())>::value, \"Should be sentinel\");\n\n    SUBCASE(\"Should be 0, 1, 2, 3\") {\n        unsigned i = 0;\n        auto generator = lz::generate_while([&i]() {\n            auto copy = i++;\n            return std::make_pair(copy, copy != 4);\n        });\n        std::array<unsigned, 4> expected = { 0, 1, 2, 3 };\n        auto actual = generator | lz::to<std::array<unsigned, expected.size()>>();\n        REQUIRE(expected == actual);\n        i = 0;\n    }\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        unsigned j = 0;\n        auto gen = lz::generate_while([&j]() {\n            j++;\n            return std::make_pair(j, j < 5);\n        });\n        auto common = make_sentinel_assign_op_tester(gen);\n        auto expected = { 1u, 2u, 3u, 4u };\n        REQUIRE(lz::equal(common, expected));\n    }\n}\n\nTEST_CASE(\"Empty or one element generate while\") {\n    SUBCASE(\"Empty\") {\n        std::function<std::pair<bool, unsigned>()> func = []() {\n            return std::make_pair(0, false);\n        };\n        lz::generate_while_iterable<decltype(func)> generator = lz::generate_while(std::move(func));\n        REQUIRE(lz::empty(generator));\n        REQUIRE_FALSE(lz::has_one(generator));\n        REQUIRE_FALSE(lz::has_many(generator));\n    }\n\n    SUBCASE(\"One element\") {\n        bool b = true;\n        auto generator = lz::generate_while([&b]() {\n            auto p = std::make_pair(0, b);\n            b = false;\n            return p;\n        });\n        REQUIRE_FALSE(lz::empty(generator));\n        REQUIRE(lz::has_one(generator));\n        REQUIRE_FALSE(lz::has_many(generator));\n    }\n}\n\nTEST_CASE(\"Generate while binary operations\") {\n    auto test = lz::generate_while([]() { return std::make_pair(0u, true); });\n    static_assert(std::is_same<decltype(*test.begin()), unsigned>::value,\n                  \"unsigned and decltype(*generator.begin()) are not the same\");\n\n    SUBCASE(\"Operator++\") {\n        unsigned i = 0;\n        auto generator = lz::generate_while([&i]() {\n            auto copy = i++;\n            return std::make_pair(copy, copy != 4);\n        });\n        auto expected = std::vector<unsigned>{ 0, 1, 2, 3 };\n        REQUIRE(lz::equal(generator, expected));\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        unsigned i = 0;\n        auto generator = lz::generate_while([&i]() {\n            auto copy = i++;\n            return std::make_pair(copy, copy != 4);\n        });\n        auto begin = generator.begin();\n        ++begin;\n        REQUIRE(begin != generator.begin());\n        REQUIRE(generator.begin() != begin);\n        REQUIRE_FALSE(begin == generator.begin());\n        REQUIRE_FALSE(generator.begin() == begin);\n        ++begin;\n        ++begin;\n        ++begin;\n        REQUIRE(begin == generator.end());\n        begin = generator.begin();\n        i = 0;\n        REQUIRE(*begin == 0);\n        while (begin != generator.end()) {\n            ++begin;\n        }\n        REQUIRE(begin == generator.end());\n    }\n}\n\nTEST_CASE(\"Generate while to containers\") {\n    SUBCASE(\"To array\") {\n        unsigned i = 0;\n        auto generator = lz::generate_while([&i]() {\n            auto copy = i++;\n            return std::make_pair(copy, copy != 4);\n        });\n        std::array<unsigned, 4> expected = { 0, 1, 2, 3 };\n        auto actual = generator | lz::to<std::array<unsigned, 4>>();\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To vector\") {\n        unsigned i = 0;\n        auto generator = lz::generate_while([&i]() {\n            auto copy = i++;\n            return std::make_pair(copy, copy != 4);\n        });\n        auto it = generator.begin();\n        it = generator.end();\n        REQUIRE(it == generator.end());\n        REQUIRE(generator.end() == it);\n        REQUIRE_FALSE(it == generator.begin());\n        REQUIRE(it != generator.begin());\n        REQUIRE(generator.begin() != it);\n        std::vector<unsigned> expected = { 0, 1, 2, 3 };\n        auto actual = generator | lz::to<std::vector>();\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        unsigned i = 0;\n        auto generator = lz::generate_while([&i]() {\n            auto copy = i++;\n            return std::make_pair(copy, copy != 4);\n        });\n        std::list<unsigned> expected = { 0, 1, 2, 3 };\n        auto actual = generator | lz::to<std::list<unsigned>>();\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To map\") {\n        unsigned i = 0;\n        auto generator = lz::generate_while([&i]() {\n            auto copy = i++;\n            return std::make_pair(copy, copy != 4);\n        });\n        std::map<unsigned, unsigned> expected = { { 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 } };\n        auto actual =\n            generator | lz::map([](unsigned x) { return std::make_pair(x, x); }) | lz::to<std::map<unsigned, unsigned>>();\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        unsigned i = 0;\n        auto generator = lz::generate_while([&i]() {\n            auto copy = i++;\n            return std::make_pair(copy, copy != 4);\n        });\n        std::unordered_map<unsigned, unsigned> expected = { { 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 } };\n        auto actual = generator | lz::map([](unsigned x) { return std::make_pair(x, x); }) |\n                      lz::to<std::unordered_map<unsigned, unsigned>>();\n        REQUIRE(actual == expected);\n    }\n}\n"
  },
  {
    "path": "tests/group_by.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/group_by.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nstruct eq_pair {\n    template<class T1, class T2>\n    bool operator()(const T1& first, const T2& second) const {\n        return first.first == second.first && lz::equal(first.second, second.second);\n    }\n};\n\nTEST_CASE(\"Group by with sentinels\") {\n    auto cstr = lz::c_string(\"aaabbccccd\");\n    std::function<bool(char, char)> equal = [](char a, char b) {\n        return a == b;\n    };\n\n    lz::group_by_iterable<decltype(cstr), decltype(equal)> grouper = lz::group_by(cstr, std::move(equal));\n    auto expected = { std::make_pair('a', lz::c_string(\"aaa\")), std::make_pair('b', lz::c_string(\"bb\")),\n                      std::make_pair('c', lz::c_string(\"cccc\")), std::make_pair('d', lz::c_string(\"d\")) };\n\n    REQUIRE(lz::equal(grouper, expected, eq_pair{}));\n}\n\nTEST_CASE(\"operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> lst = { 1, 1, 2, 2, 3, 4, 4 };\n        auto grouped = lz::group_by(lst, LZ_BIN_OP(equal_to, int){});\n        auto common = make_sentinel_assign_op_tester(grouped);\n        using reference = lz::detail::ref_iterable_t<decltype(common)>;\n        std::vector<std::pair<int, std::vector<int>>> expected2 = { std::make_pair(1, std::vector<int>{ 1, 1 }),\n                                                                    std::make_pair(2, std::vector<int>{ 2, 2 }),\n                                                                    std::make_pair(3, std::vector<int>{ 3 }),\n                                                                    std::make_pair(4, std::vector<int>{ 4, 4 }) };\n        REQUIRE(lz::equal(common, expected2, [](reference a, const std::pair<int, std::vector<int>>& b) {\n            return a.first == b.first && lz::equal(a.second, b.second);\n        }));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> vec = { 1, 1, 2, 2, 3, 4, 4 };\n        auto bidi_sentinelled = make_sized_bidi_sentinelled(vec);\n        auto grouped = lz::group_by(bidi_sentinelled, LZ_BIN_OP(equal_to, int){});\n        auto common = make_sentinel_assign_op_tester(grouped);\n        using reference = lz::detail::ref_iterable_t<decltype(common)>;\n        std::vector<std::pair<int, std::vector<int>>> expected2 = { std::make_pair(1, std::vector<int>{ 1, 1 }),\n                                                                    std::make_pair(2, std::vector<int>{ 2, 2 }),\n                                                                    std::make_pair(3, std::vector<int>{ 3 }),\n                                                                    std::make_pair(4, std::vector<int>{ 4, 4 }) };\n        REQUIRE(lz::equal(common, expected2, [](reference a, const std::pair<int, std::vector<int>>& b) {\n            return a.first == b.first && lz::equal(a.second, b.second);\n        }));\n        REQUIRE(\n            lz::equal(common | lz::reverse, expected2 | lz::reverse, [](reference a, const std::pair<int, std::vector<int>>& b) {\n                return a.first == b.first && lz::equal(a.second, b.second);\n            }));\n    }\n}\n\nTEST_CASE(\"Empty or one element group by\") {\n    SUBCASE(\"Empty\") {\n        auto grouper = lz::group_by(lz::c_string(\"\"), [](char a, char b) { return a == b; });\n        REQUIRE(lz::empty(grouper));\n        REQUIRE_FALSE(lz::has_one(grouper));\n        REQUIRE_FALSE(lz::has_many(grouper));\n    }\n\n    SUBCASE(\"One element\") {\n        auto grouper = lz::group_by(lz::c_string(\"a\"), [](char a, char b) { return a == b; });\n        static_assert(!lz::detail::is_bidi<decltype(grouper.begin())>::value, \"Should not be bidirectional iterator\");\n        REQUIRE_FALSE(lz::empty(grouper));\n        REQUIRE(lz::has_one(grouper));\n        REQUIRE_FALSE(lz::has_many(grouper));\n    }\n}\n\nTEST_CASE(\"group_by changing and creating elements\") {\n    std::vector<std::string> vec = { \"i'm\", \"done\", \"hello\", \"hellp\" };\n\n    auto grouper = vec | lz::group_by([](const std::string& a, const std::string& b) { return a.length() == b.length(); });\n    static_assert(lz::detail::is_bidi<decltype(grouper.begin())>::value, \"Should be bidirectional iterator\");\n\n    SUBCASE(\"Should be correct chunks\") {\n        std::size_t str_len = 3;\n\n        using value_type = lz::detail::val_iterable_t<decltype(grouper)>;\n        lz::for_each(grouper, [&str_len](const value_type& g) {\n            REQUIRE(g.first.length() == str_len);\n            for (const auto& str : g.second) {\n                REQUIRE(str.length() == str_len);\n            }\n            ++str_len;\n        });\n    }\n\n    SUBCASE(\"Should be by ref\") {\n        auto begin = grouper.begin();\n        *(begin->second.begin()) = \"imm\";\n        REQUIRE(vec[0] == \"imm\");\n    }\n}\n\nTEST_CASE(\"group_by binary operations\") {\n    std::vector<std::string> vec = { \"a\", \"bb\", \"ccc\", \"ccc\", \"dddd\", \"dddd\" };\n    auto grouper = lz::group_by(vec, [](const std::string& a, const std::string& b) { return a.length() == b.length(); });\n\n    SUBCASE(\"Operator++\") {\n        std::vector<std::pair<std::string, std::vector<std::string>>> expected = {\n            std::make_pair(\"a\", std::vector<std::string>{ \"a\" }), std::make_pair(\"bb\", std::vector<std::string>{ \"bb\" }),\n            std::make_pair(\"ccc\", std::vector<std::string>{ \"ccc\", \"ccc\" }),\n            std::make_pair(\"dddd\", std::vector<std::string>{ \"dddd\", \"dddd\" })\n        };\n        REQUIRE(lz::equal(grouper, expected, eq_pair{}));\n\n        vec = { \"a\", \"bb\", \"ccc\", \"ccc\", \"dddd\" };\n        auto grouper2 = lz::group_by(vec, [](const std::string& a, const std::string& b) { return a.length() == b.length(); });\n        expected = { std::make_pair(\"a\", std::vector<std::string>{ \"a\" }), std::make_pair(\"bb\", std::vector<std::string>{ \"bb\" }),\n                     std::make_pair(\"ccc\", std::vector<std::string>{ \"ccc\", \"ccc\" }),\n                     std::make_pair(\"dddd\", std::vector<std::string>{ \"dddd\" }) };\n        REQUIRE(lz::equal(grouper2, expected, eq_pair{}));\n    }\n\n    SUBCASE(\"Operator--\") {\n        std::vector<std::pair<std::string, std::vector<std::string>>> expected = {\n            std::make_pair(\"dddd\", std::vector<std::string>{ \"dddd\", \"dddd\" }),\n            std::make_pair(\"ccc\", std::vector<std::string>{ \"ccc\", \"ccc\" }),\n            std::make_pair(\"bb\", std::vector<std::string>{ \"bb\" }), std::make_pair(\"a\", std::vector<std::string>{ \"a\" })\n        };\n        REQUIRE(lz::equal(grouper | lz::reverse, expected, eq_pair{}));\n\n        vec = { \"a\", \"a\", \"bb\", \"ccc\", \"ccc\", \"dddd\", \"dddd\" };\n        auto grouper2 = lz::group_by(vec, [](const std::string& a, const std::string& b) { return a.length() == b.length(); });\n        expected = { std::make_pair(\"dddd\", std::vector<std::string>{ \"dddd\", \"dddd\" }),\n                     std::make_pair(\"ccc\", std::vector<std::string>{ \"ccc\", \"ccc\" }),\n                     std::make_pair(\"bb\", std::vector<std::string>{ \"bb\" }),\n                     std::make_pair(\"a\", std::vector<std::string>{ \"a\", \"a\" }) };\n        REQUIRE(lz::equal(grouper2 | lz::reverse, expected, eq_pair{}));\n    }\n}\n\nTEST_CASE(\"To containers group by\") {\n    std::vector<std::string> vec = { \"hello\", \"hellp\", \"i'm\", \"done\" };\n    auto grouper = vec | lz::group_by([](const std::string& a, const std::string& b) { return a.length() == b.length(); });\n    using value_type = lz::detail::val_iterable_t<decltype(grouper)>;\n    using pair_type = std::pair<std::string, std::vector<std::string>>;\n\n    SUBCASE(\"To array\") {\n        auto arr = grouper |\n                   lz::map([](const value_type& v) { return std::make_pair(v.first, v.second | lz::to<std::vector>()); }) |\n                   lz::to<std::array<pair_type, 4>>();\n\n        std::array<pair_type, 4> expected = {\n            std::make_pair(\"hello\", std::vector<std::string>{ \"hello\", \"hellp\" }),\n            std::make_pair(\"i'm\", std::vector<std::string>{ \"i'm\" }),\n            std::make_pair(\"done\", std::vector<std::string>{ \"done\" }),\n        };\n        REQUIRE(arr == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        auto vec2 = grouper |\n                    lz::map([](const value_type& v) { return std::make_pair(v.first, v.second | lz::to<std::vector>()); }) |\n                    lz::to<std::vector>();\n\n        std::vector<pair_type> expected = {\n            std::make_pair(\"hello\", std::vector<std::string>{ \"hello\", \"hellp\" }),\n            std::make_pair(\"i'm\", std::vector<std::string>{ \"i'm\" }),\n            std::make_pair(\"done\", std::vector<std::string>{ \"done\" }),\n        };\n        REQUIRE(vec2 == expected);\n    }\n\n    SUBCASE(\"To list\") {\n        auto lst = grouper |\n                   lz::map([](const value_type& v) { return std::make_pair(v.first, v.second | lz::to<std::vector>()); }) |\n                   lz::to<std::list>();\n\n        std::list<pair_type> expected = {\n            std::make_pair(\"hello\", std::vector<std::string>{ \"hello\", \"hellp\" }),\n            std::make_pair(\"i'm\", std::vector<std::string>{ \"i'm\" }),\n            std::make_pair(\"done\", std::vector<std::string>{ \"done\" }),\n        };\n        REQUIRE(lst == expected);\n    }\n\n    SUBCASE(\"To map\") {\n        auto map = grouper |\n                   lz::map([](const value_type& v) { return std::make_pair(v.first, v.second | lz::to<std::vector>()); }) |\n                   lz::to<std::map<std::string, std::vector<std::string>>>();\n\n        std::map<std::string, std::vector<std::string>> expected = {\n            { \"hello\", { \"hello\", \"hellp\" } },\n            { \"i'm\", { \"i'm\" } },\n            { \"done\", { \"done\" } },\n        };\n        REQUIRE(map == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto map = grouper |\n                   lz::map([](const value_type& v) { return std::make_pair(v.first, v.second | lz::to<std::vector>()); }) |\n                   lz::to<std::unordered_map<std::string, std::vector<std::string>>>();\n\n        std::unordered_map<std::string, std::vector<std::string>> expected = {\n            { \"hello\", { \"hello\", \"hellp\" } },\n            { \"i'm\", { \"i'm\" } },\n            { \"done\", { \"done\" } },\n        };\n        REQUIRE(map == expected);\n    }\n}\n"
  },
  {
    "path": "tests/inclusive_scan.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/generate.hpp>\n#include <Lz/inclusive_scan.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Inclusive scan with sentinels\") {\n    int x = 1;\n    auto generator = lz::generate([x]() { return x; }, 10);\n    lz::inclusive_scan_iterable<decltype(generator)> scan = lz::inclusive_scan(generator);\n    static_assert(!std::is_same<decltype(scan.begin()), decltype(scan.end())>::value, \"Should be sentinel\");\n    auto begin = scan.begin();\n    for (int i = 1; i < 10; ++i) {\n        REQUIRE(*begin == i);\n        ++begin;\n    }\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        std::vector<int> vec = { 1, 2, 3, 4 };\n        auto inclusive_scan = lz::inclusive_scan(vec);\n        auto common = make_sentinel_assign_op_tester(inclusive_scan);\n        auto expected = { 1, 3, 6, 10 };\n        REQUIRE(lz::equal(common, expected));\n        REQUIRE(lz::size(common) == lz::size(expected));\n    }\n}\n\nTEST_CASE(\"Empty or one element inclusive scan\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> empty;\n        auto scan = lz::inclusive_scan(empty);\n        REQUIRE(lz::empty(scan));\n    }\n\n    SUBCASE(\"One element\") {\n        std::vector<int> one_element = { 1 };\n        auto scan = lz::inclusive_scan(one_element);\n        REQUIRE_FALSE(lz::empty(scan));\n        REQUIRE(lz::has_one(scan));\n        REQUIRE_FALSE(lz::has_many(scan));\n    }\n}\n\nTEST_CASE(\"Inclusive scan changing and creating elements\") {\n    int arr[32];\n    std::iota(std::begin(arr), std::end(arr), 0);\n    auto scan = arr | lz::inclusive_scan;\n    REQUIRE(scan.size() == lz::size(arr));\n    auto begin = scan.begin();\n\n    std::ptrdiff_t sum = 0;\n    for (std::ptrdiff_t i = 0; i < std::distance(std::begin(arr), std::end(arr)); ++i) {\n        sum += i;\n        REQUIRE(*begin == sum);\n        ++begin;\n    }\n\n    REQUIRE(lz::distance(scan) == std::distance(std::begin(arr), std::end(arr)));\n\n    constexpr static int expected[] = { 0,   1,   3,   6,   10,  15,  21,  28,  36,  45,  55,  66,  78,  91,  105, 120,\n                                        136, 153, 171, 190, 210, 231, 253, 276, 300, 325, 351, 378, 406, 435, 465, 496 };\n    REQUIRE(lz::equal(expected, scan));\n}\n\nTEST_CASE(\"Inclusive scan splitter binary operations\") {\n    int arr[] = { 1, 2, 3, 4, 5 };\n    auto scan = lz::inclusive_scan(arr);\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 1, 3, 6, 10, 15 };\n        REQUIRE(lz::equal(scan, expected));\n    }\n\n    SUBCASE(\"Operator== & operator!=\") {\n        REQUIRE(scan.begin() != scan.end());\n        auto begin = scan.begin();\n        while (begin != scan.end()) {\n            ++begin;\n        }\n        REQUIRE(begin == scan.end());\n        begin = scan.begin();\n        ++begin;\n        REQUIRE(begin != scan.begin());\n        REQUIRE(begin != scan.end());\n    }\n}\n\nTEST_CASE(\"Inclusive scan splitter to containers\") {\n    int to_scan[] = { 2, 5, 6, 4, 87, 8, 45, 7 };\n    auto scanner = lz::inclusive_scan(to_scan);\n\n    SUBCASE(\"To array\") {\n        std::array<int, 8> expected = { 2, 7, 13, 17, 104, 112, 157, 164 };\n        auto actual = scanner | lz::to<std::array<int, expected.size()>>();\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        std::vector<int> expected = { 2, 7, 13, 17, 104, 112, 157, 164 };\n        auto actual = scanner | lz::to<std::vector>();\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        std::list<int> expected = { 2, 7, 13, 17, 104, 112, 157, 164 };\n        auto actual = scanner | lz::to<std::list<int>>();\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To map\") {\n        std::map<int, int> expected = { { 4, 2 },     { 14, 7 },    { 26, 13 },   { 34, 17 },\n                                        { 208, 104 }, { 224, 112 }, { 314, 157 }, { 328, 164 } };\n        auto actual = scanner | lz::map([](int i) { return std::make_pair(i + i, i); }) | lz::to<std::map<int, int>>();\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        std::unordered_map<int, int> expected = { { 4, 2 },     { 14, 7 },    { 26, 13 },   { 34, 17 },\n                                                  { 208, 104 }, { 224, 112 }, { 314, 157 }, { 328, 164 } };\n        auto actual = scanner | lz::map([](int i) { return std::make_pair(i + i, i); }) | lz::to<std::unordered_map<int, int>>();\n        REQUIRE(expected == actual);\n    }\n}\n"
  },
  {
    "path": "tests/init.cpp",
    "content": "#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN\n\n#include <doctest/doctest.h>\n"
  },
  {
    "path": "tests/interleave.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/interleave.hpp>\n#include <Lz/range.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward list\") {\n        std::forward_list<int> a = { 1, 3, 5 };\n        std::forward_list<int> b = { 2, 4, 6 };\n        auto interleaved = lz::interleave(a, b);\n        auto common = make_sentinel_assign_op_tester(interleaved);\n        auto expected = { 1, 2, 3, 4, 5, 6 };\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> a = { 1, 3, 5 };\n        std::vector<int> b = { 2, 4, 6 };\n        auto interleaved = lz::interleave(make_sized_bidi_sentinelled(a), make_sized_bidi_sentinelled(b));\n        auto common = make_sentinel_assign_op_tester(interleaved);\n        auto expected = { 1, 2, 3, 4, 5, 6 };\n        REQUIRE(lz::equal(common, expected));\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"random access\") {\n        auto a = lz::repeat(1, 3);\n        auto b = lz::repeat(2, 3);\n        auto interleaved = make_sentinel_assign_op_tester(lz::interleave(a, b));\n        auto expected = { 1, 2, 1, 2, 1, 2 };\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n}\n\nTEST_CASE(\"Interleaved with sentinels permutations\") {\n    auto str3 = lz::c_string(\"abc\");\n    auto str4 = lz::c_string(\"defg\");\n    const auto str5 = lz::c_string(\"hijkl\");\n\n    SUBCASE(\"With iterable that yields by value\") {\n        const auto range = lz::range('c');\n        auto interleaved = lz::interleave(str3, str4, range);\n        using t1 = decltype(*interleaved.begin());\n        static_assert(std::is_same<t1, char>::value, \"Should be char\");\n\n        auto interleaved2 = lz::interleave(str3, range, str4);\n        using t2 = decltype(*interleaved2.begin());\n        static_assert(std::is_same<t2, char>::value, \"Should be char\");\n    }\n\n    SUBCASE(\"Permutation 1: 3 vs 4 characters and 4 vs 3 characters\") {\n        auto interleaved = lz::interleave(str3, str4);\n        static_assert(!std::is_same<decltype(interleaved.begin()), decltype(interleaved.end())>::value, \"Must be sentinel\");\n\n        std::vector<char> expected = { 'a', 'd', 'b', 'e', 'c', 'f' };\n        REQUIRE(lz::equal(interleaved, expected));\n\n        expected = { 'd', 'a', 'e', 'b', 'f', 'c' };\n        interleaved = lz::interleave(str4, str3);\n        REQUIRE(lz::equal(interleaved, expected));\n    }\n\n    SUBCASE(\"Permutation 2: 4 vs 5 characters and 5 vs 4 characters\") {\n        auto interleaved1 = lz::interleave(str4, str5);\n        static_assert(!std::is_same<decltype(interleaved1.begin()), decltype(interleaved1.end())>::value, \"Must be sentinel\");\n\n        std::vector<char> expected = { 'd', 'h', 'e', 'i', 'f', 'j', 'g', 'k' };\n        REQUIRE(lz::equal(interleaved1, expected));\n\n        expected = { 'h', 'd', 'i', 'e', 'j', 'f', 'k', 'g' };\n        auto interleaved2 = lz::interleave(str5, str4);\n        REQUIRE(lz::equal(interleaved2, expected));\n    }\n\n    SUBCASE(\"Permutation 3: 5 vs 3 characters and 3 vs 5 characters\") {\n        auto interleaved1 = lz::interleave(str5, str3);\n        static_assert(!std::is_same<decltype(interleaved1.begin()), decltype(interleaved1.end())>::value, \"Must be sentinel\");\n\n        std::vector<char> expected = { 'h', 'a', 'i', 'b', 'j', 'c' };\n        REQUIRE(lz::equal(interleaved1, expected));\n\n        expected = { 'a', 'h', 'b', 'i', 'c', 'j' };\n        auto interleaved2 = lz::interleave(str3, str5);\n        REQUIRE(lz::equal(interleaved2, expected));\n    }\n\n    SUBCASE(\"Permutation 4: 3 vs 4 vs 5 characters and 5 vs 4 vs 3 characters\") {\n        auto interleaved1 = lz::interleave(str3, str4, str5);\n        static_assert(!std::is_same<decltype(interleaved1.begin()), decltype(interleaved1.end())>::value, \"Must be sentinel\");\n\n        std::vector<char> expected = { 'a', 'd', 'h', 'b', 'e', 'i', 'c', 'f', 'j' };\n        REQUIRE(lz::equal(interleaved1, expected));\n\n        expected = { 'h', 'd', 'a', 'i', 'e', 'b', 'j', 'f', 'c' };\n        auto interleaved2 = lz::interleave(str5, str4, str3);\n        REQUIRE(lz::equal(interleaved2, expected));\n    }\n\n    SUBCASE(\"Permutation 5: 4 vs 3 vs 5 characters and 5 vs 3 vs 4 characters\") {\n        auto interleaved1 = lz::interleave(str4, str3, str5);\n        static_assert(!std::is_same<decltype(interleaved1.begin()), decltype(interleaved1.end())>::value, \"Must be sentinel\");\n\n        std::vector<char> expected = { 'd', 'a', 'h', 'e', 'b', 'i', 'f', 'c', 'j' };\n        REQUIRE(lz::equal(interleaved1, expected));\n\n        expected = { 'h', 'a', 'd', 'i', 'b', 'e', 'j', 'c', 'f' };\n        auto interleaved2 = lz::interleave(str5, str3, str4);\n        REQUIRE(lz::equal(interleaved2, expected));\n    }\n\n    SUBCASE(\"Permutation 6: 3 vs 5 vs 4 characters and 4 vs 5 vs 3 characters\") {\n        auto interleaved1 = lz::interleave(str3, str5, str4);\n        static_assert(!std::is_same<decltype(interleaved1.begin()), decltype(interleaved1.end())>::value, \"Must be sentinel\");\n\n        std::vector<char> expected = { 'a', 'h', 'd', 'b', 'i', 'e', 'c', 'j', 'f' };\n        REQUIRE(lz::equal(interleaved1, expected));\n\n        expected = { 'd', 'h', 'a', 'e', 'i', 'b', 'f', 'j', 'c' };\n        auto interleaved2 = lz::interleave(str4, str5, str3);\n        REQUIRE(lz::equal(interleaved2, expected));\n    }\n}\n\nTEST_CASE(\"Empty or one element\") {\n    SUBCASE(\"All empty\") {\n        std::vector<int> a, b, c;\n        auto interleaved = lz::interleave(a, b, c);\n        using t = decltype(*interleaved.begin());\n        static_assert(std::is_same<t, int&>::value, \"Should be int&\");\n\n        REQUIRE(interleaved.size() == 0);\n        REQUIRE(lz::empty(interleaved));\n        REQUIRE_FALSE(lz::has_one(interleaved));\n        REQUIRE_FALSE(lz::has_many(interleaved));\n    }\n\n    SUBCASE(\"One empty first\") {\n        std::vector<int> a = { 1 }, b, c;\n        auto interleaved = lz::interleave(a, b, c);\n        REQUIRE(interleaved.size() == 0);\n        REQUIRE(lz::empty(interleaved));\n        REQUIRE_FALSE(lz::has_one(interleaved));\n        REQUIRE_FALSE(lz::has_many(interleaved));\n    }\n\n    SUBCASE(\"One empty second\") {\n        std::vector<int> a, b = { 1 }, c;\n        auto interleaved = lz::interleave(a, b, c);\n        REQUIRE(interleaved.size() == 0);\n        REQUIRE(lz::empty(interleaved));\n        REQUIRE_FALSE(lz::has_one(interleaved));\n        REQUIRE_FALSE(lz::has_many(interleaved));\n    }\n\n    SUBCASE(\"One empty third\") {\n        std::vector<int> a, b, c = { 1 };\n        auto interleaved = lz::interleave(a, b, c);\n        REQUIRE(interleaved.size() == 0);\n        REQUIRE(lz::empty(interleaved));\n        REQUIRE_FALSE(lz::has_one(interleaved));\n        REQUIRE_FALSE(lz::has_many(interleaved));\n    }\n\n    SUBCASE(\"One empty third\") {\n        std::vector<int> a, b, c = { 1 };\n        auto interleaved = lz::interleave(a, b, c);\n        REQUIRE(interleaved.size() == 0);\n        REQUIRE(lz::empty(interleaved));\n        REQUIRE_FALSE(lz::has_one(interleaved));\n        REQUIRE_FALSE(lz::has_many(interleaved));\n    }\n\n    SUBCASE(\"First empty, second and third one\") {\n        std::vector<int> a, b = { 1 }, c = { 2 };\n        auto interleaved = lz::interleave(a, b, c);\n        REQUIRE(interleaved.size() == 0);\n        REQUIRE(lz::empty(interleaved));\n        REQUIRE_FALSE(lz::has_one(interleaved));\n        REQUIRE_FALSE(lz::has_many(interleaved));\n    }\n\n    SUBCASE(\"First one, second empty and third one\") {\n        std::vector<int> a = { 1 }, b, c = { 2 };\n        auto interleaved = lz::interleave(a, b, c);\n        REQUIRE(interleaved.size() == 0);\n        REQUIRE(lz::empty(interleaved));\n        REQUIRE_FALSE(lz::has_one(interleaved));\n        REQUIRE_FALSE(lz::has_many(interleaved));\n    }\n\n    SUBCASE(\"First one, second one and third empty\") {\n        std::vector<int> a = { 1 }, b = { 2 }, c;\n        auto interleaved = lz::interleave(a, b, c);\n        REQUIRE(interleaved.size() == 0);\n        REQUIRE(lz::empty(interleaved));\n        REQUIRE_FALSE(lz::has_one(interleaved));\n        REQUIRE_FALSE(lz::has_many(interleaved));\n    }\n\n    SUBCASE(\"All one\") {\n        std::vector<int> a = { 1 }, b = { 2 }, c = { 3 };\n        auto interleaved = lz::interleave(a, b, c);\n        REQUIRE(interleaved.size() == 3);\n        REQUIRE_FALSE(lz::empty(interleaved));\n        REQUIRE_FALSE(lz::has_one(interleaved));\n        REQUIRE(lz::has_many(interleaved));\n    }\n}\n\nTEST_CASE(\"Non sentinelled, forward, backward\") {\n    std::vector<int> a = { 1, 2, 3 }, b = { 4, 5, 6, 7 }, c = { 8, 9, 10, 11, 12 };\n\n    SUBCASE(\"Operator++/--, permutation 1: 3 vs 4 vs 5 items vs 4 vs 3 vs 5 items\") {\n        std::vector<int> expected = { 1, 4, 8, 2, 5, 9, 3, 6, 10 };\n        auto interleaved = lz::interleave(a, b, c);\n\n        static_assert(std::is_same<decltype(interleaved.begin()), decltype(interleaved.end())>::value, \"Must not be sentinel\");\n\n        REQUIRE(lz::equal(interleaved, expected));\n        REQUIRE(lz::equal(interleaved | lz::reverse, expected | lz::reverse));\n\n        expected = { 4, 1, 8, 5, 2, 9, 6, 3, 10 };\n        interleaved = lz::interleave(b, a, c);\n        REQUIRE(lz::equal(interleaved, expected));\n        REQUIRE(lz::equal(interleaved | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"Operator++/--, permutation 2: 4 vs 5 vs 3 items vs 5 vs 4 vs 3 items\") {\n        std::vector<int> expected = { 4, 8, 1, 5, 9, 2, 6, 10, 3 };\n        auto interleaved = lz::interleave(b, c, a);\n        REQUIRE(lz::equal(interleaved, expected));\n        REQUIRE(lz::equal(interleaved | lz::reverse, expected | lz::reverse));\n\n        expected = { 8, 4, 1, 9, 5, 2, 10, 6, 3 };\n        interleaved = lz::interleave(c, b, a);\n        REQUIRE(lz::equal(interleaved, expected));\n        REQUIRE(lz::equal(interleaved | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"Operator++/--, permutation 3: 5 vs 3 vs 4 items vs 3 vs 5 vs 4 items\") {\n        std::vector<int> expected = { 8, 1, 4, 9, 2, 5, 10, 3, 6 };\n        auto interleaved = lz::interleave(c, a, b);\n        REQUIRE(lz::equal(interleaved, expected));\n        REQUIRE(lz::equal(interleaved | lz::reverse, expected | lz::reverse));\n\n        expected = { 1, 8, 4, 2, 9, 5, 3, 10, 6 };\n        interleaved = lz::interleave(a, c, b);\n        REQUIRE(lz::equal(interleaved, expected));\n        REQUIRE(lz::equal(interleaved | lz::reverse, expected | lz::reverse));\n    }\n}\n\nTEST_CASE(\"Non sentinelled, operator+/-. length 4, 5, 6\") {\n    std::vector<int> a = { 1, 2, 3, 4 }, b = { 5, 6, 7, 8, 9 }, c = { 10, 11, 12, 13, 14, 15 };\n\n    SUBCASE(\"Permutation 1: a, b, c\") {\n        auto interleaved = lz::interleave(a, b, c);\n        static_assert(lz::detail::is_ra<decltype(interleaved.begin())>::value, \"Must be random access\");\n        auto expected = { 1, 5, 10, 2, 6, 11, 3, 7, 12, 4, 8, 13 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 2: b, a, c\") {\n        auto interleaved = lz::interleave(b, a, c);\n        auto expected = { 5, 1, 10, 6, 2, 11, 7, 3, 12, 8, 4, 13 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 3: b, c, a\") {\n        auto interleaved = lz::interleave(b, c, a);\n        std::vector<int> expected = { 5, 10, 1, 6, 11, 2, 7, 12, 3, 8, 13, 4 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 4: c, b, a\") {\n        auto interleaved = lz::interleave(c, b, a);\n        auto expected = { 10, 5, 1, 11, 6, 2, 12, 7, 3, 13, 8, 4 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 5: c, a, b\") {\n        auto interleaved = lz::interleave(c, a, b);\n        std::vector<int> expected = { 10, 1, 5, 11, 2, 6, 12, 3, 7, 13, 4, 8 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 6: a, c, b\") {\n        auto interleaved = lz::interleave(a, c, b);\n        auto expected = { 1, 10, 5, 2, 11, 6, 3, 12, 7, 4, 13, 8 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n}\n\nTEST_CASE(\"Non sentinelled, operator+/-. length 4, 5, 6, 7\") {\n    std::vector<int> a = { 1, 2, 3, 4 }, b = { 5, 6, 7, 8, 9 }, c = { 10, 11, 12, 13, 14, 15 },\n                     d = { 16, 17, 18, 19, 20, 21, 22 };\n\n    SUBCASE(\"Permutation 1: a, b, c, d\") {\n        auto interleaved = lz::interleave(a, b, c, d);\n        std::vector<int> expected = { 1, 5, 10, 16, 2, 6, 11, 17, 3, 7, 12, 18, 4, 8, 13, 19 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 2: b, c, d, a\") {\n        auto interleaved = lz::interleave(b, c, d, a);\n        std::vector<int> expected = { 5, 10, 16, 1, 6, 11, 17, 2, 7, 12, 18, 3, 8, 13, 19, 4 };\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 3: c, d, a, b\") {\n        auto interleaved = lz::interleave(c, d, a, b);\n        std::vector<int> expected = { 10, 16, 1, 5, 11, 17, 2, 6, 12, 18, 3, 7, 13, 19, 4, 8 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 4: d, a, b, c\") {\n        auto interleaved = lz::interleave(d, a, b, c);\n        std::vector<int> expected = { 16, 1, 5, 10, 17, 2, 6, 11, 18, 3, 7, 12, 19, 4, 8, 13 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 5: a, d, b, c\") {\n        auto interleaved = lz::interleave(a, d, b, c);\n        std::vector<int> expected = { 1, 16, 5, 10, 2, 17, 6, 11, 3, 18, 7, 12, 4, 19, 8, 13 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 6: d, c, b, a\") {\n        auto interleaved = lz::interleave(d, c, b, a);\n        std::vector<int> expected = { 16, 10, 5, 1, 17, 11, 6, 2, 18, 12, 7, 3, 19, 13, 8, 4 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 7: c, b, a, d\") {\n        auto interleaved = lz::interleave(c, b, a, d);\n        std::vector<int> expected = { 10, 5, 1, 16, 11, 6, 2, 17, 12, 7, 3, 18, 13, 8, 4, 19 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 8: b, a, d, c\") {\n        auto interleaved = lz::interleave(b, a, d, c);\n        std::vector<int> expected = { 5, 1, 16, 10, 6, 2, 17, 11, 7, 3, 18, 12, 8, 4, 19, 13 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 9: a, c, d, b\") {\n        auto interleaved = lz::interleave(a, c, d, b);\n        std::vector<int> expected = { 1, 10, 16, 5, 2, 11, 17, 6, 3, 12, 18, 7, 4, 13, 19, 8 };\n        REQUIRE(lz::size(interleaved) == lz::size(expected));\n        test_procs::test_operator_plus(interleaved, expected);\n        test_procs::test_operator_minus(interleaved);\n    }\n}\n\nTEST_CASE(\"Sentinelled, operator+/-. length 4, 5, 6, 7\") {\n    auto a = lz::repeat(0, 4), b = lz::repeat(0, 5), c = lz::repeat(0, 6), d = lz::repeat(0, 7);\n\n    SUBCASE(\"Permutation 1: a, b, c, d\") {\n        auto interleaved = lz::interleave(a, b, c, d);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 2: b, c, d, a\") {\n        auto interleaved = lz::interleave(b, c, d, a);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 3: c, d, a, b\") {\n        auto interleaved = lz::interleave(c, d, a, b);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 4: d, a, b, c\") {\n        auto interleaved = lz::interleave(d, a, b, c);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 5: a, d, b, c\") {\n        auto interleaved = lz::interleave(a, d, b, c);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 6: d, c, b, a\") {\n        auto interleaved = lz::interleave(d, c, b, a);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 7: c, b, a, d\") {\n        auto interleaved = lz::interleave(c, b, a, d);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 8: b, a, d, c\") {\n        auto interleaved = lz::interleave(b, a, d, c);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 9: a, c, d, b\") {\n        auto interleaved = lz::interleave(a, c, d, b);\n        test_procs::test_operator_minus(interleaved);\n    }\n}\n\nTEST_CASE(\"Sentinelled, operator+/-. length 4, 5, 6\") {\n    auto a = lz::repeat(0, 4), b = lz::repeat(0, 5), c = lz::repeat(0, 6);\n\n    SUBCASE(\"Permutation 1: a, b, c\") {\n        auto interleaved = lz::interleave(a, b, c);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 2: b, a, c\") {\n        auto interleaved = lz::interleave(b, a, c);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 3: b, c, a\") {\n        auto interleaved = lz::interleave(b, c, a);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 4: c, b, a\") {\n        auto interleaved = lz::interleave(c, b, a);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 5: c, a, b\") {\n        auto interleaved = lz::interleave(c, a, b);\n        test_procs::test_operator_minus(interleaved);\n    }\n\n    SUBCASE(\"Permutation 6: a, c, b\") {\n        auto interleaved = lz::interleave(a, c, b);\n        test_procs::test_operator_minus(interleaved);\n    }\n}\n"
  },
  {
    "path": "tests/intersection.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/intersection.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Intersection tests with sentinels\") {\n    auto str = lz::c_string(\"aaaabbcccddee\");\n    auto str2 = lz::c_string(\"aabccce\");\n    lz::intersection_iterable<decltype(str), decltype(str2)> intersect = lz::intersection(str, str2);\n    static_assert(!std::is_same<decltype(intersect.begin()), decltype(intersect.end())>::value, \"Must be sentinel\");\n    REQUIRE((intersect | lz::to<std::string>()) == \"aabccce\");\n\n    std::swap(str, str2);\n    intersect = lz::intersection(str, str2, LZ_BIN_OP(less, char){});\n    REQUIRE((intersect | lz::to<std::string>()) == \"aabccce\");\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> a = { 1, 2, 3, 4, 5 };\n        std::forward_list<int> b = { 2, 4, 6 };\n        auto intersected = lz::intersection(a, b);\n        auto common = make_sentinel_assign_op_tester(intersected);\n        auto expected = { 2, 4 };\n        REQUIRE(lz::equal(common, expected));\n\n        intersected = lz::intersection(b, a);\n        common = make_sentinel_assign_op_tester(intersected);\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> a = { 1, 2, 3, 4, 5 };\n        std::vector<int> b = { 2, 4, 6 };\n        auto intersected = lz::intersection(make_sized_bidi_sentinelled(a), make_sized_bidi_sentinelled(b));\n        auto common = make_sentinel_assign_op_tester(intersected);\n        auto expected = { 2, 4 };\n        REQUIRE(lz::equal(common, expected));\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n    }\n}\n\nTEST_CASE(\"Empty or one element intersection\") {\n    SUBCASE(\"All empty\") {\n        std::string a;\n        std::string b;\n        auto intersect = a | lz::intersection(b);\n        REQUIRE(lz::empty(intersect));\n        REQUIRE_FALSE(lz::has_one(intersect));\n        REQUIRE_FALSE(lz::has_many(intersect));\n    }\n\n    SUBCASE(\"One element 1\") {\n        std::string a = \"h\";\n        std::string b;\n        auto intersect = a | lz::intersection(b, LZ_BIN_OP(less, char){});\n        REQUIRE(lz::empty(intersect));\n        REQUIRE_FALSE(lz::has_one(intersect));\n        REQUIRE_FALSE(lz::has_many(intersect));\n    }\n\n    SUBCASE(\"One element 2\") {\n        std::string a;\n        std::string b = \"w\";\n        auto intersect = lz::intersection(a, b);\n        REQUIRE(lz::empty(intersect));\n        REQUIRE_FALSE(lz::has_one(intersect));\n        REQUIRE_FALSE(lz::has_many(intersect));\n    }\n\n    SUBCASE(\"One element both 1\") {\n        std::string a = \"h\";\n        std::string b = \"h\";\n        auto intersect = lz::intersection(a, b);\n        REQUIRE_FALSE(lz::empty(intersect));\n        REQUIRE(lz::has_one(intersect));\n        REQUIRE_FALSE(lz::has_many(intersect));\n    }\n\n    SUBCASE(\"One element both 2\") {\n        std::string a = \"h\";\n        std::string b = \"w\";\n        auto intersect = lz::intersection(a, b);\n        REQUIRE(lz::empty(intersect));\n        REQUIRE_FALSE(lz::has_one(intersect));\n        REQUIRE_FALSE(lz::has_many(intersect));\n    }\n}\n\nTEST_CASE(\"Intersection binary operations\") {\n    SUBCASE(\"Operator++/-- 1\") {\n        std::string a = \"aaaabbcccddee\";\n        std::string b = \"aabccce\";\n        auto intersect = lz::intersection(a, b);\n        std::string expected = \"aabccce\";\n        REQUIRE(lz::equal(intersect, expected));\n        REQUIRE(lz::equal(intersect | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"Operator++/-- 2\") {\n        std::string a = \"aaaabbcccddee\";\n        std::string b = \"aabccce\";\n        auto intersect = lz::intersection(b, a);\n        std::string expected = \"aabccce\";\n        REQUIRE(lz::equal(intersect, expected));\n        REQUIRE(lz::equal(intersect | lz::reverse, expected | lz::reverse));\n    }\n}\n\nTEST_CASE(\"To container intersection\") {\n    std::string a = \"aaaabbcccddee\";\n    std::string b = \"aabccce\";\n    auto intersect = lz::intersection(a, b);\n\n    SUBCASE(\"To array\") {\n        auto intersected = intersect | lz::to<std::array<char, 7>>();\n        REQUIRE(intersected == std::array<char, 7>{ 'a', 'a', 'b', 'c', 'c', 'c', 'e' });\n    }\n\n    SUBCASE(\"To vector\") {\n        auto intersected = intersect | lz::to<std::vector<char>>();\n        REQUIRE(intersected == std::vector<char>{ 'a', 'a', 'b', 'c', 'c', 'c', 'e' });\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        auto intersected = intersect | lz::to<std::list<char>>();\n        REQUIRE(intersected == std::list<char>{ 'a', 'a', 'b', 'c', 'c', 'c', 'e' });\n    }\n\n    SUBCASE(\"To map\") {\n        auto intersected =\n            intersect | lz::map([](const char i) { return std::make_pair(i, i); }) | lz::to<std::map<char, char>>();\n        std::map<char, char> expected = {\n            std::make_pair('a', 'a'),\n            std::make_pair('b', 'b'),\n            std::make_pair('c', 'c'),\n            std::make_pair('e', 'e'),\n        };\n        REQUIRE(intersected == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto intersected =\n            intersect | lz::map([](const char i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<char, char>>();\n        std::unordered_map<char, char> expected = {\n            std::make_pair('a', 'a'),\n            std::make_pair('b', 'b'),\n            std::make_pair('c', 'c'),\n            std::make_pair('e', 'e'),\n        };\n        REQUIRE(intersected == expected);\n    }\n}\n"
  },
  {
    "path": "tests/iter_tools.cpp",
    "content": "#include <Lz/algorithm/equal.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/iter_tools.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Lines\") {\n    const lz::string_view expected[] = { \"hello world\", \"this is a message\", \"testing\" };\n    lz::lines_iterable_sv<> actual = lz::lines(lz::string_view(\"hello world\\nthis is a message\\ntesting\"));\n    REQUIRE(lz::equal(actual, expected));\n\n    auto actual2 = lz::lines(lz::c_string(\"hello world\\nthis is a message\\ntesting\"));\n    REQUIRE(lz::equal(actual2, expected));\n    actual2 = lz::c_string(\"hello world\\nthis is a message\\ntesting\") | lz::lines;\n    REQUIRE(lz::equal(actual2, expected));\n\n    std::string to_split = \"hello world\\nthis is a message\\ntesting\";\n    lz::lines_iterable<std::string> actual3 = lz::lines(to_split);\n    REQUIRE(lz::equal(actual3, expected));\n    actual3 = to_split | lz::lines;\n    REQUIRE(lz::equal(actual3, expected));\n}\n\nTEST_CASE(\"As\") {\n    SUBCASE(\"Without sentinel\") {\n        const lz::basic_string_view<char> actual = \"hello world\";\n        const lz::basic_string_view<unsigned char> expected = reinterpret_cast<const unsigned char*>(\"hello world\");\n#ifdef LZ_HAS_CXX_11\n        REQUIRE(lz::equal(actual | lz::as<unsigned char>{}, expected));\n        REQUIRE(lz::equal(lz::as<unsigned char>{}(actual), expected));\n#else\n        REQUIRE(lz::equal(lz::as<unsigned char>(actual), expected));\n        REQUIRE(lz::equal(actual | lz::as<unsigned char>, expected));\n#endif\n    }\n\n    SUBCASE(\"With sentinel\") {\n        const auto actual = lz::c_string(\"hello world\");\n        const auto expected = lz::c_string(reinterpret_cast<const unsigned char*>(\"hello world\"));\n#ifdef LZ_HAS_CXX_11\n        REQUIRE(lz::equal(lz::as<unsigned char>{}(actual), expected));\n        REQUIRE(lz::equal(actual | lz::as<unsigned char>{}, expected));\n#else\n        REQUIRE(lz::equal(lz::as<unsigned char>(actual), expected));\n        REQUIRE(lz::equal(actual | lz::as<unsigned char>, expected));\n#endif\n    }\n}\n\nTEST_CASE(\"Zip with\") {\n    SUBCASE(\"Without sentinel\") {\n        const std::vector<int> v1 = { 1, 2, 3, 4, 5 };\n        const std::vector<int> v2 = { 6, 7, 8, 9, 10 };\n        const std::vector<int> v3 = { 11, 12, 13, 14, 15 };\n        constexpr int expected[] = { 1 + 6 + 11, 2 + 7 + 12, 3 + 8 + 13, 4 + 9 + 14, 5 + 10 + 15 };\n        using v = const std::vector<int>;\n        std::function<int(int, int, int)> fn = [](int a, int b, int c) {\n            return a + b + c;\n        };\n        lz::zip_with_iterable<decltype(fn), v, v, v> actual = lz::zip_with(std::move(fn), v1, v2, v3);\n        REQUIRE(lz::equal(actual, expected));\n    }\n\n    SUBCASE(\"With sentinel\") {\n        const auto v1 = lz::c_string(\"hello\");\n        const auto v2 = lz::c_string(\"world\");\n        const auto v3 = lz::c_string(\"12345\");\n        constexpr int expected[] = { 'h' + 'w' + '1', 'e' + 'o' + '2', 'l' + 'r' + '3', 'l' + 'l' + '4', 'o' + 'd' + '5' };\n        auto actual = lz::zip_with([](int a, int b, int c) { return a + b + c; }, v1, v2, v3);\n        REQUIRE(lz::equal(actual, expected));\n    }\n}\n\nTEST_CASE(\"Unzip with\") {\n    const std::vector<std::tuple<int, int>> zipped = { std::make_tuple(1, 6), std::make_tuple(2, 7), std::make_tuple(3, 8),\n                                                       std::make_tuple(4, 9), std::make_tuple(5, 10) };\n    std::function<int(int, int)> unzip_fn = [](int a, int b) {\n        return a + b;\n    };\n    lz::unzip_with_iterable<decltype(zipped), std::function<int(int, int)>> actual = zipped | lz::unzip_with(std::move(unzip_fn));\n    const std::vector<int> expected = { 7, 9, 11, 13, 15 };\n    REQUIRE(lz::equal(actual, expected));\n}\n\nTEST_CASE(\"Keys & values\") {\n    std::map<int, std::string> m = { { 1, \"hello\" }, { 2, \"world\" }, { 3, \"!\" } };\n    const std::vector<int> expected_keys = { 1, 2, 3 };\n    const std::vector<std::string> expected_values = { \"hello\", \"world\", \"!\" };\n\n    SUBCASE(\"Key & value\") {\n        lz::keys_iterable<decltype(m)> keys = lz::keys(m);\n        lz::values_iterable<decltype(m)> values = lz::values(m);\n        REQUIRE(lz::equal(keys, expected_keys));\n        REQUIRE(lz::equal(values, expected_values));\n        keys = m | lz::keys;\n        values = m | lz::values;\n        REQUIRE(lz::equal(keys, expected_keys));\n        REQUIRE(lz::equal(values, expected_values));\n    }\n\n    SUBCASE(\"Get nth\") {\n        const std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n        const std::vector<int> expected = { 3, 6, 9 };\n#ifdef LZ_HAS_CXX_11\n        lz::get_nth_iterable<decltype(three_tuple_vec), 2> actual = lz::get_nth<2>{}(three_tuple_vec);\n        REQUIRE(lz::equal(actual, expected));\n        actual = three_tuple_vec | lz::get_nth<2>{};\n        REQUIRE(lz::equal(actual, expected));\n#else\n        lz::get_nth_iterable<decltype(three_tuple_vec), 2> actual = lz::get_nth<2>(three_tuple_vec);\n        REQUIRE(lz::equal(actual, expected));\n        actual = three_tuple_vec | lz::get_nth<2>;\n        REQUIRE(lz::equal(actual, expected));\n#endif\n    }\n\n    SUBCASE(\"Get nths\") {\n        const std::vector<std::tuple<int, int, int>> three_tuple_vec = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };\n        const std::vector<std::tuple<int, int>> expected = { { 1, 3 }, { 4, 6 }, { 7, 9 } };\n#ifdef LZ_HAS_CXX_11\n        lz::get_nths_iterable<decltype(three_tuple_vec), 0, 2> actual = lz::get_nths<0, 2>{}(three_tuple_vec);\n#else\n        lz::get_nths_iterable<decltype(three_tuple_vec), 0, 2> actual = lz::get_nths<0, 2>(three_tuple_vec);\n#endif\n        REQUIRE(lz::equal(actual, expected));\n    }\n}\n\nTEST_CASE(\"Filtermap\") {\n    SUBCASE(\"With sentinels\") {\n        std::function<bool(char)> filter_fun = [](char c) {\n            return c == 'o' || c == 'e';\n        };\n        std::function<char(char)> map_fun = [](char c) {\n            return c;\n        };\n\n        const auto actual = lz::c_string(\"hello world\");\n        const auto expected = lz::c_string(\"eoo\");\n        lz::filter_map_iterable<decltype(actual), decltype(filter_fun), decltype(map_fun)> actual_filter_map =\n            lz::filter_map(actual, filter_fun, map_fun);\n        REQUIRE(lz::equal(actual_filter_map, expected));\n        actual_filter_map = actual | lz::filter_map(std::move(filter_fun), std::move(map_fun));\n        REQUIRE(lz::equal(actual_filter_map, expected));\n    }\n\n    SUBCASE(\"Without sentinels\") {\n        std::function<bool(int)> filter_fun = [](int i) {\n            return i % 2 == 0;\n        };\n        std::function<int(int)> map_fun = [](int i) {\n            return i;\n        };\n\n        const std::vector<int> actual = { 1, 2, 3, 4, 5 };\n        const std::vector<int> expected = { 2, 4 };\n        auto actual_filter_map = lz::filter_map(actual, filter_fun, map_fun);\n        REQUIRE(lz::equal(actual_filter_map, expected));\n        actual_filter_map = actual | lz::filter_map(std::move(filter_fun), std::move(map_fun));\n        REQUIRE(lz::equal(actual_filter_map, expected));\n    }\n}\n\nTEST_CASE(\"Select\") {\n    std::array<int, 6> to_select = { 1, 2, 3, 4, 5, 6 };\n    const std::array<bool, to_select.size()> selector = { true, false, true, false, true, false };\n    const std::vector<int> expected = { 1, 3, 5 };\n    lz::select_iterable<decltype(to_select), decltype(selector)> actual = lz::select(to_select, selector);\n    REQUIRE(lz::equal(actual, expected));\n    actual = to_select | lz::select(selector);\n    REQUIRE(lz::equal(actual, expected));\n}\n\nTEST_CASE(\"Trim variants\") {\n    SUBCASE(\"Drop back while\") {\n        std::function<bool(int)> pred = [](int i) {\n            return i > 3;\n        };\n        const std::vector<int> actual = { 1, 2, 3, 4, 5 };\n        const std::vector<int> expected = { 1, 2, 3 };\n        lz::drop_back_iterable<decltype(actual), decltype(pred)> actual_trim_back = lz::drop_back_while(actual, pred);\n        REQUIRE(lz::equal(actual_trim_back, expected));\n        actual_trim_back = actual | lz::drop_back_while(std::move(pred));\n        REQUIRE(lz::equal(actual_trim_back, expected));\n    }\n\n    SUBCASE(\"Trim vec\") {\n        const std::vector<int> actual = { 1, 2, 3, 4, 5 };\n        const std::vector<int> expected = { 3, 4 };\n        std::function<bool(int)> first_pred = [](int i) {\n            return i < 3;\n        };\n        std::function<bool(int)> last_pred = [](int i) {\n            return i > 4;\n        };\n        lz::trim_iterable<decltype(actual)> actual_trim = lz::trim(actual, std::move(first_pred), std::move(last_pred));\n        REQUIRE(lz::equal(actual_trim, expected));\n    }\n\n    SUBCASE(\"Trim string\") {\n        const std::string actual = \"   hello world   \";\n        const std::string expected = \"hello world\";\n        lz::trim_string_iterable<const std::string> actual_trim = lz::trim(actual);\n        REQUIRE(lz::equal(actual_trim, expected));\n        actual_trim = actual | lz::trim;\n        REQUIRE(lz::equal(actual_trim, expected));\n\n        const lz::string_view actual_view = \"   hello world   \";\n        const lz::string_view expected_view = \"hello world\";\n        auto actual_trim_view = lz::trim(actual_view);\n        REQUIRE(lz::equal(actual_trim_view, expected_view));\n        actual_trim_view = actual_view | lz::trim;\n        REQUIRE(lz::equal(actual_trim_view, expected_view));\n    }\n}\n\nTEST_CASE(\"iter_decay\") {\n    std::vector<int> v1 = { 1, 2, 3, 4, 5 };\n    std::vector<int> v2 = { 6, 7, 8, 9, 10 };\n    auto f1 = lz::filter(v1, [](int i) { return i % 2 == 0; });\n    auto f2 = lz::filter(v2, [](int i) { return i % 2 == 0; });\n\n#ifdef LZ_HAS_CXX_11\n    auto zipped = lz::zip(f1 | lz::iter_decay(std::forward_iterator_tag{}), f2);\n#else\n    auto zipped = lz::zip(f1 | lz::iter_decay(std::forward_iterator_tag{}), f2);\n#endif\n\n    static_assert(std::is_same<lz::detail::iter_cat_iterable_t<decltype(zipped)>, std::forward_iterator_tag>::value, \"must be forward\");\n    auto expeted = { std::make_tuple(2, 6), std::make_tuple(4, 8) };\n    REQUIRE(lz::equal(zipped, expeted));\n}\n\nTEST_CASE(\"Pad\") {\n    std::vector<int> v1 = { 1, 2, 3 };\n    int value = 0;\n    std::ptrdiff_t amount = 3;\n    auto padded = lz::pad(v1, std::ref(value), amount);\n    static_assert(std::is_same<decltype(*padded.begin()), std::reference_wrapper<int>>::value, \"\");\n    std::vector<int> expected = { 1, 2, 3, 0, 0, 0 };\n\n    REQUIRE(lz::ssize(padded) == amount + lz::ssize(v1));\n    REQUIRE(lz::equal(padded, expected));\n\n    padded = v1 | lz::pad(std::ref(value), amount);\n    REQUIRE(lz::ssize(padded) == amount + lz::ssize(v1));\n    REQUIRE(lz::equal(padded, expected));\n\n    SUBCASE(\"reference types\") {\n        static_assert(std::is_same<lz::detail::common_reference_t<std::reference_wrapper<int>, std::reference_wrapper<int>>,\n                                   std::reference_wrapper<int>>::value,\n                      \"\");\n        static_assert(std::is_same<lz::detail::common_reference_t<std::reference_wrapper<const int>, std::reference_wrapper<int>>,\n                                   std::reference_wrapper<const int>>::value,\n                      \"\");\n        static_assert(std::is_same<lz::detail::common_reference_t<std::reference_wrapper<int>, std::reference_wrapper<const int>>,\n                                   std::reference_wrapper<const int>>::value,\n                      \"\");\n\n        static_assert(std::is_same<lz::detail::common_reference_t<std::reference_wrapper<int>, int>, int>::value, \"\");\n        static_assert(std::is_same<lz::detail::common_reference_t<int, std::reference_wrapper<int>>, int>::value, \"\");\n\n        static_assert(std::is_same<lz::detail::common_reference_t<std::reference_wrapper<int>, const int>, int>::value, \"\");\n        static_assert(std::is_same<lz::detail::common_reference_t<const int, std::reference_wrapper<int>>, int>::value, \"\");\n\n        static_assert(std::is_same<lz::detail::common_reference_t<std::reference_wrapper<const int>, int>, int>::value, \"\");\n        static_assert(std::is_same<lz::detail::common_reference_t<int, std::reference_wrapper<const int>>, int>::value, \"\");\n\n        static_assert(std::is_same<lz::detail::common_reference_t<int, int>, int>::value, \"\");\n\n        static_assert(\n            std::is_same<lz::detail::common_reference_t<std::reference_wrapper<int>, int&>, std::reference_wrapper<int>>::value,\n            \"\");\n        static_assert(\n            std::is_same<lz::detail::common_reference_t<int&, std::reference_wrapper<int>>, std::reference_wrapper<int>>::value,\n            \"\");\n\n        static_assert(std::is_same<lz::detail::common_reference_t<std::reference_wrapper<const int>, const int&>,\n                                   std::reference_wrapper<const int>>::value,\n                      \"\");\n        static_assert(std::is_same<lz::detail::common_reference_t<const int&, std::reference_wrapper<const int>>,\n                                   std::reference_wrapper<const int>>::value,\n                      \"\");\n\n        static_assert(std::is_same<lz::detail::common_reference_t<std::reference_wrapper<int>, const int&>,\n                                   std::reference_wrapper<const int>>::value,\n                      \"\");\n        static_assert(std::is_same<lz::detail::common_reference_t<const int&, std::reference_wrapper<int>>,\n                                   std::reference_wrapper<const int>>::value,\n                      \"\");\n\n        static_assert(std::is_same<lz::detail::common_reference_t<std::reference_wrapper<const int>, int&>,\n                                   std::reference_wrapper<const int>>::value,\n                      \"\");\n        static_assert(std::is_same<lz::detail::common_reference_t<int&, std::reference_wrapper<const int>>,\n                                   std::reference_wrapper<const int>>::value,\n                      \"\");\n\n        //\n\n        static_assert(\n            std::is_same<lz::detail::common_reference_t<std::reference_wrapper<int>&, int&>, std::reference_wrapper<int>>::value,\n            \"\");\n\n        static_assert(std::is_same<lz::detail::common_reference_t<const std::reference_wrapper<int>&, int&>,\n                                   std::reference_wrapper<int>>::value,\n                      \"\");\n        static_assert(std::is_same<lz::detail::common_reference_t<const std::reference_wrapper<int>&, const int&>,\n                                   std::reference_wrapper<const int>>::value,\n                      \"\");\n    }\n}\n"
  },
  {
    "path": "tests/iterator.cpp",
    "content": "#include <Lz/c_string.hpp>\n#include <Lz/detail/procs/next_fast.hpp>\n#include <Lz/repeat.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Sentinel operators\") {\n    SUBCASE(\"Operator==\") {\n        static_assert(lz::default_sentinel == lz::default_sentinel, \"\");\n    }\n\n    SUBCASE(\"Operator!=\") {\n        static_assert(!(lz::default_sentinel != lz::default_sentinel), \"\");\n    }\n\n    SUBCASE(\"Operator<\") {\n        static_assert(!(lz::default_sentinel < lz::default_sentinel), \"\");\n    }\n\n    SUBCASE(\"Operator>\") {\n        static_assert(!(lz::default_sentinel > lz::default_sentinel), \"\");\n    }\n\n    SUBCASE(\"Operator<=\") {\n        static_assert(lz::default_sentinel <= lz::default_sentinel, \"\");\n    }\n\n    SUBCASE(\"Operator>=\") {\n        static_assert(lz::default_sentinel >= lz::default_sentinel, \"\");\n    }\n}\n\nTEST_CASE(\"default_sentinel with iterator operators\") {\n    auto repeater = lz::repeat(20, 5);\n\n    SUBCASE(\"Operator== with iterator\") {\n        auto last = repeater.begin() + lz::ssize(repeater);\n        REQUIRE(last == lz::default_sentinel);\n        REQUIRE(last.operator==(lz::default_sentinel));\n        REQUIRE(lz::default_sentinel == last);\n\n        --last;\n        REQUIRE_FALSE(last == lz::default_sentinel);\n        REQUIRE_FALSE(last.operator==(lz::default_sentinel));\n        REQUIRE_FALSE(lz::default_sentinel == last);\n    }\n\n    SUBCASE(\"Operator!= with iterator\") {\n        auto last = repeater.begin() + lz::ssize(repeater);\n        REQUIRE_FALSE(last != lz::default_sentinel);\n        REQUIRE_FALSE(last.operator!=(lz::default_sentinel));\n        REQUIRE_FALSE(lz::default_sentinel != last);\n\n        --last;\n        REQUIRE(last != lz::default_sentinel);\n        REQUIRE(last.operator!=(lz::default_sentinel));\n        REQUIRE(lz::default_sentinel != last);\n    }\n\n    SUBCASE(\"Operator> with iterator\") {\n        auto last = repeater.begin() + lz::ssize(repeater);\n        REQUIRE_FALSE(last > lz::default_sentinel);\n        REQUIRE_FALSE(last.operator>(lz::default_sentinel));\n        REQUIRE_FALSE(lz::default_sentinel > last);\n\n        --last;\n        REQUIRE_FALSE(last > lz::default_sentinel);\n        REQUIRE_FALSE(last.operator>(lz::default_sentinel));\n        REQUIRE(lz::default_sentinel > last);\n    }\n\n    SUBCASE(\"Operator< with iterator\") {\n        auto last = repeater.begin() + lz::ssize(repeater);\n        REQUIRE_FALSE(last < lz::default_sentinel);\n        REQUIRE_FALSE(last.operator<(lz::default_sentinel));\n        REQUIRE_FALSE(lz::default_sentinel < last);\n\n        --last;\n        REQUIRE(last < lz::default_sentinel);\n        REQUIRE(last.operator<(lz::default_sentinel));\n        REQUIRE_FALSE(lz::default_sentinel < last);\n    }\n\n    SUBCASE(\"Operator>= with iterator\") {\n        auto last = repeater.begin() + lz::ssize(repeater);\n        REQUIRE(last >= lz::default_sentinel);\n        REQUIRE(last.operator>=(lz::default_sentinel));\n        REQUIRE(lz::default_sentinel >= last);\n\n        --last;\n        REQUIRE_FALSE(last >= lz::default_sentinel);\n        REQUIRE_FALSE(last.operator>=(lz::default_sentinel));\n        REQUIRE(lz::default_sentinel >= last);\n    }\n\n    SUBCASE(\"Operator<= with iterator\") {\n        auto last = repeater.begin() + lz::ssize(repeater);\n        REQUIRE(last <= lz::default_sentinel);\n        REQUIRE(last.operator<=(lz::default_sentinel));\n        REQUIRE(lz::default_sentinel <= last);\n\n        --last;\n        REQUIRE(last <= lz::default_sentinel);\n        REQUIRE(last.operator<=(lz::default_sentinel));\n        REQUIRE_FALSE(lz::default_sentinel <= last);\n    }\n\n    SUBCASE(\"Operator- with iterator\") {\n        auto last = repeater.begin() + lz::ssize(repeater);\n        REQUIRE(last - lz::default_sentinel == 0);\n        REQUIRE(last.operator-(lz::default_sentinel) == 0);\n        REQUIRE(lz::default_sentinel - last == 0);\n\n        --last;\n        REQUIRE(last - lz::default_sentinel == -1);\n        REQUIRE(last.operator-(lz::default_sentinel) == -1);\n        REQUIRE(lz::default_sentinel - last == 1);\n    }\n}\n\nTEST_CASE(\"Helper functions\") {\n    SUBCASE(\"next_fast ra\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto next = lz::detail::next_fast(vec, 2);\n        REQUIRE(next == std::next(vec.begin(), 2));\n        REQUIRE(*next == 3);\n\n        next = lz::detail::next_fast(vec, 4);\n        REQUIRE(next == std::next(vec.begin(), 4));\n        REQUIRE(*next == 5);\n    }\n\n    SUBCASE(\"next_fast ra sentinel\") {\n        auto repeat = lz::repeat(1, 5);\n        auto next = lz::detail::next_fast(repeat, 2);\n        REQUIRE(next == std::next(repeat.begin(), 2));\n        REQUIRE(*next == 1);\n\n        next = lz::detail::next_fast(repeat, 5);\n        REQUIRE(next == std::end(repeat));\n    }\n\n    SUBCASE(\"next_fast bidi\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5 };\n        auto next = lz::detail::next_fast(lst, 2);\n        REQUIRE(next == std::next(lst.begin(), 2));\n        REQUIRE(*next == 3);\n\n        next = lz::detail::next_fast(lst, 4);\n        REQUIRE(next == std::next(lst.begin(), 4));\n        REQUIRE(*next == 5);\n    }\n\n    SUBCASE(\"next_fast fwd\") {\n        auto cstr = lz::c_string(\"Hello\");\n        auto next = lz::detail::next_fast(cstr, 2);\n        REQUIRE(next == std::next(cstr.begin(), 2));\n        REQUIRE(*next == 'l');\n\n        next = lz::detail::next_fast(cstr, 4);\n        REQUIRE(next == std::next(cstr.begin(), 4));\n        REQUIRE(*next == 'o');\n    }\n\n    SUBCASE(\"next_fast_safe ra\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5 };\n        auto next = lz::detail::next_fast_safe(vec, 2);\n        REQUIRE(next == std::next(vec.begin(), 2));\n        REQUIRE(*next == 3);\n\n        next = lz::detail::next_fast_safe(vec, lz::ssize(vec) + 1);\n        REQUIRE(next == std::end(vec));\n    }\n\n    SUBCASE(\"next_fast_safe ra sentinel\") {\n        auto repeat = lz::repeat(1, 5);\n        auto next = lz::detail::next_fast_safe(repeat, 2);\n        REQUIRE(next == std::next(repeat.begin(), 2));\n        REQUIRE(*next == 1);\n\n        next = lz::detail::next_fast_safe(repeat, lz::ssize(repeat) + 1);\n        REQUIRE(next == std::end(repeat));\n    }\n\n    SUBCASE(\"next_fast_safe fwd\") {\n        auto cstr = lz::c_string(\"Hel1o\");\n        auto next = lz::detail::next_fast_safe(cstr, 2);\n        REQUIRE(next == std::next(cstr.begin(), 2));\n        REQUIRE(*next == 'l');\n\n        next = lz::detail::next_fast_safe(cstr, lz::distance(cstr) + 1);\n        REQUIRE(next == std::end(cstr));\n    }\n\n    SUBCASE(\"next_fast_safe bidi\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5 };\n        auto next = lz::detail::next_fast_safe(lst, 2);\n        REQUIRE(next == std::next(lst.begin(), 2));\n        REQUIRE(*next == 3);\n\n        next = lz::detail::next_fast_safe(lst, lz::ssize(lst) + 1);\n        REQUIRE(next == std::end(lst));\n    }\n}\n"
  },
  {
    "path": "tests/join_where.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/join_where.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nstruct customer {\n    int id;\n};\n\nstruct payment_bill {\n    int customer_id;\n    int id;\n};\n\nTEST_CASE(\"Join where with sentinels\") {\n    auto c_str = lz::c_string(\"To join on\");\n    auto sorted_seq = lz::c_string(\"Toxzzzz\");\n\n    using f1 = std::function<char(char)>;\n    using f2 = std::function<char(char)>;\n    using f3 = std::function<std::tuple<char, char>(char, char)>;\n\n    lz::join_where_iterable<decltype(c_str), decltype(sorted_seq), f1, f2, f3> joined =\n        lz::join_where(c_str, sorted_seq, f1([](char c) { return c; }), f2([](char c) { return c; }),\n                       f3([](char c, char c2) { return std::make_tuple(c, c2); }));\n\n    static_assert(!std::is_same<decltype(joined.begin()), decltype(joined.end())>::value, \"Should be sentinel\");\n    std::vector<std::tuple<char, char>> expected = {\n        std::make_tuple('T', 'T'),\n        std::make_tuple('o', 'o'),\n        std::make_tuple('o', 'o'),\n        std::make_tuple('o', 'o'),\n    };\n\n    auto vec = joined | lz::to<std::vector>();\n    REQUIRE(vec == expected);\n\n    SUBCASE(\"Operator=\") {\n        std::vector<std::pair<int, int>> vec3 = { { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 } };\n        std::vector<std::pair<int, int>> vec4 = { { 1, 2 }, { 3, 4 }, { 4, 5 }, { 5, 6 } };\n        auto joined2 = lz::join_where(\n            vec3, vec4, [](const std::pair<int, int>& a) { return a.first; },\n            [](const std::pair<int, int>& a) { return a.first; },\n            [](const std::pair<int, int>& a, const std::pair<int, int>& b) { return std::make_pair(a.first, b.second); });\n\n        auto common = make_sentinel_assign_op_tester(joined2);\n        using reference = lz::detail::ref_iterable_t<decltype(common)>;\n\n        std::vector<std::pair<int, int>> expected2 = { { 1, 2 }, { 3, 4 }, { 4, 5 } };\n        REQUIRE(lz::equal(common, expected2,\n                          [](reference a, const std::pair<int, int>& b) { return a.first == b.first && a.second == b.second; }));\n    }\n}\n\nTEST_CASE(\"Join changing and creating elements\") {\n    std::vector<customer> customers{\n        customer{ 25 }, customer{ 1 }, customer{ 39 }, customer{ 103 }, customer{ 99 },\n    };\n    std::vector<payment_bill> payment_bills{\n        payment_bill{ 25, 0 }, payment_bill{ 25, 2 },    payment_bill{ 25, 3 },\n        payment_bill{ 99, 1 }, payment_bill{ 2523, 52 }, payment_bill{ 2523, 53 },\n    };\n\n    auto joined = lz::join_where(\n        customers, payment_bills, [](const customer& p) { return p.id; }, [](const payment_bill& c) { return c.customer_id; },\n        [](const customer& p, const payment_bill& c) { return std::make_tuple(p, c); });\n\n    SUBCASE(\"Should initialized with first match\") {\n        std::tuple<customer, payment_bill> match = *joined.begin();\n        customer& customer = std::get<0>(match);\n        payment_bill& payment_bill = std::get<1>(match);\n\n        REQUIRE(customer.id == payment_bill.customer_id);\n        REQUIRE(customer.id == 25);\n        REQUIRE(payment_bill.id == 0);\n    }\n}\n\nTEST_CASE(\"Empty or one element join where\") {\n    SUBCASE(\"Empty join\") {\n        std::vector<customer> customers;\n        std::vector<payment_bill> payment_bills;\n        auto joined = lz::join_where(\n            customers, payment_bills, [](const customer& p) { return p.id; }, [](const payment_bill& c) { return c.customer_id; },\n            [](const customer& p, const payment_bill& c) { return std::make_tuple(p, c); });\n        REQUIRE(lz::empty(joined));\n        REQUIRE_FALSE(lz::has_many(joined));\n        REQUIRE_FALSE(lz::has_one(joined));\n    }\n\n    SUBCASE(\"One element join 1\") {\n        std::vector<customer> customers{ customer{ 25 } };\n        std::vector<payment_bill> payment_bills;\n        auto joined = lz::join_where(\n            customers, payment_bills, [](const customer& p) { return p.id; }, [](const payment_bill& c) { return c.customer_id; },\n            [](const customer& p, const payment_bill& c) { return std::make_tuple(p, c); });\n        REQUIRE(lz::empty(joined));\n    }\n\n    SUBCASE(\"One element join 2\") {\n        std::vector<customer> customers;\n        std::vector<payment_bill> payment_bills{ payment_bill{ 25, 0 } };\n        auto joined = lz::join_where(\n            customers, payment_bills, [](const customer& p) { return p.id; }, [](const payment_bill& c) { return c.customer_id; },\n            [](const customer& p, const payment_bill& c) { return std::make_tuple(p, c); });\n        REQUIRE(lz::empty(joined));\n    }\n\n    SUBCASE(\"One element join 3\") {\n        std::vector<customer> customers{ customer{ 25 } };\n        std::vector<payment_bill> payment_bills{ payment_bill{ 25, 0 } };\n        auto joined = lz::join_where(\n            customers, payment_bills, [](const customer& p) { return p.id; }, [](const payment_bill& c) { return c.customer_id; },\n            [](const customer& p, const payment_bill& c) { return std::make_tuple(p, c); });\n        REQUIRE(lz::has_one(joined));\n        REQUIRE_FALSE(lz::has_many(joined));\n        REQUIRE_FALSE(lz::empty(joined));\n    }\n}\n\nTEST_CASE(\"Left join binary operations\") {\n    std::vector<customer> customers{\n        customer{ 25 }, customer{ 1 }, customer{ 39 }, customer{ 103 }, customer{ 99 },\n    };\n    std::vector<payment_bill> payment_bills{\n        payment_bill{ 25, 0 }, payment_bill{ 25, 2 },    payment_bill{ 25, 3 },\n        payment_bill{ 99, 1 }, payment_bill{ 2523, 52 }, payment_bill{ 2523, 53 },\n    };\n\n    auto joined = customers |\n                  lz::join_where(\n                      payment_bills, [](const customer& p) { return p.id; }, [](const payment_bill& c) { return c.customer_id; },\n                      [](const customer& p, const payment_bill& c) { return std::make_tuple(p, c); });\n\n    auto expected = std::vector<std::tuple<customer, payment_bill>>{ std::make_tuple(customer{ 25 }, payment_bill{ 25, 0 }),\n                                                                     std::make_tuple(customer{ 25 }, payment_bill{ 25, 2 }),\n                                                                     std::make_tuple(customer{ 25 }, payment_bill{ 25, 3 }),\n                                                                     std::make_tuple(customer{ 99 }, payment_bill{ 99, 1 }) };\n    SUBCASE(\"Operator++\") {\n        REQUIRE(lz::equal(joined, expected,\n                          [](const std::tuple<customer, payment_bill>& a, const std::tuple<customer, payment_bill>& b) {\n                              auto& a_fst = std::get<0>(a);\n                              auto& a_snd = std::get<1>(a);\n                              auto& b_fst = std::get<0>(b);\n                              auto& b_snd = std::get<1>(b);\n                              return a_fst.id == b_fst.id && a_snd.id == b_snd.id && a_snd.customer_id == b_snd.customer_id;\n                          }));\n    }\n}\n\nTEST_CASE(\"join_where_iterable to containers\") {\n    std::vector<customer> customers{\n        customer{ 25 }, customer{ 1 }, customer{ 39 }, customer{ 103 }, customer{ 99 },\n    };\n    std::vector<payment_bill> payment_bills{\n        payment_bill{ 25, 0 }, payment_bill{ 25, 2 },    payment_bill{ 25, 3 },\n        payment_bill{ 99, 1 }, payment_bill{ 2523, 52 }, payment_bill{ 2523, 53 },\n    };\n\n    auto joined = lz::join_where(\n        customers, payment_bills, [](const customer& p) { return p.id; }, [](const payment_bill& c) { return c.customer_id; },\n        [](const customer& p, const payment_bill& c) { return std::make_tuple(p, c); });\n\n    SUBCASE(\"To array\") {\n        std::array<std::tuple<customer, payment_bill>, 4> expected = { std::make_tuple(customer{ 25 }, payment_bill{ 25, 0 }),\n                                                                       std::make_tuple(customer{ 25 }, payment_bill{ 25, 2 }),\n                                                                       std::make_tuple(customer{ 25 }, payment_bill{ 25, 3 }),\n                                                                       std::make_tuple(customer{ 99 }, payment_bill{ 99, 1 }) };\n\n        auto array = joined | lz::to<std::array<std::tuple<customer, payment_bill>, 4>>();\n        REQUIRE(lz::equal(array, expected,\n                          [](const std::tuple<customer, payment_bill>& a, const std::tuple<customer, payment_bill>& b) {\n                              auto& a_fst = std::get<0>(a);\n                              auto& a_snd = std::get<1>(a);\n                              auto& b_fst = std::get<0>(b);\n                              auto& b_snd = std::get<1>(b);\n                              return a_fst.id == b_fst.id && a_snd.id == b_snd.id && a_snd.customer_id == b_snd.customer_id;\n                          }));\n    }\n\n    SUBCASE(\"To vector\") {\n        std::vector<std::tuple<customer, payment_bill>> expected = { std::make_tuple(customer{ 25 }, payment_bill{ 25, 0 }),\n                                                                     std::make_tuple(customer{ 25 }, payment_bill{ 25, 2 }),\n                                                                     std::make_tuple(customer{ 25 }, payment_bill{ 25, 3 }),\n                                                                     std::make_tuple(customer{ 99 }, payment_bill{ 99, 1 }) };\n\n        auto vec = joined | lz::to<std::vector>();\n        REQUIRE(lz::equal(vec, expected,\n                          [](const std::tuple<customer, payment_bill>& a, const std::tuple<customer, payment_bill>& b) {\n                              auto& a_fst = std::get<0>(a);\n                              auto& a_snd = std::get<1>(a);\n                              auto& b_fst = std::get<0>(b);\n                              auto& b_snd = std::get<1>(b);\n                              return a_fst.id == b_fst.id && a_snd.id == b_snd.id && a_snd.customer_id == b_snd.customer_id;\n                          }));\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        std::list<std::tuple<customer, payment_bill>> expected = { std::make_tuple(customer{ 25 }, payment_bill{ 25, 0 }),\n                                                                   std::make_tuple(customer{ 25 }, payment_bill{ 25, 2 }),\n                                                                   std::make_tuple(customer{ 25 }, payment_bill{ 25, 3 }),\n                                                                   std::make_tuple(customer{ 99 }, payment_bill{ 99, 1 }) };\n\n        auto list = joined | lz::to<std::list>();\n        REQUIRE(lz::equal(list, expected,\n                          [](const std::tuple<customer, payment_bill>& a, const std::tuple<customer, payment_bill>& b) {\n                              auto& a_fst = std::get<0>(a);\n                              auto& a_snd = std::get<1>(a);\n                              auto& b_fst = std::get<0>(b);\n                              auto& b_snd = std::get<1>(b);\n                              return a_fst.id == b_fst.id && a_snd.id == b_snd.id && a_snd.customer_id == b_snd.customer_id;\n                          }));\n    }\n\n    SUBCASE(\"To map\") {\n        using pair = std::pair<int, std::tuple<customer, payment_bill>>;\n\n        std::map<int, std::tuple<customer, payment_bill>> expected = {\n            { 0, std::make_tuple(customer{ 25 }, payment_bill{ 25, 0 }) },\n            { 2, std::make_tuple(customer{ 25 }, payment_bill{ 25, 2 }) },\n            { 3, std::make_tuple(customer{ 25 }, payment_bill{ 25, 3 }) },\n            { 1, std::make_tuple(customer{ 99 }, payment_bill{ 99, 1 }) }\n        };\n\n        auto actual =\n            joined |\n            lz::map([](const std::tuple<customer, payment_bill>& val) { return std::make_pair(std::get<1>(val).id, val); }) |\n            lz::to<std::map<int, std::tuple<customer, payment_bill>>>();\n\n        REQUIRE(lz::equal(expected, actual, [](const pair& a, const pair& b) {\n            return a.first == b.first && std::get<1>(a.second).id == std::get<1>(b.second).id &&\n                   std::get<1>(a.second).customer_id == std::get<1>(b.second).customer_id;\n        }));\n    }\n\n    SUBCASE(\"To unordered map\") {\n        using pair = std::pair<int, std::tuple<customer, payment_bill>>;\n\n        std::unordered_map<int, std::tuple<customer, payment_bill>> expected = {\n            { 0, std::make_tuple(customer{ 25 }, payment_bill{ 25, 0 }) },\n            { 2, std::make_tuple(customer{ 25 }, payment_bill{ 25, 2 }) },\n            { 3, std::make_tuple(customer{ 25 }, payment_bill{ 25, 3 }) },\n            { 1, std::make_tuple(customer{ 99 }, payment_bill{ 99, 1 }) }\n        };\n\n        auto actual =\n            joined |\n            lz::map([](const std::tuple<customer, payment_bill>& val) { return std::make_pair(std::get<1>(val).id, val); }) |\n            lz::to<std::unordered_map<int, std::tuple<customer, payment_bill>>>();\n\n        REQUIRE(lz::equal(expected, actual, [](const pair& a, const pair& b) {\n            return a.first == b.first && std::get<1>(a.second).id == std::get<1>(b.second).id &&\n                   std::get<1>(a.second).customer_id == std::get<1>(b.second).customer_id;\n        }));\n    }\n}\n"
  },
  {
    "path": "tests/loop.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/loop.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"inf loop_iterable tests with sentinels\") {\n    auto cstr = lz::c_string(\"Hello\");\n    lz::loop_iterable_inf<decltype(cstr)> looper = lz::loop(cstr);\n    static_assert(!std::is_same<decltype(looper.begin()), decltype(looper.end())>::value, \"Should be sentinel\");\n\n    REQUIRE(*looper.begin() == 'H');\n    auto looper_it = looper.begin();\n    auto c_str_it = cstr.begin();\n    for (std::size_t i = 0; i < 100; ++i) {\n        REQUIRE(*looper_it == *c_str_it);\n        ++looper_it;\n        ++c_str_it;\n        if (c_str_it == cstr.end()) {\n            c_str_it = cstr.begin();\n        }\n    }\n\n    SUBCASE(\"Operator=\") {\n        looper_it = looper.begin();\n        // inf never stops\n        REQUIRE(looper_it != looper.begin());\n        looper_it = looper.end();\n        // inf never stops\n        REQUIRE(looper_it != looper.end());\n    }\n}\n\nTEST_CASE(\"Empty loop iterable\") {\n    std::vector<int> vec;\n    auto looper = lz::loop(vec);\n    REQUIRE(lz::empty(looper));\n}\n\nTEST_CASE(\"Basic functionality loop\") {\n    std::vector<int> vec = { 1, 2, 3, 4 };\n    auto looper = lz::loop(vec);\n    static_assert(!std::is_same<decltype(looper.begin()), decltype(looper.end())>::value, \"Should be sentinel\");\n\n    SUBCASE(\"Always true\") {\n        REQUIRE(looper.begin() != looper.end());\n        REQUIRE(looper.begin() != looper.begin());\n    }\n\n    SUBCASE(\"Always false\") {\n        REQUIRE_FALSE(looper.begin() == looper.end());\n        REQUIRE_FALSE(looper.begin() == looper.begin());\n    }\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> lst = { 1, 2, 3 };\n        auto looped = lz::loop(lst, 2);\n        auto common = make_sentinel_assign_op_tester(looped);\n        auto expected = { 1, 2, 3, 1, 2, 3 };\n        REQUIRE(lz::equal(common, expected));\n\n        lst = {};\n        looped = lz::loop(lst, 2);\n        common = make_sentinel_assign_op_tester(looped);\n        REQUIRE(lz::empty(common));\n\n        lst = { 1, 2, 3 };\n        looped = lz::loop(lst, 0);\n        common = make_sentinel_assign_op_tester(looped);\n        REQUIRE(lz::empty(common));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> a = { 1, 2, 3, 4, 5 };\n        auto loop = lz::loop(make_sized_bidi_sentinelled(a), 2);\n        auto common = make_sentinel_assign_op_tester(loop);\n        auto expected = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 };\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n\n        a = {};\n        loop = lz::loop(make_sized_bidi_sentinelled(a), 2);\n        common = make_sentinel_assign_op_tester(loop);\n        REQUIRE(lz::empty(common));\n\n        a = { 1, 2, 3, 4, 5 };\n        loop = lz::loop(make_sized_bidi_sentinelled(a), 0);\n        common = make_sentinel_assign_op_tester(loop);\n        REQUIRE(lz::empty(common));\n    }\n\n    SUBCASE(\"random access\") {\n        auto a = lz::repeat(1, 3);\n        auto looper_common = make_sentinel_assign_op_tester(lz::loop(a, 2));\n        auto expected = { 1, 1, 1, 1, 1, 1 };\n        test_procs::test_operator_plus(looper_common, expected);\n        test_procs::test_operator_minus(looper_common);\n\n        a = lz::repeat(0, 0);\n        looper_common = make_sentinel_assign_op_tester(lz::loop(a, 2));\n        REQUIRE(lz::empty(looper_common));\n        test_procs::test_operator_minus(looper_common);\n\n        a = lz::repeat(1, 3);\n        looper_common = make_sentinel_assign_op_tester(lz::loop(a, 0));\n        REQUIRE(lz::empty(looper_common));\n        test_procs::test_operator_minus(looper_common);\n    }\n}\n\nTEST_CASE(\"Loop with non while true argument\") {\n    std::vector<int> vec = { 1, 2, 3, 4 };\n\n    SUBCASE(\"Empty\") {\n        lz::loop_iterable<std::vector<int>> looper = lz::loop(vec, 0);\n        static_assert(std::is_same<decltype(looper.begin()), decltype(looper.end())>::value, \"Should not be sentinel\");\n        REQUIRE(looper.size() == 0);\n        REQUIRE(lz::empty(looper));\n\n        std::vector<int> v;\n        auto looper2 = lz::loop(v, 0);\n        REQUIRE(looper2.size() == 0);\n        REQUIRE(lz::empty(looper2));\n        REQUIRE(!lz::has_one(looper2));\n        REQUIRE(!lz::has_many(looper2));\n\n        looper2 = lz::loop(v, 2);\n        REQUIRE(looper2.size() == 0);\n        REQUIRE(lz::empty(looper2));\n        REQUIRE(!lz::has_one(looper2));\n        REQUIRE(!lz::has_many(looper2));\n    }\n\n    SUBCASE(\"Size\") {\n        auto looper = vec | lz::loop(2);\n        CHECK(looper.size() == static_cast<std::size_t>(std::distance(looper.begin(), looper.end())));\n        CHECK(looper.size() == 8);\n        looper = lz::loop(vec, 3);\n        CHECK(looper.size() == static_cast<std::size_t>(std::distance(looper.begin(), looper.end())));\n        CHECK(looper.size() == 12);\n    }\n\n    SUBCASE(\"Operator--/++\") {\n        auto looper = lz::loop(vec, 2);\n        std::vector<int> expected = { 1, 2, 3, 4, 1, 2, 3, 4 };\n        REQUIRE(lz::equal(looper, expected));\n        REQUIRE(lz::equal(looper | lz::reverse, expected | lz::reverse, [](const int a, const int b) { return a == b; }));\n    }\n\n    SUBCASE(\"Operator+\") {\n        auto looper = lz::loop(vec, 2);\n        std::vector<int> expected = { 1, 2, 3, 4, 1, 2, 3, 4 };\n        test_procs::test_operator_plus(looper, expected);\n\n        looper = lz::loop(vec, 3);\n        expected = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 };\n        test_procs::test_operator_plus(looper, expected);\n\n        looper = lz::loop(vec, 1);\n        expected = { 1, 2, 3, 4 };\n        test_procs::test_operator_plus(looper, expected);\n\n        looper = lz::loop(vec, 0);\n        expected = {};\n        test_procs::test_operator_plus(looper, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        auto looper = lz::loop(vec, 2);\n        test_procs::test_operator_minus(looper);\n\n        looper = lz::loop(vec, 1);\n        test_procs::test_operator_minus(looper);\n\n        looper = lz::loop(vec, 3);\n        test_procs::test_operator_minus(looper);\n\n        looper = lz::loop(vec, 0);\n        test_procs::test_operator_minus(looper);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto l = lz::loop(lz::repeat(0, 5), 2);\n        test_procs::test_operator_minus(l);\n\n        l = lz::loop(lz::repeat(0, 5), 1);\n        test_procs::test_operator_minus(l);\n\n        l = lz::loop(lz::repeat(0, 5), 3);\n        test_procs::test_operator_minus(l);\n\n        l = lz::loop(lz::repeat(0, 5), 0);\n        test_procs::test_operator_minus(l);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto l = lz::loop(lz::repeat(0, 5), 2);\n        std::vector<int> expected = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };\n        test_procs::test_operator_plus(l, expected);\n\n        l = lz::loop(lz::repeat(0, 5), 1);\n        expected = { 0, 0, 0, 0, 0 };\n        test_procs::test_operator_plus(l, expected);\n\n        l = lz::loop(lz::repeat(0, 5), 3);\n        expected = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };\n        test_procs::test_operator_plus(l, expected);\n\n        l = lz::loop(lz::repeat(0, 5), 0);\n        expected = {};\n        test_procs::test_operator_plus(l, expected);\n    }\n}\n"
  },
  {
    "path": "tests/map.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nstruct TestStruct {\n    std::string test_field_str;\n    int test_field_int;\n};\n\nTEST_CASE(\"Map with sentinels\") {\n    auto cstr = lz::c_string(\"Hello, World!\");\n    auto map = lz::map(cstr, [](char c) { return std::toupper(c); });\n    static_assert(!std::is_same<decltype(map.end()), decltype(map.begin())>::value, \"Should be sentinels\");\n    auto c_str_expected = lz::c_string(\"HELLO, WORLD!\");\n    REQUIRE(lz::equal(map, c_str_expected));\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> a = { 1, 3, 5 };\n        auto map = lz::map(a, [](int i) { return i; });\n        auto common = make_sentinel_assign_op_tester(map);\n        auto expected = { 1, 3, 5 };\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> a = { 1, 2, 3, 4, 5 };\n        auto map = lz::map(make_sized_bidi_sentinelled(a), [](int i) { return i; });\n        auto common = make_sentinel_assign_op_tester(map);\n        auto expected = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"random access\") {\n        auto a = lz::repeat(1, 3);\n        auto map_common = make_sentinel_assign_op_tester(lz::map(a, [](int i) { return i; }));\n        auto expected = { 1, 1, 1 };\n        test_procs::test_operator_plus(map_common, expected);\n        test_procs::test_operator_minus(map_common);\n    }\n}\n\nTEST_CASE(\"Map changing and creating elements\") {\n    constexpr std::size_t size = 3;\n    std::array<TestStruct, size> array = { TestStruct{ \"FieldA\", 1 }, TestStruct{ \"FieldB\", 2 }, TestStruct{ \"FieldC\", 3 } };\n\n    SUBCASE(\"Should map out element\") {\n        auto map = lz::map(array, [](const TestStruct& t) { return t.test_field_str; });\n        REQUIRE(map.size() == size);\n        static_assert(std::is_same<decltype(map.end()), decltype(map.begin())>::value, \"Should not be sentinels\");\n        static_assert(std::is_same<decltype(*map.begin()), std::string>::value, \"Types to not match (decltype(*map.begin()) and std::string)\");\n\n        auto it = map.begin();\n        REQUIRE(*it == \"FieldA\");\n        REQUIRE(*(++it) == \"FieldB\");\n        REQUIRE(*(++it) == \"FieldC\");\n    }\n\n    SUBCASE(\"Should be by reference\") {\n        std::size_t count = 0;\n        std::function<std::string&(TestStruct&)> f = [&count, &array](TestStruct& t) -> std::string& {\n            REQUIRE(&t == &array[count++]);\n            return t.test_field_str;\n        };\n        lz::map_iterable<decltype(array), decltype(f)> map = array | lz::map(std::move(f));\n\n        for (auto&& _ : map) {\n            static_cast<void>(_);\n        }\n    }\n}\n\nTEST_CASE(\"Map binary operations\") {\n    constexpr std::size_t size = 3;\n    std::array<TestStruct, size> array = { TestStruct{ \"FieldA\", 1 }, TestStruct{ \"FieldB\", 2 }, TestStruct{ \"FieldC\", 3 } };\n\n    std::function<std::string(TestStruct)> f = [](const TestStruct& t) {\n        return t.test_field_str;\n    };\n    auto map = lz::map(array, std::move(f));\n    auto it = map.begin();\n\n    SUBCASE(\"Operator++\") {\n        auto expected = std::vector<std::string>{ \"FieldA\", \"FieldB\", \"FieldC\" };\n        REQUIRE(lz::equal(map, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto expected = std::vector<std::string>{ \"FieldC\", \"FieldB\", \"FieldA\" };\n        REQUIRE(lz::equal(map | lz::reverse, expected));\n    }\n\n    SUBCASE(\"Operator== & operator!=\") {\n        REQUIRE(it != map.end());\n        it = map.end();\n        REQUIRE(it == map.end());\n    }\n\n    SUBCASE(\"Operator+\") {\n        auto expected = std::vector<std::string>{ \"FieldA\", \"FieldB\", \"FieldC\" };\n        test_procs::test_operator_plus(map, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(map);\n    }\n\n    SUBCASE(\"Operator- sentinel\") {\n        auto repeater = lz::repeat(20, 5);\n        auto map_sentinel = lz::map(repeater, [](int i) { return i; });\n        test_procs::test_operator_minus(map_sentinel);\n    }\n\n    SUBCASE(\"Operator+ sentinel\") {\n        auto repeater = lz::repeat(20, 5);\n        auto map_sentinel = lz::map(repeater, [](int i) { return i; });\n        std::vector<int> expected = { 20, 20, 20, 20, 20 };\n        test_procs::test_operator_plus(map_sentinel, expected);\n    }\n}\n\nTEST_CASE(\"Empty or one element map\") {\n    SUBCASE(\"Empty map\") {\n        std::vector<int> vec;\n        auto map = lz::map(vec, [](int i) { return i; });\n        REQUIRE(lz::empty(map));\n    }\n\n    SUBCASE(\"One element map\") {\n        std::vector<int> vec = { 1 };\n        auto map = lz::map(vec, [](int i) { return i; });\n        REQUIRE_FALSE(lz::empty(map));\n        REQUIRE(lz::has_one(map));\n        REQUIRE_FALSE(lz::has_many(map));\n    }\n}\n\nTEST_CASE(\"Map to containers\") {\n    constexpr std::size_t size = 3;\n    std::array<TestStruct, size> array = { TestStruct{ \"FieldA\", 1 }, TestStruct{ \"FieldB\", 2 }, TestStruct{ \"FieldC\", 3 } };\n    auto map = lz::map(array, [](const TestStruct& t) { return t.test_field_str; });\n\n    SUBCASE(\"To array\") {\n        auto str_array = map | lz::to<std::array<std::string, size>>();\n\n        for (std::size_t i = 0; i < array.size(); i++) {\n            REQUIRE(str_array[i] == array[i].test_field_str);\n        }\n    }\n\n    SUBCASE(\"To vector\") {\n        auto str_vec = map | lz::to<std::vector>();\n\n        for (std::size_t i = 0; i < array.size(); i++) {\n            REQUIRE(str_vec[i] == array[i].test_field_str);\n        }\n    }\n\n    SUBCASE(\"To other container using to()\") {\n        auto str_list = map | lz::to<std::list<std::string>>();\n        auto list_iter = str_list.begin();\n\n        for (std::size_t i = 0; i < array.size(); i++, ++list_iter) {\n            REQUIRE(*list_iter == array[i].test_field_str);\n        }\n    }\n\n    SUBCASE(\"To map\") {\n        std::map<std::string, std::string> actual = map | lz::map([](const std::string& s) { return std::make_pair(s, s); }) |\n                                                    lz::to<std::map<std::string, std::string>>();\n\n        std::map<std::string, std::string> expected = {\n            std::make_pair(\"FieldA\", \"FieldA\"),\n            std::make_pair(\"FieldB\", \"FieldB\"),\n            std::make_pair(\"FieldC\", \"FieldC\"),\n        };\n\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        std::unordered_map<std::string, std::string> actual = map |\n                                                              lz::map([](const std::string& s) { return std::make_pair(s, s); }) |\n                                                              lz::to<std::unordered_map<std::string, std::string>>();\n\n        std::unordered_map<std::string, std::string> expected = {\n            std::make_pair(\"FieldA\", \"FieldA\"),\n            std::make_pair(\"FieldB\", \"FieldB\"),\n            std::make_pair(\"FieldC\", \"FieldC\"),\n        };\n\n        REQUIRE(actual == expected);\n    }\n}\n"
  },
  {
    "path": "tests/maybe_owned.cpp",
    "content": "#include <Lz/detail/maybe_owned.hpp>\n#include <Lz/filter.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <doctest/doctest.h>\n\nnamespace doctest {\nnamespace detail {\n// workaround for volatile pointers not being handled correctly by the default filldata\n\n// clang-format off\n\ntemplate<typename T>\nstruct filldata<volatile T*> {\nDOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4180)\n    static void fill(std::ostream* stream, const volatile T* in) {\n        DOCTEST_MSVC_SUPPRESS_WARNING_POP\n        DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(\"-Wmicrosoft-cast\")\n        filldata<const volatile void*>::fill(stream,\n#if DOCTEST_GCC == 0 || DOCTEST_GCC >= DOCTEST_COMPILER(4, 9, 0)\n                                             reinterpret_cast<const volatile void*>(in)\n#else\n                                             *reinterpret_cast<const volatile void* const*>(&in)\n#endif\n        );\nDOCTEST_CLANG_SUPPRESS_WARNING_POP\n    }\n};\n\n// clang-format on\ntemplate<>\nvoid filldata<const volatile void*>::fill(std::ostream* stream, const volatile void* in) {\n    if (in) {\n        *stream << in;\n    }\n    else {\n        *stream << \"nullptr\";\n    }\n}\n} // namespace detail\n} // namespace doctest\n\nTEST_CASE(\"maybe_owned basic tests\") {\n    SUBCASE(\"STD container\") {\n        std::vector<int> vec{ 1, 2, 3 };\n        lz::maybe_owned<std::vector<int>> maybe_owned{ vec };\n        static_assert(std::is_same<decltype(maybe_owned), lz::detail::maybe_owned_impl<std::vector<int>, false>>::value,\n                      \"Should be by ref\");\n        REQUIRE(&(*vec.begin()) == &(*maybe_owned.begin()));\n    }\n\n    SUBCASE(\"Const STD container\") {\n        const std::vector<int> vec{ 1, 2, 3 };\n        lz::maybe_owned<const std::vector<int>> maybe_owned{ vec };\n        static_assert(std::is_same<decltype(maybe_owned), lz::detail::maybe_owned_impl<const std::vector<int>, false>>::value,\n                      \"Should be by ref\");\n        REQUIRE(&(*vec.begin()) == &(*maybe_owned.begin()));\n    }\n\n    SUBCASE(\"C array\") {\n        int arr[] = { 1, 2, 3 };\n        lz::maybe_owned<int[3]> maybe_owned{ arr };\n        static_assert(std::is_same<decltype(maybe_owned), lz::detail::maybe_owned_impl<int[3], false>>::value,\n                      \"Should be by ref\");\n        REQUIRE(&(*arr) == &(*maybe_owned.begin()));\n    }\n\n    SUBCASE(\"Volatile C array\") {\n        volatile int arr[] = { 1, 2, 3 };\n        lz::maybe_owned<volatile int[3]> maybe_owned{ arr };\n        static_assert(std::is_same<decltype(maybe_owned), lz::detail::maybe_owned_impl<volatile int[3], false>>::value,\n                      \"Should be by ref\");\n        REQUIRE(&(*arr) == &(*maybe_owned.begin()));\n    }\n\n    SUBCASE(\"Const C array\") {\n        const int arr[] = { 1, 2, 3 };\n        lz::maybe_owned<const int[3]> maybe_owned{ arr };\n        REQUIRE(&(*arr) == &(*maybe_owned.begin()));\n        static_assert(std::is_same<decltype(maybe_owned), lz::detail::maybe_owned_impl<const int[3], false>>::value,\n                      \"Should be by ref\");\n    }\n\n    SUBCASE(\"Const volatile C array\") {\n        const volatile int arr[] = { 1, 2, 3 };\n        lz::maybe_owned<const volatile int[3]> maybe_owned{ arr };\n        REQUIRE(&(*arr) == &(*maybe_owned.begin()));\n        static_assert(std::is_same<decltype(maybe_owned), lz::detail::maybe_owned_impl<const volatile int[3], false>>::value,\n                      \"Should be by ref\");\n    }\n}\n\nstruct iterable {\n    int* _begin;\n    int* _end;\n\n    int* begin() {\n        return _begin;\n    }\n\n    int* end() {\n        return _end;\n    }\n};\n\nTEST_CASE(\"lz::copied\") {\n    SUBCASE(\"Iterable that is inherited by lz::lazy_view\") {\n        std::vector<int> vec{ 1, 2, 3 };\n        auto filter = lz::filter(vec, [](int i) { return i > 1; });\n        auto copied = lz::as_copied(filter);\n        REQUIRE(&(*filter.begin()) == &(*copied.begin()));\n    }\n\n    SUBCASE(\"Iterable that is not inherited by lz::lazy_view\") {\n        std::vector<int> vec{ 1, 2, 3 };\n        auto copied = lz::as_copied(vec);\n        REQUIRE(&(*vec.begin()) != &(*copied.begin()));\n    }\n\n    SUBCASE(\"Custom iterable\") {\n        int arr[] = { 1, 2, 3 };\n        iterable it{ arr, arr + lz::size(arr) };\n        lz::copied<iterable> copied = lz::as_copied(it);\n        REQUIRE(&(*it.begin()) == &(*copied.begin()));\n    }\n}\n"
  },
  {
    "path": "tests/module_tests/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28)\nproject(ModuleTests LANGUAGES CXX)\n\ninclude(FetchContent)\n\noption(CPP_LAZY_USE_MODULES_FIND_PACKAGE \"Use find_package to locate cpp-lazy\" OFF)\n\nif (CPP_LAZY_USE_MODULES_FIND_PACKAGE)\n    find_package(cpp-lazy REQUIRED CONFIG)\nelse()\n    FetchContent_Declare(cpp-lazy SOURCE_DIR \"${CMAKE_CURRENT_LIST_DIR}/../..\")\n    FetchContent_MakeAvailable(cpp-lazy)\nendif()\n\nadd_executable(module_test module_test.cpp)\ntarget_link_libraries(module_test PRIVATE cpp-lazy::cpp-lazy)\n"
  },
  {
    "path": "tests/module_tests/module_test.cpp",
    "content": "import lz;\n\n#include <iostream>\n#include <vector>\n\nint main() {\n    // Example usage of cpp-lazy\n    std::vector<int> vec = { 1, 2, 3, 4, 5 };\n    auto doubled = vec | lz::map([](int i) { return i * 2; });\n    std::cout << doubled << std::endl; // Output: 2, 4, 6, 8, 10\n    std::cout << \"Size = \" << lz::size(doubled) << std::endl; // Output: Size= 5\n\n    for (const auto& val : doubled) {\n        std::cout << val << \" \"; // Output: 2 4 6 8 10\n    }\n    std::cout << std::endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/pairwise.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/cached_size.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/pairwise.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> lst = { 1, 2, 3, 4, 5 };\n        auto common = make_sentinel_assign_op_tester(lz::pairwise(lst, 3));\n        using value_type = lz::detail::val_iterable_t<decltype(common)>;\n        std::vector<std::vector<int>> expected = { { 1, 2, 3 }, { 2, 3, 4 }, { 3, 4, 5 } };\n        REQUIRE(lz::equal(common, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5 };\n        auto bidi_sentinelled = make_sized_bidi_sentinelled(lst);\n        auto common = make_sentinel_assign_op_tester(lz::pairwise(bidi_sentinelled, 3));\n        using value_type = lz::detail::val_iterable_t<decltype(common)>;\n        std::vector<std::vector<int>> expected = { { 1, 2, 3 }, { 2, 3, 4 }, { 3, 4, 5 } };\n        REQUIRE(lz::equal(common, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse,\n                          [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n\n    SUBCASE(\"random access\") {\n        auto repeater = lz::repeat(20, 5);\n        auto common = make_sentinel_assign_op_tester(lz::pairwise(repeater, 3));\n        using value_type2 = lz::detail::val_iterable_t<decltype(common)>;\n\n        std::vector<std::vector<int>> expected = { { 20, 20, 20 }, { 20, 20, 20 }, { 20, 20, 20 } };\n        test_procs::test_operator_plus(common, expected,\n                                       [](value_type2 a, const std::vector<int>& b) { return lz::equal(a, b); });\n        test_procs::test_operator_minus(common);\n    }\n}\n\nTEST_CASE(\"Empty or one element\") {\n    SUBCASE(\"Empty vector\") {\n        std::vector<int> vec;\n        auto it = lz::pairwise(vec, 2);\n        REQUIRE(lz::size(it) == 0);\n        REQUIRE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::has_one(it));\n        REQUIRE(lz::distance(it) == 0);\n    }\n\n    SUBCASE(\"Single element vector 1\") {\n        std::vector<int> vec{ 1 };\n        auto it = lz::pairwise(vec, 1);\n        static_assert(!lz::detail::has_sentinel<decltype(vec)>::value, \"\");\n        REQUIRE(lz::size(it) == 1);\n        REQUIRE(lz::has_one(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::empty(it));\n        auto expected = { 1 };\n        REQUIRE(lz::equal(*it.begin(), expected));\n        REQUIRE(lz::distance(it) == 1);\n    }\n\n    SUBCASE(\"Single element vector 2\") {\n        std::vector<int> vec{ 1 };\n        auto it = lz::pairwise(vec, 2);\n        REQUIRE(lz::size(it) == 0);\n        REQUIRE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::has_one(it));\n        REQUIRE(lz::distance(it) == 0);\n    }\n\n    SUBCASE(\"Two elements vector 2\") {\n        std::vector<int> vec{ 1, 2 };\n        auto it = lz::pairwise(vec, 2);\n        REQUIRE(lz::size(it) == 1);\n        REQUIRE_FALSE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE(lz::has_one(it));\n        auto expected = { 1, 2 };\n        REQUIRE(lz::equal(*it.begin(), expected));\n        REQUIRE(lz::distance(it) == 1);\n    }\n\n    SUBCASE(\"Forward non sized sentinelled\") {\n        auto cstr = lz::c_string(\"\");\n        auto it = lz::pairwise(cstr, 2);\n        static_assert(lz::detail::has_sentinel<decltype(cstr)>::value, \"\");\n        REQUIRE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::has_one(it));\n    }\n\n    SUBCASE(\"Forward non sized sentinelled 1\") {\n        auto cstr = lz::c_string(\"a\");\n        auto it = lz::pairwise(cstr, 1);\n        REQUIRE_FALSE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE(lz::has_one(it));\n        auto expected = { 'a' };\n        REQUIRE(lz::equal(*it.begin(), expected));\n    }\n\n    SUBCASE(\"Forward non sized sentinelled 2\") {\n        auto cstr = lz::c_string(\"a\");\n        auto it = lz::pairwise(cstr, 2);\n        REQUIRE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::has_one(it));\n    }\n\n    SUBCASE(\"Two elements forward non sized sentinelled\") {\n        auto cstr = lz::c_string(\"ab\");\n        auto it = lz::pairwise(cstr, 2);\n        REQUIRE_FALSE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE(lz::has_one(it));\n        auto expected = { 'a', 'b' };\n        REQUIRE(lz::equal(*it.begin(), expected));\n    }\n\n    SUBCASE(\"Empty sized bidi iterable\") {\n        std::list<int> lst;\n        auto it = lz::pairwise(lst, 2);\n        REQUIRE(lz::size(it) == 0);\n        REQUIRE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::has_one(it));\n        REQUIRE(lz::distance(it) == 0);\n    }\n\n    SUBCASE(\"Single element sized bidi iterable 1\") {\n        std::list<int> lst{ 1 };\n        auto it = lz::pairwise(lst, 1);\n        REQUIRE(lz::size(it) == 1);\n        REQUIRE(lz::has_one(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::empty(it));\n        auto expected = { 1 };\n        REQUIRE(lz::equal(*it.begin(), expected));\n        REQUIRE(lz::distance(it) == 1);\n    }\n\n    SUBCASE(\"Single element sized bidi iterable 2\") {\n        std::list<int> lst{ 1 };\n        auto it = lz::pairwise(lst, 2);\n        REQUIRE(lz::size(it) == 0);\n        REQUIRE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::has_one(it));\n    }\n\n    SUBCASE(\"Two elements sized bidi iterable 2\") {\n        std::list<int> lst{ 1, 2 };\n        auto it = lz::pairwise(lst, 2);\n        REQUIRE(lz::size(it) == 1);\n        REQUIRE_FALSE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE(lz::has_one(it));\n        auto expected = { 1, 2 };\n        REQUIRE(lz::equal(*it.begin(), expected));\n    }\n\n    SUBCASE(\"Empty non sized bidi iterable\") {\n        std::vector<int> vec;\n        auto it = lz::pairwise(vec | lz::filter([](int) { return true; }), 2);\n        static_assert(!lz::detail::has_sentinel<decltype(vec)>::value, \"\");\n        REQUIRE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::has_one(it));\n        REQUIRE(lz::distance(it) == 0);\n    }\n\n    SUBCASE(\"Single element non sized bidi iterable 1\") {\n        std::vector<int> vec{ 1 };\n        auto it = lz::pairwise(vec | lz::filter([](int) { return true; }), 1);\n        REQUIRE(lz::has_one(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::empty(it));\n        auto expected = { 1 };\n        REQUIRE(lz::equal(*it.begin(), expected));\n        REQUIRE(lz::distance(it) == 1);\n    }\n\n    SUBCASE(\"Single element non sized bidi iterable 2\") {\n        std::vector<int> vec{ 1 };\n        auto it = lz::pairwise(vec | lz::filter([](int) { return true; }), 2);\n        REQUIRE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE_FALSE(lz::has_one(it));\n        REQUIRE(lz::distance(it) == 0);\n    }\n\n    SUBCASE(\"Two elements non sized bidi iterable 2\") {\n        std::vector<int> vec{ 1, 2 };\n        auto it = lz::pairwise(vec | lz::filter([](int) { return true; }), 2);\n        REQUIRE_FALSE(lz::empty(it));\n        REQUIRE_FALSE(lz::has_many(it));\n        REQUIRE(lz::has_one(it));\n        auto expected = { 1, 2 };\n        REQUIRE(lz::equal(*it.begin(), expected));\n        REQUIRE(lz::distance(it) == 1);\n    }\n}\n\nTEST_CASE(\"pairwise non sentinelled iterator random access\") {\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        std::vector<int> v;\n        auto p = lz::pairwise(v, 1);\n        auto it = p.begin();\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n\n        v = { 1 };\n        p = lz::pairwise(v, 1);\n        it = p.begin();\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n\n        p = lz::pairwise(v, 2);\n        it = p.begin();\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n    }\n\n    std::vector<int> vec{ 1, 2, 3, 4, 5 };\n\n    SUBCASE(\"Forward\") {\n        std::vector<std::vector<int>> expected = { { 1 }, { 2 }, { 3 }, { 4 }, { 5 } };\n        auto it = lz::pairwise(vec, 1);\n        using value_type = typename decltype(it.begin())::value_type;\n\n        REQUIRE(lz::size(it) == vec.size());\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 } };\n        it = lz::pairwise(vec, 2);\n        REQUIRE(lz::size(it) == vec.size() - 1);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2, 3 }, { 2, 3, 4 }, { 3, 4, 5 } };\n        it = vec | lz::pairwise(3);\n        REQUIRE(lz::size(it) == vec.size() - 2);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2, 3, 4 }, { 2, 3, 4, 5 } };\n        it = vec | lz::pairwise(4);\n        REQUIRE(lz::size(it) == vec.size() - 3);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2, 3, 4, 5 } };\n        it = vec | lz::pairwise(5);\n        REQUIRE(lz::size(it) == vec.size() - 4);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n\n    SUBCASE(\"Backward\") {\n        std::vector<std::vector<int>> expected = { { 5 }, { 4 }, { 3 }, { 2 }, { 1 } };\n        auto it = lz::pairwise(vec, 1) | lz::reverse;\n        using value_type = typename decltype(it.begin())::value_type;\n\n        REQUIRE(lz::size(it) == vec.size());\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 4, 5 }, { 3, 4 }, { 2, 3 }, { 1, 2 } };\n        it = lz::pairwise(vec, 2) | lz::reverse;\n        REQUIRE(lz::size(it) == vec.size() - 1);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 3, 4, 5 }, { 2, 3, 4 }, { 1, 2, 3 } };\n        it = vec | lz::pairwise(3) | lz::reverse;\n        REQUIRE(lz::size(it) == vec.size() - 2);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 2, 3, 4, 5 }, { 1, 2, 3, 4 } };\n        it = vec | lz::pairwise(4) | lz::reverse;\n        REQUIRE(lz::size(it) == vec.size() - 3);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2, 3, 4, 5 } };\n        it = vec | lz::pairwise(5) | lz::reverse;\n        REQUIRE(lz::size(it) == vec.size() - 4);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n\n    SUBCASE(\"Operator+/-\") {\n        auto it = lz::pairwise(vec, 1);\n        using value_type = typename decltype(it.begin())::value_type;\n        const auto eq_compare = [](value_type a, const std::vector<int>& b) {\n            return lz::equal(a, b);\n        };\n\n        std::vector<std::vector<int>> expected = { { 1 }, { 2 }, { 3 }, { 4 }, { 5 } };\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n\n        expected = { { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 } };\n        it = lz::pairwise(vec, 2);\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n\n        expected = { { 1, 2, 3 }, { 2, 3, 4 }, { 3, 4, 5 } };\n        it = vec | lz::pairwise(3);\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n\n        expected = { { 1, 2, 3, 4 }, { 2, 3, 4, 5 } };\n        it = vec | lz::pairwise(4);\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n\n        expected = { { 1, 2, 3, 4, 5 } };\n        it = vec | lz::pairwise(5);\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n    }\n}\n\nTEST_CASE(\"pairwise sentinelled iterator random access\") {\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        auto rep = lz::repeat(1, 0);\n        auto p = lz::pairwise(rep, 1);\n        auto it = p.begin();\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n\n        rep = lz::repeat(1, 1);\n        p = lz::pairwise(rep, 1);\n        it = p.begin();\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n\n        p = lz::pairwise(rep, 2);\n        it = p.begin();\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n    }\n\n    auto repeater = lz::repeat(1, 5);\n\n    SUBCASE(\"Forward\") {\n        std::vector<std::vector<int>> expected = { { 1 }, { 1 }, { 1 }, { 1 }, { 1 } };\n        auto it = lz::pairwise(repeater, 1);\n        static_assert(lz::detail::has_sentinel<decltype(repeater)>::value, \"\");\n        using value_type = typename decltype(it.begin())::value_type;\n\n        REQUIRE(lz::size(it) == repeater.size());\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 } };\n        it = lz::pairwise(repeater, 2);\n        REQUIRE(lz::size(it) == repeater.size() - 1);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } };\n        it = repeater | lz::pairwise(3);\n        REQUIRE(lz::size(it) == repeater.size() - 2);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 1, 1, 1 }, { 1, 1, 1, 1 } };\n        it = repeater | lz::pairwise(4);\n        REQUIRE(lz::size(it) == repeater.size() - 3);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 1, 1, 1, 1 } };\n        it = repeater | lz::pairwise(5);\n        REQUIRE(lz::size(it) == repeater.size() - 4);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n\n    SUBCASE(\"Operator+/-\") {\n        auto it = lz::pairwise(repeater, 1);\n        using value_type = typename decltype(it.begin())::value_type;\n        const auto eq_compare = [](value_type a, const std::vector<int>& b) {\n            return lz::equal(a, b);\n        };\n\n        std::vector<std::vector<int>> expected = { { 1 }, { 1 }, { 1 }, { 1 }, { 1 } };\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n\n        expected = { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 } };\n        it = lz::pairwise(repeater, 2);\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n\n        expected = { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } };\n        it = repeater | lz::pairwise(3);\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n\n        expected = { { 1, 1, 1, 1 }, { 1, 1, 1, 1 } };\n        it = repeater | lz::pairwise(4);\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n\n        expected = { { 1, 1, 1, 1, 1 } };\n        it = repeater | lz::pairwise(5);\n        test_procs::test_operator_plus(it, expected, eq_compare);\n        test_procs::test_operator_minus(it);\n    }\n}\n\nTEST_CASE(\"Pairwise non sentinelled bidi sized\") {\n    std::list<int> lst{ 1, 2, 3, 4, 5 };\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        auto p = lz::pairwise(lst, 1);\n        auto it = p.begin();\n        REQUIRE(it == p.begin());\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n        it = p.end();\n        REQUIRE(it == p.end());\n\n        lst = { 1 };\n        p = lz::pairwise(lst, 1);\n        it = p.begin();\n        REQUIRE(it == p.begin());\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n        it = p.end();\n        REQUIRE(it == p.end());\n\n        p = lz::pairwise(lst, 2);\n        it = p.begin();\n        REQUIRE(it == p.begin());\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n        it = p.end();\n        REQUIRE(it == p.end());\n    }\n\n    SUBCASE(\"Forward\") {\n        std::vector<std::vector<int>> expected = { { 1 }, { 2 }, { 3 }, { 4 }, { 5 } };\n        auto it = lz::pairwise(lst, 1);\n        using value_type = typename decltype(it.begin())::value_type;\n\n        REQUIRE(lz::size(it) == lst.size());\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 } };\n        it = lz::pairwise(lst, 2);\n        REQUIRE(lz::size(it) == lst.size() - 1);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2, 3 }, { 2, 3, 4 }, { 3, 4, 5 } };\n        it = lst | lz::pairwise(3);\n        REQUIRE(lz::size(it) == lst.size() - 2);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2, 3, 4 }, { 2, 3, 4, 5 } };\n        it = lst | lz::pairwise(4);\n        REQUIRE(lz::size(it) == lst.size() - 3);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2, 3, 4, 5 } };\n        it = lst | lz::pairwise(5);\n        REQUIRE(lz::size(it) == lst.size() - 4);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n\n    SUBCASE(\"Backward\") {\n        std::vector<std::vector<int>> expected = { { 5 }, { 4 }, { 3 }, { 2 }, { 1 } };\n        auto it = lz::pairwise(lst, 1) | lz::reverse;\n        using value_type = typename decltype(it.begin())::value_type;\n\n        REQUIRE(lz::size(it) == lst.size());\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 4, 5 }, { 3, 4 }, { 2, 3 }, { 1, 2 } };\n        it = lz::pairwise(lst, 2) | lz::reverse;\n        REQUIRE(lz::size(it) == lst.size() - 1);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 3, 4, 5 }, { 2, 3, 4 }, { 1, 2, 3 } };\n        it = lst | lz::pairwise(3) | lz::reverse;\n        REQUIRE(lz::size(it) == lst.size() - 2);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 2, 3, 4, 5 }, { 1, 2, 3, 4 } };\n        it = lst | lz::pairwise(4) | lz::reverse;\n        REQUIRE(lz::size(it) == lst.size() - 3);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n\n        expected = { { 1, 2, 3, 4, 5 } };\n        it = lst | lz::pairwise(5) | lz::reverse;\n        REQUIRE(lz::size(it) == lst.size() - 4);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n}\n\nTEST_CASE(\"Non sized fwd sentinelled\") {\n    auto cstr = lz::c_string(\"abcde\");\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        auto p = lz::pairwise(cstr, 1);\n        auto it = p.begin();\n        REQUIRE(it == p.begin());\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n\n        cstr = lz::c_string(\"a\");\n        p = lz::pairwise(cstr, 1);\n        it = p.begin();\n        REQUIRE(it == p.begin());\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n\n        p = lz::pairwise(cstr, 2);\n        it = p.begin();\n        REQUIRE(it == p.begin());\n        it = lz::default_sentinel;\n        REQUIRE(it == p.end());\n        REQUIRE(p.end() == it);\n    }\n\n    SUBCASE(\"Forward\") {\n        std::vector<std::vector<char>> expected = { { 'a' }, { 'b' }, { 'c' }, { 'd' }, { 'e' } };\n        const std::size_t cstr_size = lz::eager_size(cstr);\n        auto it = lz::pairwise(cstr, 1);\n        using value_type = typename decltype(it.begin())::value_type;\n\n        REQUIRE(lz::eager_size(it) == cstr_size);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<char>& b) { return lz::equal(a, b); }));\n\n        expected = { { 'a', 'b' }, { 'b', 'c' }, { 'c', 'd' }, { 'd', 'e' } };\n        it = lz::pairwise(cstr, 2);\n        REQUIRE(lz::eager_size(it) == cstr_size - 1);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<char>& b) { return lz::equal(a, b); }));\n\n        expected = { { 'a', 'b', 'c' }, { 'b', 'c', 'd' }, { 'c', 'd', 'e' } };\n        it = cstr | lz::pairwise(3);\n        REQUIRE(lz::eager_size(it) == cstr_size - 2);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<char>& b) { return lz::equal(a, b); }));\n\n        expected = { { 'a', 'b', 'c', 'd' }, { 'b', 'c', 'd', 'e' } };\n        it = cstr | lz::pairwise(4);\n        REQUIRE(lz::eager_size(it) == cstr_size - 3);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<char>& b) { return lz::equal(a, b); }));\n\n        expected = { { 'a', 'b', 'c', 'd', 'e' } };\n        it = cstr | lz::pairwise(5);\n        REQUIRE(lz::eager_size(it) == cstr_size - 4);\n        REQUIRE(lz::equal(it, expected, [](value_type a, const std::vector<char>& b) { return lz::equal(a, b); }));\n    }\n}\n\nTEST_CASE(\"Non sized fwd non sentinelled\") {\n    std::forward_list<int> flst{ 1, 2, 3, 4, 5 };\n    auto pairwise = lz::pairwise(flst, 2);\n\n    SUBCASE(\"Forward\") {\n        std::vector<std::vector<int>> expected = { { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 } };\n        using value_type = typename decltype(pairwise.begin())::value_type;\n\n        REQUIRE(lz::equal(pairwise, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n    }\n}\n\nTEST_CASE(\"bidi sized sentinel\") {\n    std::list<int> list{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n    auto filtered = list | lz::filter([](int i) { return i % 2 == 0; }) | lz::cache_size;\n    auto t = make_sized_bidi_sentinelled(filtered);\n    auto pw = lz::pairwise(t, 2);\n    std::vector<std::vector<int>> expected = { { 2, 4 }, { 4, 6 }, { 6, 8 }, { 8, 10 } };\n    using value_type = typename decltype(pw.begin())::value_type;\n    REQUIRE(lz::equal(pw, expected, [](value_type a, const std::vector<int>& b) { return lz::equal(a, b); }));\n}\n"
  },
  {
    "path": "tests/piping.cpp",
    "content": "#include <Lz/algorithm/equal.hpp>\n#include <Lz/chunk_if.hpp>\n#include <Lz/drop.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/range.hpp>\n#include <Lz/stream.hpp>\n#include <Lz/take.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Iterator chaining\") {\n    SUBCASE(\"test 1\") {\n        unsigned dummy = 0;\n        auto iterable = lz::range(unsigned(10)) | lz::map([dummy](unsigned i) mutable {\n                            ++dummy;\n                            return std::to_string(i);\n                        }) |\n                        lz::take(5) | lz::drop(1) | lz::filter([](const std::string& str) { return std::isdigit(str[0]); });\n        std::array<std::string, 4> expected = { \"1\", \"2\", \"3\", \"4\" };\n        REQUIRE(lz::equal(iterable, expected));\n    }\n\n    SUBCASE(\"test 2\") {\n        unsigned dummy = 0;\n        auto chunk_iterable = lz::range(unsigned(10)) | lz::chunk_if([dummy](unsigned i) mutable {\n                                  ++dummy;\n                                  return i % 2 == 0;\n                              });\n        using chunk_type = typename decltype(chunk_iterable.begin())::reference;\n        auto mapped_chunk = chunk_iterable | lz::map([](chunk_type chunk) { return lz::format(chunk); }) | lz::take(3);\n        std::vector<std::string> expected = { \"\", \"1\", \"3\" };\n        REQUIRE(lz::equal(mapped_chunk, expected));\n    }\n\n    SUBCASE(\"test 3, with to<>\") {\n        unsigned dummy = 0;\n        auto iterable = lz::range(unsigned(10)) | lz::map([dummy](unsigned i) mutable {\n                            ++dummy;\n                            return std::to_string(i);\n                        }) |\n                        lz::take(5) | lz::drop(1) | lz::filter([](const std::string& str) { return std::isdigit(str[0]); }) |\n                        lz::to<std::vector>();\n        std::array<std::string, 4> expected = { \"1\", \"2\", \"3\", \"4\" };\n        REQUIRE(lz::equal(iterable, expected));\n    }\n\n    SUBCASE(\"test 4, with to<>\") {\n        auto chunk_iterable = lz::range(unsigned(10)) | lz::chunk_if([](unsigned i) { return i % 2 == 0; });\n        unsigned dummy = 0;\n\n        using chunk_type = typename decltype(chunk_iterable.begin())::reference;\n        auto mapped_chunk = chunk_iterable | lz::map([dummy](chunk_type chunk) mutable {\n                                ++dummy;\n                                return lz::format(chunk);\n                            }) |\n                            lz::take(3) | lz::to<std::vector>();\n        std::vector<std::string> expected = { \"\", \"1\", \"3\" };\n        REQUIRE(lz::equal(mapped_chunk, expected));\n    }\n}\n"
  },
  {
    "path": "tests/random.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/all_of.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/random.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n#include <random>\n\nTEST_CASE(\"random_iterable should be random\") {\n    constexpr std::size_t size = 5;\n\n    SUBCASE(\"random_iterable doubles\") {\n        constexpr long double start = 0;\n        constexpr long double end = 1;\n        const auto random_array = lz::random(start, end, size) | lz::to<std::array<long double, size>>();\n        REQUIRE(random_array.size() == size);\n        auto randomArray2 = lz::random(start, end, size) | lz::to<std::array<long double, size>>();\n        while (random_array == randomArray2) {\n            randomArray2 = lz::random(start, end, size) | lz::to<std::array<long double, size>>();\n        }\n        REQUIRE(random_array != randomArray2);\n    }\n\n    SUBCASE(\"random_iterable ints\") {\n        const auto random_array = lz::random((std::numeric_limits<int>::min)(), (std::numeric_limits<int>::max)(), size) |\n                                  lz::to<std::array<int, size>>();\n        const auto random_array_2 = lz::random((std::numeric_limits<int>::min)(), (std::numeric_limits<int>::max)(), size) |\n                                    lz::to<std::array<int, size>>();\n        REQUIRE(random_array != random_array_2);\n    }\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        auto random = lz::random(1, 10, 5);\n        auto common = make_sentinel_assign_op_tester(random);\n\n        REQUIRE(lz::size(common) == 5);\n        auto dummy = { 0, 0, 0, 0, 0 };\n        REQUIRE(lz::equal(common, dummy, [](int a, int) { return a <= 10 && a >= 0; }));\n        REQUIRE(lz::equal(common | lz::reverse, dummy, [](int a, int) { return a <= 10 && a >= 0; }));\n        test_procs::test_operator_minus(common);\n        test_procs::test_operator_plus(common, dummy, [](int a, int) { return a <= 10 && a >= 0; });\n    }\n}\n\nTEST_CASE(\"random_iterable with custom distro's and custom engine\") {\n    std::random_device rd;\n    std::mt19937_64 gen(rd());\n    std::poisson_distribution<> d(500000);\n    lz::random_iterable<int, std::poisson_distribution<>, std::mt19937_64> r = lz::random(d, gen, 3);\n    static_assert(!std::is_same<decltype(r.begin()), decltype(r.end())>::value, \"Should not be the same\");\n    REQUIRE(lz::distance(r) == 3);\n\n    const auto current_rand = *r.begin();\n    auto next_rand = *r.begin();\n    while (current_rand == next_rand) {\n        next_rand = *r.begin();\n    }\n    REQUIRE(current_rand != next_rand);\n}\n\nTEST_CASE(\"Empty or one element random\") {\n    SUBCASE(\"Empty random\") {\n        lz::random_iterable<int, std::uniform_int_distribution<>, std::mt19937> r = lz::random(0, 0, 0);\n        REQUIRE(lz::empty(r));\n    }\n\n    SUBCASE(\"One element random\") {\n        auto r = lz::random(0, 0, 1);\n        REQUIRE_FALSE(lz::empty(r));\n        REQUIRE(lz::has_one(r));\n        REQUIRE_FALSE(lz::has_many(r));\n    }\n}\n\nTEST_CASE(\"random_iterable binary operations\") {\n    constexpr std::ptrdiff_t size = 5;\n    lz::common_random_iterable<double, std::uniform_real_distribution<>, std::mt19937> random = lz::common_random(0., 1., size);\n    static_assert(std::is_same<decltype(random.begin()), decltype(random.end())>::value, \"Should be the same\");\n\n    SUBCASE(\"Operator++\") {\n        auto it = random.begin();\n        REQUIRE(lz::distance(it, random.end()) == 5);\n        ++it;\n        REQUIRE(*it >= 0.);\n        REQUIRE(*it <= 1.);\n        REQUIRE(lz::distance(it, random.end()) == 4);\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto rev = lz::reverse(random);\n        REQUIRE(lz::equal(rev, random, [](double a, double) { return a >= 0. && a <= 1.; }));\n        REQUIRE(rev.size() == size);\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        auto it = random.begin();\n        REQUIRE(it != random.end());\n        REQUIRE(it == random.begin());\n        REQUIRE(random.end() != it);\n        REQUIRE(random.begin() == it);\n\n        while (it != random.end()) {\n            ++it;\n        }\n\n        REQUIRE(it == random.end());\n        REQUIRE(it != random.begin());\n        REQUIRE(random.end() == it);\n        REQUIRE(random.begin() != it);\n    }\n\n    SUBCASE(\"Operator*\") {\n        auto it = random.begin();\n        REQUIRE(*it >= 0.);\n        REQUIRE(*it <= 1.);\n    }\n\n    SUBCASE(\"Operator+\") {\n        std::vector<double> dummy = { 1., 1., 1., 1., 1. };\n        test_procs::test_operator_plus(random, dummy, [](double a, double) { return a >= 0. && a <= 1.; });\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(random);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto rand = lz::random(0., 1., size);\n        test_procs::test_operator_minus(rand);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto rand = lz::random(0., 1., size);\n        std::vector<double> dummy(size, 0.);\n        test_procs::test_operator_plus(rand, dummy, [](double a, double) { return a >= 0. && a <= 1.; });\n    }\n}\n\nTEST_CASE(\"random_iterable to containers\") {\n    constexpr std::size_t size = 10;\n    lz::default_random_iterable<double> range = lz::random(0., 1., size);\n\n    SUBCASE(\"To array\") {\n        REQUIRE((range | lz::to<std::array<double, size>>()).size() == size);\n        REQUIRE(lz::all_of(range, [](double val) { return val >= 0. && val <= 1.; }));\n    }\n\n    SUBCASE(\"To vector\") {\n        REQUIRE((range | lz::to<std::vector>()).size() == size);\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        REQUIRE((range | lz::to<std::list>()).size() == size);\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = range | lz::map([](const double i) { return std::make_pair(i, i); }) | lz::to<std::map<double, double>>();\n        REQUIRE(actual.size() == size);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto actual =\n            range | lz::map([](const double i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<double, double>>();\n        REQUIRE(actual.size() == size);\n    }\n}\n"
  },
  {
    "path": "tests/range.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/range.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <doctest/doctest.h>\n\nauto compare = [](double a, doctest::Approx b) {\n    return a == b;\n};\n\nTEST_CASE(\"Range permutations\") {\n    SUBCASE(\"1 step, int\") {\n        auto range = lz::range(0, 10, 1);\n        std::vector<int> expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n        REQUIRE(lz::equal(range, expected));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse));\n\n        range = lz::range(9, -1, -1);\n        expected = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };\n        REQUIRE(lz::equal(range, expected));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"2 steps, int\") {\n        auto range = lz::range(0, 10, 2);\n        std::vector<int> expected = { 0, 2, 4, 6, 8 };\n        REQUIRE(lz::equal(range, expected));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse));\n\n        range = lz::range(8, -2, -2);\n        expected = { 8, 6, 4, 2, 0 };\n        REQUIRE(lz::equal(range, expected));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"3 steps, int\") {\n        auto range = lz::range(0, 10, 3);\n        std::vector<int> expected = { 0, 3, 6, 9 };\n        REQUIRE(lz::equal(range, expected));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse));\n\n        range = lz::range(9, -3, -3);\n        expected = { 9, 6, 3, 0 };\n        REQUIRE(lz::equal(range, expected));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"1 step double\") {\n        auto range = lz::range(0., 10., 1.);\n        std::vector<doctest::Approx> expected = { doctest::Approx(0), doctest::Approx(1), doctest::Approx(2), doctest::Approx(3),\n                                                  doctest::Approx(4), doctest::Approx(5), doctest::Approx(6), doctest::Approx(7),\n                                                  doctest::Approx(8), doctest::Approx(9) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n\n        range = lz::range(9., -1., -1.);\n        expected = { doctest::Approx(9), doctest::Approx(8), doctest::Approx(7), doctest::Approx(6), doctest::Approx(5),\n                     doctest::Approx(4), doctest::Approx(3), doctest::Approx(2), doctest::Approx(1), doctest::Approx(0) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n    }\n\n    SUBCASE(\"0.5 step double\") {\n        auto range = lz::range(0., 5.6, 0.5);\n        std::vector<doctest::Approx> expected = { doctest::Approx(0),   doctest::Approx(0.5), doctest::Approx(1),\n                                                  doctest::Approx(1.5), doctest::Approx(2),   doctest::Approx(2.5),\n                                                  doctest::Approx(3),   doctest::Approx(3.5), doctest::Approx(4),\n                                                  doctest::Approx(4.5), doctest::Approx(5),   doctest::Approx(5.5) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n\n        range = lz::range(5., -0.5, -0.5);\n        expected = { doctest::Approx(5), doctest::Approx(4.5), doctest::Approx(4), doctest::Approx(3.5),\n                     doctest::Approx(3), doctest::Approx(2.5), doctest::Approx(2), doctest::Approx(1.5),\n                     doctest::Approx(1), doctest::Approx(0.5), doctest::Approx(0) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n    }\n\n    SUBCASE(\"2.5 step double\") {\n        auto range = lz::range(0., 5.5, 2.5);\n        std::vector<doctest::Approx> expected = { doctest::Approx(0), doctest::Approx(2.5), doctest::Approx(5) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n\n        range = lz::range(2.5, -7.5, -2.5);\n        expected = { doctest::Approx(2.5), doctest::Approx(0), doctest::Approx(-2.5), doctest::Approx(-5.) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n    }\n\n    SUBCASE(\"No step specified int\") {\n        auto range = lz::range(10);\n        std::vector<int> expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n        REQUIRE(lz::equal(range, expected));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse));\n\n        range = lz::range(-1, 10);\n        expected = { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n        REQUIRE(lz::equal(range, expected));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"No step specified double\") {\n        auto range = lz::range(10.5);\n        std::vector<doctest::Approx> expected = { doctest::Approx(0), doctest::Approx(1), doctest::Approx(2), doctest::Approx(3),\n                                                  doctest::Approx(4), doctest::Approx(5), doctest::Approx(6), doctest::Approx(7),\n                                                  doctest::Approx(8), doctest::Approx(9), doctest::Approx(10) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n\n        range = lz::range(10.);\n        expected = { doctest::Approx(0), doctest::Approx(1), doctest::Approx(2), doctest::Approx(3), doctest::Approx(4),\n                     doctest::Approx(5), doctest::Approx(6), doctest::Approx(7), doctest::Approx(8), doctest::Approx(9) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n\n        range = lz::range(-1., 10.5);\n        expected = { doctest::Approx(-1.), doctest::Approx(0), doctest::Approx(1), doctest::Approx(2),\n                     doctest::Approx(3),   doctest::Approx(4), doctest::Approx(5), doctest::Approx(6),\n                     doctest::Approx(7),   doctest::Approx(8), doctest::Approx(9), doctest::Approx(10) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n\n        range = lz::range(-1., 10.);\n        expected = { doctest::Approx(-1.), doctest::Approx(0), doctest::Approx(1), doctest::Approx(2),\n                     doctest::Approx(3),   doctest::Approx(4), doctest::Approx(5), doctest::Approx(6),\n                     doctest::Approx(7),   doctest::Approx(8), doctest::Approx(9) };\n        REQUIRE(lz::equal(range, expected, compare));\n        REQUIRE(lz::equal(range | lz::reverse, expected | lz::reverse, compare));\n    }\n\n    SUBCASE(\"Empty range\") {\n        auto range = lz::range(0, 0, 10);\n        REQUIRE(lz::empty(range));\n        REQUIRE(lz::size(range) == 0);\n        REQUIRE(!lz::has_many(range));\n        REQUIRE(!lz::has_one(range));\n\n        auto range2 = lz::range(0, 0);\n        REQUIRE(lz::empty(range2));\n        REQUIRE(lz::size(range2) == 0);\n        REQUIRE(!lz::has_many(range2));\n        REQUIRE(!lz::has_one(range2));\n    }\n}\n\nTEST_CASE(\"Binary operations\") {\n    SUBCASE(\"With step, int uneven\") {\n        lz::stepwise_range_iterable<int> range = lz::range(0, 10, 3);\n        std::vector<int> expected = { 0, 3, 6, 9 };\n        test_procs::test_operator_plus(range, expected);\n        test_procs::test_operator_minus(range);\n    }\n\n    SUBCASE(\"With step, int even\") {\n        auto range = lz::range(0, 10, 2);\n        std::vector<int> expected = { 0, 2, 4, 6, 8 };\n        test_procs::test_operator_plus(range, expected);\n        test_procs::test_operator_minus(range);\n    }\n\n    SUBCASE(\"With step, double\") {\n        auto range = lz::range(0., 5.5, 0.5);\n        std::vector<doctest::Approx> expected = { doctest::Approx(0),   doctest::Approx(0.5), doctest::Approx(1),\n                                                  doctest::Approx(1.5), doctest::Approx(2),   doctest::Approx(2.5),\n                                                  doctest::Approx(3),   doctest::Approx(3.5), doctest::Approx(4),\n                                                  doctest::Approx(4.5), doctest::Approx(5) };\n        test_procs::test_operator_plus(range, expected, compare);\n        test_procs::test_operator_minus(range);\n    }\n\n    SUBCASE(\"Without step, int\") {\n        lz::range_iterable<int> range = lz::range(10);\n        std::vector<int> expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n        test_procs::test_operator_plus(range, expected);\n        test_procs::test_operator_minus(range);\n    }\n\n    SUBCASE(\"Without step, double\") {\n        auto range = lz::range(10.);\n        std::vector<doctest::Approx> expected = { doctest::Approx(0), doctest::Approx(1), doctest::Approx(2), doctest::Approx(3),\n                                                  doctest::Approx(4), doctest::Approx(5), doctest::Approx(6), doctest::Approx(7),\n                                                  doctest::Approx(8), doctest::Approx(9) };\n        test_procs::test_operator_plus(range, expected, compare);\n        test_procs::test_operator_minus(range);\n\n        range = lz::range(10.5);\n        expected = { doctest::Approx(0), doctest::Approx(1), doctest::Approx(2), doctest::Approx(3),\n                     doctest::Approx(4), doctest::Approx(5), doctest::Approx(6), doctest::Approx(7),\n                     doctest::Approx(8), doctest::Approx(9), doctest::Approx(10) };\n        test_procs::test_operator_plus(range, expected, compare);\n        test_procs::test_operator_minus(range);\n    }\n\n    SUBCASE(\"Without step, int, with start\") {\n        lz::range_iterable<int> range = lz::range(2, 10);\n        std::vector<int> expected = { 2, 3, 4, 5, 6, 7, 8, 9 };\n        test_procs::test_operator_plus(range, expected);\n        test_procs::test_operator_minus(range);\n    }\n\n    SUBCASE(\"Without step, double, with start\") {\n        auto range = lz::range(2.3, 10.5);\n        std::vector<doctest::Approx> expected = { doctest::Approx(2.3), doctest::Approx(3.3), doctest::Approx(4.3),\n                                                  doctest::Approx(5.3), doctest::Approx(6.3), doctest::Approx(7.3),\n                                                  doctest::Approx(8.3), doctest::Approx(9.3), doctest::Approx(10.3) };\n        test_procs::test_operator_plus(range, expected, compare);\n        test_procs::test_operator_minus(range);\n\n        range = lz::range(-1., 10.);\n        expected = { doctest::Approx(-1.), doctest::Approx(0), doctest::Approx(1), doctest::Approx(2),\n                     doctest::Approx(3),   doctest::Approx(4), doctest::Approx(5), doctest::Approx(6),\n                     doctest::Approx(7),   doctest::Approx(8), doctest::Approx(9) };\n        test_procs::test_operator_plus(range, expected, compare);\n        test_procs::test_operator_minus(range);\n    }\n}\n"
  },
  {
    "path": "tests/regex_split.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/regex_split.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"regex_split_iterable changing and creating elements\") {\n    std::regex r1(R\"(\\s+)\");\n\n    SUBCASE(\"Empty string\") {\n        std::string s = \"\";\n        lz::regex_split_iterable<std::sregex_token_iterator> splitter = lz::regex_split(s, r1);\n        auto actual = splitter | lz::to<std::vector>();\n        std::vector<std::string> expected = {};\n        REQUIRE(lz::equal(actual, expected));\n    }\n\n    SUBCASE(\"Starting with delimiter\") {\n        std::string s = \"    Hello, world! How are you?\";\n        auto splitter = s | lz::regex_split(r1);\n        auto actual = splitter | lz::to<std::vector>();\n        std::vector<std::string> expected = { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" };\n        REQUIRE(lz::equal(actual, expected));\n    }\n\n    SUBCASE(\"Ending with delimiter\") {\n        std::string s = \"Hello, world! How are you?    \";\n        auto splitter = lz::regex_split(s, r1);\n        auto actual = splitter | lz::to<std::vector>();\n        std::vector<std::string> expected = { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" };\n        REQUIRE(lz::equal(actual, expected));\n    }\n\n    SUBCASE(\"Starting and ending with delimiter\") {\n        std::string s = \"    Hello, world! How are you?    \";\n        auto splitter = lz::regex_split(s, r1);\n        auto actual = splitter | lz::to<std::vector>();\n        std::vector<std::string> expected = { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" };\n        REQUIRE(lz::equal(actual, expected));\n    }\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        std::string s = \"hello, world! This is a test.\";\n        std::regex r(\"\\\\s+\");\n        auto splitted = lz::regex_split(s, r);\n        auto common = make_sentinel_assign_op_tester(splitted);\n        auto expected = { \"hello,\", \"world!\", \"This\", \"is\", \"a\", \"test.\" };\n        REQUIRE(lz::equal(common, expected));\n    }\n}\n\nTEST_CASE(\"Empty or one element regex split\") {\n    SUBCASE(\"Empty\") {\n        std::regex r1(R\"(\\s+)\");\n        std::string s;\n        auto splitter = lz::regex_split(s, r1);\n        REQUIRE(lz::empty(splitter));\n        REQUIRE_FALSE(lz::has_one(splitter));\n        REQUIRE_FALSE(lz::has_many(splitter));\n    }\n\n    SUBCASE(\"One element with result\") {\n        std::regex r1(R\"(\\s+)\");\n        std::string s = \"Hello \";\n        auto splitter = lz::regex_split(s, r1);\n        REQUIRE_FALSE(lz::empty(splitter));\n        REQUIRE(lz::has_one(splitter));\n        REQUIRE_FALSE(lz::has_many(splitter));\n    }\n\n    SUBCASE(\"One element without result\") {\n        std::regex r1(R\"(\\s+)\");\n        std::string s = \"Hello\";\n        auto splitter = lz::regex_split(s, r1);\n        REQUIRE_FALSE(lz::empty(splitter));\n        REQUIRE(lz::has_one(splitter));\n        REQUIRE_FALSE(lz::has_many(splitter));\n    }\n}\n\nTEST_CASE(\"regex_split_iterable binary operations\") {\n    std::regex r1(R\"(\\s+)\");\n    std::string s = \"    Hello, world! How are you?    \";\n    auto splitter = lz::regex_split(s, r1);\n    auto begin = splitter.begin();\n\n    SUBCASE(\"Operator++\") {\n        ++begin;\n        REQUIRE(lz::distance(begin, splitter.end()) == 4);\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        REQUIRE(begin != splitter.end());\n        while (begin != splitter.end()) {\n            ++begin;\n        }\n        REQUIRE(begin == splitter.end());\n    }\n}\n\nTEST_CASE(\"regex_split_iterable to containers\") {\n    std::regex r1(R\"(\\s+)\");\n    std::string s = \"    Hello, world! How are you?    \";\n    auto splitter = lz::regex_split(s, r1);\n\n    SUBCASE(\"To vector\") {\n        auto vec = splitter | lz::to<std::vector>();\n        std::vector<std::string> expected = { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" };\n        REQUIRE(lz::equal(vec, expected));\n    }\n\n    SUBCASE(\"To list\") {\n        auto list = splitter | lz::to<std::list>();\n        std::list<std::string> expected = { \"Hello,\", \"world!\", \"How\", \"are\", \"you?\" };\n        REQUIRE(lz::equal(list, expected));\n    }\n}\n"
  },
  {
    "path": "tests/repeat.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/algorithm/for_each_while.hpp>\n#include <Lz/common.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"repeat_iterable binary operations\") {\n    const int amount = 5;\n    lz::repeat_iterable<int> repeater = lz::repeat(20, amount);\n    auto expected = { 20, 20, 20, 20, 20 };\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        auto repeated = lz::repeat(20, 5);\n        auto common = make_sentinel_assign_op_tester(repeated);\n        auto expected2 = { 20, 20, 20, 20, 20 };\n        REQUIRE(lz::equal(common, expected2));\n        REQUIRE(lz::size(common) == lz::size(expected2));\n    }\n\n    SUBCASE(\"Operator++\") {\n        REQUIRE(lz::equal(repeater, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        REQUIRE(lz::equal(repeater | lz::common | lz::reverse, expected));\n    }\n\n    SUBCASE(\"Operator+\") {\n        test_procs::test_operator_plus(repeater, expected);\n\n        auto iterable = make_sentinel_assign_op_tester(repeater);\n        test_procs::test_operator_plus(iterable, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(repeater);\n        auto iterable = make_sentinel_assign_op_tester(repeater);\n        test_procs::test_operator_minus(iterable);\n    }\n}\n\nTEST_CASE(\"Empty or one element repeat\") {\n    SUBCASE(\"Empty\") {\n        auto repeater = lz::repeat(20, 0);\n        REQUIRE(lz::empty(repeater));\n        REQUIRE_FALSE(lz::has_one(repeater));\n        REQUIRE_FALSE(lz::has_many(repeater));\n        REQUIRE(lz::size(repeater) == 0);\n    }\n\n    SUBCASE(\"One element with result\") {\n        auto repeater = lz::repeat(20, 1);\n        REQUIRE_FALSE(lz::empty(repeater));\n        REQUIRE(lz::has_one(repeater));\n        REQUIRE_FALSE(lz::has_many(repeater));\n        REQUIRE(lz::size(repeater) == 1);\n    }\n}\n\nTEST_CASE(\"repeat_iterable to containers\") {\n    constexpr auto times = 5;\n    const int to_repeat = 20;\n    lz::repeat_iterable<const int&> repeater = lz::repeat(to_repeat, times);\n    REQUIRE(&(*repeater.begin()) == &to_repeat); // Ensure it is by reference\n    static_assert(std::is_const<typename std::remove_reference<decltype(*repeater.begin())>::type>::value,\n                  \"Should be const reference\");\n\n    SUBCASE(\"To array\") {\n        std::array<int, times> array = repeater | lz::to<std::array<int, times>>();\n        auto expected = { 20, 20, 20, 20, 20 };\n        REQUIRE(lz::equal(array, expected));\n    }\n\n    SUBCASE(\"To vector\") {\n        std::vector<int> vec = repeater | lz::to<std::vector>();\n        auto expected = { 20, 20, 20, 20, 20 };\n        REQUIRE(lz::equal(vec, expected));\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        std::list<int> lst = repeater | lz::to<std::list>();\n        auto expected = { 20, 20, 20, 20, 20 };\n        REQUIRE(lz::equal(lst, expected));\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = repeater | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>();\n        std::map<int, int> expected = { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } };\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto actual =\n            repeater | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>();\n        std::unordered_map<int, int> expected = { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } };\n        REQUIRE(actual == expected);\n    }\n}\n\nTEST_CASE(\"repeat_iterable infinite\") {\n    int to_repeat = 20;\n    lz::repeat_iterable_inf<int&> repeater = lz::repeat(to_repeat);\n\n    SUBCASE(\"Should be infinite\") {\n        std::size_t counter = 0;\n        lz::for_each_while(repeater, [&counter](int i) {\n            REQUIRE(i == 20);\n            ++counter;\n            if (counter == 100) {\n                return false;\n            }\n            return true;\n        });\n        REQUIRE(counter == 100);\n    }\n\n    SUBCASE(\"Should be by reference\") {\n        auto start = repeater.begin();\n        REQUIRE(&(*start) == &to_repeat);\n    }\n}\n\nTEST_CASE(\"repeat_iterable infinite binary operations\") {\n    auto repeater = lz::repeat(20);\n    auto begin = repeater.begin();\n\n    SUBCASE(\"Operator++\") {\n        ++begin;\n        REQUIRE(*begin == 20);\n    }\n\n    SUBCASE(\"Not empty, has many, not has one\") {\n        REQUIRE_FALSE(lz::empty(repeater));\n        REQUIRE(lz::has_many(repeater));\n        REQUIRE_FALSE(lz::has_one(repeater));\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        REQUIRE(begin != repeater.end());\n        std::size_t counter = 0;\n        while (begin != repeater.end()) {\n            ++begin;\n            ++counter;\n            if (counter == 100) {\n                break;\n            }\n        }\n        REQUIRE(counter == 100);\n        REQUIRE(begin != repeater.end());\n        REQUIRE(begin != begin);\n    }\n}\n"
  },
  {
    "path": "tests/reverse.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/cached_size.hpp>\n#include <Lz/common.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Non cached reverse\") {\n    SUBCASE(\"Non sentinelled reverse\") {\n        const std::vector<int> v = { 1, 2, 3, 4, 5 };\n        const std::vector<int> expected = { 5, 4, 3, 2, 1 };\n\n        lz::reverse_iterable<const std::vector<int>> reversed = lz::reverse(v);\n        REQUIRE(lz::size(reversed) == 5);\n        REQUIRE(lz::equal(reversed, expected));\n\n        reversed = v | lz::reverse;\n        REQUIRE(lz::equal(reversed, expected));\n        REQUIRE(lz::size(reversed) == 5);\n    }\n\n    SUBCASE(\"Sentinelled reverse\") {\n        auto repeater = lz::repeat(20, 5) | lz::common | lz::reverse;\n        auto expected = { 20, 20, 20, 20, 20 };\n        REQUIRE(lz::size(repeater) == 5);\n        REQUIRE(lz::equal(repeater, expected));\n    }\n\n    SUBCASE(\"Bidi sentinelled reverse\") {\n        auto arr = { 1, 2, 3, 4, 5 };\n        auto bidi_sent = make_sized_bidi_sentinelled(arr);\n        REQUIRE(lz::size(bidi_sent) == lz::size(arr));\n        auto rev = bidi_sent | lz::common | lz::reverse;\n        REQUIRE(lz::size(rev) == lz::size(arr));\n        auto expected = { 5, 4, 3, 2, 1 };\n        REQUIRE(lz::equal(rev, expected));\n    }\n}\n\nTEST_CASE(\"Cached reverse\") {\n    SUBCASE(\"Sentinelled cached reverse\") {\n        auto repeater = lz::repeat(20, 5);\n        auto r = lz::cached_reverse(repeater);\n\n        auto expected = { 20, 20, 20, 20, 20 };\n        REQUIRE(lz::equal(r, expected));\n\n        auto it = r.begin();\n        it = lz::default_sentinel;\n\n        REQUIRE(it == lz::default_sentinel);\n        REQUIRE(lz::default_sentinel == it);\n\n        REQUIRE(it != r.begin());\n        REQUIRE(r.begin() != it);\n        REQUIRE(it == r.end());\n        REQUIRE(r.end() == it);\n\n        test_procs::test_operator_minus(r);\n\n        std::vector<int> expected2 = { 20, 20, 20, 20, 20 };\n        test_procs::test_operator_plus(r, expected2);\n    }\n\n    const std::vector<int> v = { 1, 2, 3, 4, 5 };\n    lz::cached_reverse_iterable<const std::vector<int>> reversed = lz::cached_reverse(v);\n\n    SUBCASE(\"Non sentinelled empty\") {\n        std::vector<int> empty;\n        auto rev = lz::cached_reverse(empty);\n        REQUIRE(lz::size(rev) == 0);\n        REQUIRE(lz::empty(rev));\n        REQUIRE_FALSE(lz::has_many(rev));\n        REQUIRE_FALSE(lz::has_one(rev));\n    }\n\n    SUBCASE(\"Non sentinelled one element\") {\n        std::vector<int> one_element = { 1 };\n        auto rev = lz::cached_reverse(one_element);\n        REQUIRE(lz::size(rev) == 1);\n        REQUIRE_FALSE(lz::empty(rev));\n        REQUIRE(lz::has_one(rev));\n        REQUIRE_FALSE(lz::has_many(rev));\n        auto expected = { 1 };\n        REQUIRE(lz::equal(rev, expected));\n    }\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        std::list<int> list{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n        // Make it so that it has a sentinel and is bidirectional\n        auto t = make_sized_bidi_sentinelled(list);\n        auto common = lz::cached_reverse(make_sentinel_assign_op_tester(t));\n        std::vector<int> expected = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };\n        REQUIRE(lz::equal(common, expected));\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"Non sentinelled operator== sentinel\") {\n        REQUIRE(reversed.begin() != lz::default_sentinel);\n        REQUIRE(reversed.end() == lz::default_sentinel);\n    }\n\n    SUBCASE(\"Non sentinelled operator--\") {\n        auto expected = { 5, 4, 3, 2, 1 };\n        REQUIRE(lz::equal(reversed, expected));\n    }\n\n    SUBCASE(\"Non sentinelled operator++\") {\n        auto expected = { 1, 2, 3, 4, 5 };\n        REQUIRE(lz::equal(reversed | lz::cached_reverse, expected));\n    }\n\n    SUBCASE(\"Non sentinelled operator+\") {\n        std::vector<int> expected = { 1, 2, 3, 4, 5 };\n        test_procs::test_operator_plus(reversed, expected | lz::reverse);\n    }\n\n    SUBCASE(\"Non sentinelled operator-\") {\n        test_procs::test_operator_minus(reversed);\n    }\n}\n"
  },
  {
    "path": "tests/rotate.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/rotate.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"rotate_iterable with sentinels\") {\n    auto c_str = lz::c_string(\"Hello, World!\");\n    lz::rotate_iterable<decltype(c_str)> rotated = lz::rotate(c_str, 7);\n    static_assert(!std::is_same<decltype(rotated.begin()), decltype(rotated.end())>::value, \"Should be sentinel\");\n    REQUIRE((rotated | lz::to<std::string>()) == \"World!Hello, \");\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> lst{ 1, 2, 3, 4, 5 };\n        auto rotated2 = lz::rotate(lst, 2);\n        auto common = make_sentinel_assign_op_tester(rotated2);\n        std::vector<int> expected = { 3, 4, 5, 1, 2 };\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::list<int> lst{ 1, 2, 3, 4, 5 };\n        auto rotated2 = lz::rotate(make_sized_bidi_sentinelled(lst), 2);\n        auto common = make_sentinel_assign_op_tester(rotated2);\n        std::vector<int> expected = { 3, 4, 5, 1, 2 };\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"random access\") {\n        auto rotated2 = lz::rotate(lz::repeat(5, 5), 2);\n        auto common = make_sentinel_assign_op_tester(rotated2);\n        std::vector<int> expected = { 5, 5, 5, 5, 5 };\n        test_procs::test_operator_minus(common);\n        test_procs::test_operator_plus(common, expected);\n    }\n}\n\nTEST_CASE(\"rotate_iterable basic functionality\") {\n    std::array<int, 5> arr = { 1, 2, 3, 4, 5 };\n    auto rotate = lz::rotate(arr, 2);\n    REQUIRE(rotate.size() == arr.size());\n\n    SUBCASE(\"With bidirectional iterator\") {\n        std::list<int> lst = { 1, 2, 3, 4, 5, 6 };\n        auto rotator = lz::rotate(lst, 2);\n        REQUIRE(std::distance(rotator.begin(), rotator.end()) == static_cast<std::ptrdiff_t>(lst.size()));\n        REQUIRE(rotator.size() == lst.size());\n    }\n\n    SUBCASE(\"Rotate where n is larger than size / 2 (random access & sized)\") {\n        rotate = lz::rotate(arr, 3);\n        REQUIRE(rotate.size() == arr.size());\n        REQUIRE((rotate | lz::to<std::vector>()) == std::vector<int>{ 4, 5, 1, 2, 3 });\n    }\n\n    SUBCASE(\"Rotate where n is larger than size / 2 (forward, not sized)\") {\n        auto str = lz::c_string(\"Hello, World!\");\n        auto rotator = lz::rotate(str, 7);\n        auto expected = lz::c_string(\"World!Hello, \");\n        REQUIRE(lz::equal(rotator, expected));\n    }\n\n    SUBCASE(\"Rotate where n == size\") {\n        rotate = lz::rotate(arr, lz::size(arr));\n        REQUIRE(rotate.size() == 0);\n        REQUIRE(lz::empty(rotate));\n        REQUIRE(!lz::has_one(rotate));\n        REQUIRE(!lz::has_many(rotate));\n    }\n}\n\nTEST_CASE(\"rotate_iterable binary operations\") {\n    std::vector<int> vec = { 1, 2, 3, 4 };\n    auto rotate = lz::rotate(vec, 3);\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 4, 1, 2, 3 };\n        REQUIRE(lz::equal(rotate, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto expected = { 4, 1, 2, 3 };\n        REQUIRE(lz::equal(rotate | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"Operator+\") {\n        std::vector<int> expected = { 4, 1, 2, 3 };\n        test_procs::test_operator_plus(rotate, expected);\n\n        std::vector<int> container = { 1, 2, 3, 4, 5 };\n        rotate = lz::rotate(container, 3);\n        expected = { 4, 5, 1, 2, 3 };\n        test_procs::test_operator_plus(rotate, expected);\n\n        container = { 1, 2, 3, 4 };\n        rotate = lz::rotate(container, 2);\n        expected = { 3, 4, 1, 2 };\n        test_procs::test_operator_plus(rotate, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(rotate);\n\n        std::vector<int> container = { 1, 2, 3, 4, 5 };\n        rotate = lz::rotate(container, 3);\n        test_procs::test_operator_minus(rotate);\n\n        container = { 1, 2, 3, 4 };\n        rotate = lz::rotate(container, 2);\n        test_procs::test_operator_minus(rotate);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto r = lz::rotate(lz::repeat(1, 5), 2);\n        test_procs::test_operator_minus(r);\n\n        r = lz::rotate(lz::repeat(1, 0), 0);\n        test_procs::test_operator_minus(r);\n\n        r = lz::rotate(lz::repeat(1, 1), 1);\n        test_procs::test_operator_minus(r);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto r = lz::rotate(lz::repeat(0, 5), 2);\n        std::vector<int> expected = { 0, 0, 0, 0, 0 };\n        test_procs::test_operator_plus(r, expected);\n\n        r = lz::rotate(lz::repeat(0, 1), 1);\n        expected = {};\n        test_procs::test_operator_plus(r, expected);\n\n        r = lz::rotate(lz::repeat(0, 0), 0);\n        expected = {};\n        test_procs::test_operator_plus(r, expected);\n    }\n}\n\nTEST_CASE(\"Empty or one element rotate\") {\n    SUBCASE(\"Empty, increment 2\") {\n        std::vector<int> vec{};\n        auto rotate = lz::rotate(vec, 2);\n        REQUIRE(lz::empty(rotate));\n        REQUIRE_FALSE(lz::has_many(rotate));\n        REQUIRE_FALSE(lz::has_one(rotate));\n    }\n\n    SUBCASE(\"One element, increment 2\") {\n        std::vector<int> vec = { 1 };\n        auto rotate = lz::rotate(vec, 2);\n        REQUIRE(lz::empty(rotate));\n        REQUIRE(!lz::has_many(rotate));\n        REQUIRE(!lz::has_one(rotate));\n    }\n\n    SUBCASE(\"One element, increment 0\") {\n        std::vector<int> vec = { 1 };\n        auto rotate = lz::rotate(vec, 0);\n        REQUIRE_FALSE(lz::empty(rotate));\n        REQUIRE_FALSE(lz::has_many(rotate));\n        REQUIRE(lz::has_one(rotate));\n    }\n\n    SUBCASE(\"Empty, increment 2 with sentinel\") {\n        auto cstr = lz::c_string(\"\");\n        auto rotate = lz::rotate(cstr, 2);\n        REQUIRE(lz::empty(rotate));\n        REQUIRE_FALSE(lz::has_many(rotate));\n        REQUIRE_FALSE(lz::has_one(rotate));\n    }\n\n    SUBCASE(\"One element, increment 0 with sentinel\") {\n        auto cstr = lz::c_string(\"a\");\n        auto rotate = lz::rotate(cstr, 0);\n        REQUIRE_FALSE(lz::empty(rotate));\n        REQUIRE_FALSE(lz::has_many(rotate));\n        REQUIRE(lz::has_one(rotate));\n    }\n\n    SUBCASE(\"One element, increment 2\") {\n        auto cstr = lz::c_string(\"a\");\n        auto rotate = lz::rotate(cstr, 2);\n        REQUIRE(lz::empty(rotate));\n        REQUIRE(!lz::has_many(rotate));\n        REQUIRE(!lz::has_one(rotate));\n    }\n}\n\nTEST_CASE(\"rotate_iterable to containers\") {\n    constexpr std::size_t size = 6;\n    std::vector<int> vec = { 1, 2, 3, 4, 5, 6 };\n    auto rotator = lz::rotate(vec, 2);\n\n    SUBCASE(\"Reverse to container\") {\n        auto reversed = lz::reverse(rotator);\n        REQUIRE((reversed | lz::to<std::array<int, size>>()) == std::array<int, size>{ 2, 1, 6, 5, 4, 3 });\n    }\n\n    SUBCASE(\"To array\") {\n        REQUIRE((rotator | lz::to<std::array<int, size>>()) == std::array<int, size>{ 3, 4, 5, 6, 1, 2 });\n    }\n\n    SUBCASE(\"To vector\") {\n        REQUIRE((rotator | lz::to<std::vector>()) == std::vector<int>{ 3, 4, 5, 6, 1, 2 });\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        REQUIRE((rotator | lz::to<std::list<int>>()) == std::list<int>{ 3, 4, 5, 6, 1, 2 });\n    }\n\n    SUBCASE(\"To map\") {\n        auto map = rotator | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>();\n        REQUIRE(map == std::map<int, int>{ { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 1, 1 }, { 2, 2 } });\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto map = rotator | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>();\n        REQUIRE(map == std::unordered_map<int, int>{ { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 1, 1 }, { 2, 2 } });\n    }\n}\n"
  },
  {
    "path": "tests/split.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/split.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Split with custom container\") {\n    std::string to_split = \"Hello world test 123\";\n#ifdef LZ_HAS_CXX_11\n    auto splitter = to_split | lz::t_split<std::vector<char>>{}(\" \");\n#else\n    auto splitter = to_split | lz::t_split<std::vector<char>>(\" \");\n#endif\n    std::vector<std::vector<char>> expected = {\n        { 'H', 'e', 'l', 'l', 'o' }, { 'w', 'o', 'r', 'l', 'd' }, { 't', 'e', 's', 't' }, { '1', '2', '3' }\n    };\n    REQUIRE(lz::equal(splitter, expected));\n}\n\nTEST_CASE(\"Splitter permutations\") {\n    SUBCASE(\"No delimiters at end and begin\") {\n        const std::string to_split = \"Hello world test 123\";\n        lz::sv_multiple_split_iterable<const std::string> splitter = lz::sv_split(to_split, \" \");\n        auto vec = splitter | lz::to<std::vector>();\n\n        std::vector<lz::string_view> expected = { \"Hello\", \"world\", \"test\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"Starting with one delimiter ending with none\") {\n        const std::string to_split = \" Hello world test 123\";\n        auto splitter = to_split | lz::s_split(\" \");\n        std::vector<std::string> expected = { \"\", \"Hello\", \"world\", \"test\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"Starting with two delimiters ending with none\") {\n        const std::string to_split = \"  Hello world test 123\";\n        auto splitter = to_split | lz::sv_split(\" \");\n        std::vector<std::string> expected = { \"\", \"\", \"Hello\", \"world\", \"test\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"Ending with one delimiter starting with none\") {\n        const std::string to_split = \"Hello world test 123 \";\n        auto splitter = lz::sv_split(to_split, \" \");\n\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"Ending with two delimiters starting with none\") {\n        const std::string to_split = \"Hello world test 123  \";\n        auto splitter = to_split | lz::sv_split(\" \");\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\", \"\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"Starting and ending with one delimiter\") {\n        const std::string to_split = \" Hello world test 123 \";\n        using value_type = lz::basic_iterable<decltype(to_split.begin())>;\n        auto splitter = lz::split(to_split, \" \") | lz::map([](const value_type& vt) { return vt | lz::to<std::string>(); }) |\n                        lz::to<std::vector>();\n        std::vector<std::string> expected = { \"\", \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(splitter == expected);\n    }\n\n    SUBCASE(\"Starting and ending with two delimiters\") {\n        const std::string to_split = \"  Hello world test 123  \";\n        auto splitter = lz::sv_split(to_split, \" \");\n        std::vector<std::string> expected = { \"\", \"\", \"Hello\", \"world\", \"test\", \"123\", \"\", \"\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"No delimiters at all\") {\n        const std::string to_split = \"Hello world test 123\";\n        auto splitter = lz::sv_split(to_split, \" \");\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"Multiple delimiters in the middle\") {\n        const std::string to_split = \"Hello  world  test  123\";\n        auto splitter = lz::sv_split(to_split, \" \");\n        std::vector<std::string> expected = { \"Hello\", \"\", \"world\", \"\", \"test\", \"\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        SUBCASE(\"not ending with delim\") {\n            const std::string s = \"hello, world! This is a test.\";\n\n            SUBCASE(\"single\") {\n                auto splitted = lz::sv_split(s, ' ');\n                auto common = make_sentinel_assign_op_tester(splitted);\n                auto expected = { \"hello,\", \"world!\", \"This\", \"is\", \"a\", \"test.\" };\n                REQUIRE(lz::equal(common, expected));\n            }\n\n            SUBCASE(\"multiple\") {\n                auto splitted2 = lz::sv_split(s, \", \");\n                auto common2 = make_sentinel_assign_op_tester(splitted2);\n                auto expected2 = { \"hello\", \"world! This is a test.\" };\n                REQUIRE(lz::equal(common2, expected2));\n            }\n        }\n\n        SUBCASE(\"ending with delim\") {\n            SUBCASE(\"single\") {\n                const std::string s = \"hello, world! This is a test. \";\n                auto splitted = lz::sv_split(s, ' ');\n                auto common = make_sentinel_assign_op_tester(splitted);\n                auto expected = { \"hello,\", \"world!\", \"This\", \"is\", \"a\", \"test.\", \"\" };\n                REQUIRE(lz::equal(common, expected));\n            }\n\n            SUBCASE(\"multiple\") {\n                const std::string s = \"hello, world! This is a test, \";\n                auto splitted2 = lz::sv_split(s, \", \");\n                auto common2 = make_sentinel_assign_op_tester(splitted2);\n                auto expected = { \"hello\", \"world! This is a test\", \"\" };\n                REQUIRE(lz::equal(common2, expected));\n            }\n        }\n    }\n}\n\nTEST_CASE(\"Splitter changing and creating elements\") {\n    const std::string to_split = \"Hello  world  test  123  \";\n    const char* delimiter = \"  \";\n    auto splitter = lz::sv_split(to_split, delimiter);\n\n    SUBCASE(\"Should split on delimiter\") {\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"Should be correct value type\") {\n        static_assert(std::is_same<decltype(*lz::sv_split(to_split, delimiter).begin()), lz::string_view>::value,\n                      \"should be the same\");\n        static_assert(std::is_same<decltype(*lz::s_split(to_split, delimiter).begin()), std::string>::value,\n                      \"should be the same\");\n        static_assert(std::is_same<decltype(*lz::split(to_split, delimiter).begin()),\n                                   lz::basic_iterable<decltype(to_split.begin())>>::value,\n                      \"should be the same\");\n    }\n}\n\nTEST_CASE(\"Empty or one element string splitter\") {\n    SUBCASE(\"Empty\") {\n        std::string to_split;\n        using it = std::string::iterator;\n        using iterable = lz::split_iterable<lz::basic_iterable<it>, std::string, lz::copied_sv>;\n        iterable splitter = lz::split(to_split, \" \");\n\n        REQUIRE(lz::empty(splitter));\n        REQUIRE_FALSE(lz::has_many(splitter));\n        REQUIRE_FALSE(lz::has_one(splitter));\n    }\n\n    SUBCASE(\"One element\") {\n        std::string to_split = \"Hello\";\n        auto splitter = lz::split(to_split, \" \");\n        REQUIRE_FALSE(lz::empty(splitter));\n        REQUIRE_FALSE(lz::has_many(splitter));\n        REQUIRE(lz::has_one(splitter));\n    }\n\n    SUBCASE(\"One element with delimiter\") {\n        std::string to_split = \"Hello \";\n        auto splitter = lz::split(to_split, \" \");\n        REQUIRE_FALSE(lz::empty(splitter));\n        REQUIRE(lz::has_many(splitter));\n        REQUIRE_FALSE(lz::has_one(splitter));\n    }\n}\n\nTEST_CASE(\"Splitter binary operations\") {\n    std::string to_split = \" Hello world test 123 \";\n    lz::sv_multiple_split_iterable<std::string> splitter = to_split | lz::sv_split(\" \");\n    auto it = splitter.begin();\n\n    REQUIRE(*it == \"\");\n\n    SUBCASE(\"Operator++\") {\n        REQUIRE(*it == \"\");\n        ++it;\n        REQUIRE(*it == \"Hello\");\n        ++it;\n        REQUIRE(*it == \"world\");\n        ++it;\n        REQUIRE(*it == \"test\");\n        ++it;\n        REQUIRE(*it == \"123\");\n        ++it;\n        REQUIRE(*it == \"\");\n        REQUIRE(it != splitter.end());\n        ++it;\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        REQUIRE(it != splitter.end());\n        while (it != splitter.end()) {\n            ++it;\n        }\n        REQUIRE(it == splitter.end());\n    }\n}\n\nTEST_CASE(\"Splitter to containers\") {\n    std::string to_split = \"Hello world test 123 \";\n    auto splitter = lz::sv_split(to_split, \" \");\n    auto cmp = [](const std::string& a, const lz::string_view& b) {\n        return a == std::string{ b.data(), b.size() };\n    };\n\n    SUBCASE(\"To array\") {\n        std::array<std::string, 5> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(expected, splitter, cmp));\n    }\n\n    SUBCASE(\"To vector\") {\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(expected, splitter, cmp));\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        std::list<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(expected, splitter, cmp));\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = splitter | lz::map([](const lz::string_view v) {\n                          return std::make_pair(std::string{ v.data(), v.size() }, std::string{ v.data(), v.size() });\n                      }) |\n                      lz::to<std::map<std::string, std::string>>();\n\n        std::map<std::string, std::string> expected = {\n            std::make_pair(std::string(\"Hello\"), std::string(\"Hello\")),\n            std::make_pair(std::string(\"world\"), std::string(\"world\")),\n            std::make_pair(std::string(\"test\"), std::string(\"test\")),\n            std::make_pair(std::string(\"123\"), std::string(\"123\")),\n            std::make_pair(std::string(\"\"), std::string(\"\")),\n        };\n\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto actual = splitter | lz::map([](const lz::string_view v) {\n                          return std::make_pair(std::string{ v.data(), v.size() }, std::string{ v.data(), v.size() });\n                      }) |\n                      lz::to<std::unordered_map<std::string, std::string>>();\n        std::unordered_map<std::string, std::string> expected = { std::make_pair(std::string(\"Hello\"), std::string(\"Hello\")),\n                                                                  std::make_pair(std::string(\"world\"), std::string(\"world\")),\n                                                                  std::make_pair(std::string(\"test\"), std::string(\"test\")),\n                                                                  std::make_pair(std::string(\"123\"), std::string(\"123\")),\n                                                                  std::make_pair(std::string(\"\"), std::string(\"\")) };\n\n        REQUIRE(actual == expected);\n    }\n}\n\n// ---------------------- One element splitters ----------------------\n\nTEST_CASE(\"One element splitter with custom container\") {\n    std::string to_split = \"Hello world test 123\";\n#ifdef LZ_HAS_CXX_11\n    lz::split_iterable<std::vector<char>, std::string, char> splitter = to_split | lz::t_split<std::vector<char>>{}(' ');\n#else\n    lz::split_iterable<std::vector<char>, std::string, char> splitter = lz::t_split<std::vector<char>>(to_split, ' ');\n#endif\n    std::vector<std::vector<char>> expected = {\n        { 'H', 'e', 'l', 'l', 'o' }, { 'w', 'o', 'r', 'l', 'd' }, { 't', 'e', 's', 't' }, { '1', '2', '3' }\n    };\n    REQUIRE(lz::equal(splitter, expected));\n}\n\nTEST_CASE(\"One element splitter permutations\") {\n    SUBCASE(\"No delimiters at end and begin\") {\n        const std::string to_split = \"Hello world test 123\";\n        lz::sv_single_split_iterable<const std::string> splitter = lz::sv_split(to_split, ' ');\n        auto vec = splitter | lz::to<std::vector>();\n\n        std::vector<lz::string_view> expected = { \"Hello\", \"world\", \"test\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n\n        auto it = splitter.begin();\n        REQUIRE(it == splitter.begin());\n        it = splitter.end();\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"Starting with one delimiter ending with none\") {\n        const std::string to_split = \" Hello world test 123\";\n        lz::s_single_split_iterable<const std::string> splitter = lz::s_split(to_split, ' ');\n        std::vector<std::string> expected = { \"\", \"Hello\", \"world\", \"test\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n\n        auto it = splitter.begin();\n        REQUIRE(it == splitter.begin());\n        it = splitter.end();\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"Starting with two delimiters ending with none\") {\n        const std::string to_split = \"  Hello world test 123\";\n        auto splitter = lz::sv_split(to_split, ' ');\n        std::vector<std::string> expected = { \"\", \"\", \"Hello\", \"world\", \"test\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n\n        auto it = splitter.begin();\n        REQUIRE(it == splitter.begin());\n        it = splitter.end();\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"Ending with one delimiter starting with none\") {\n        const std::string to_split = \"Hello world test 123 \";\n        auto splitter = lz::sv_split(to_split, ' ');\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(splitter, expected));\n\n        auto it = splitter.begin();\n        REQUIRE(it == splitter.begin());\n        it = splitter.end();\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"Ending with two delimiters starting with none\") {\n        const std::string to_split = \"Hello world test 123  \";\n        auto splitter = to_split | lz::sv_split(' ');\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\", \"\" };\n        REQUIRE(lz::equal(splitter, expected));\n\n        auto it = splitter.begin();\n        REQUIRE(it == splitter.begin());\n        it = splitter.end();\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"Starting and ending with one delimiter\") {\n        const std::string to_split = \" Hello world test 123 \";\n        using value_type = lz::basic_iterable<decltype(to_split.begin())>;\n        auto splitter = lz::split(to_split, ' ') | lz::map([](const value_type& vt) { return vt | lz::to<std::string>(); }) |\n                        lz::to<std::vector>();\n        std::vector<std::string> expected = { \"\", \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(splitter == expected);\n\n        auto it = splitter.begin();\n        REQUIRE(it == splitter.begin());\n        it = splitter.end();\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"Starting and ending with two delimiters\") {\n        const std::string to_split = \"  Hello world test 123  \";\n        auto splitter = lz::sv_split(to_split, ' ');\n        std::vector<std::string> expected = { \"\", \"\", \"Hello\", \"world\", \"test\", \"123\", \"\", \"\" };\n        REQUIRE(lz::equal(splitter, expected));\n\n        auto it = splitter.begin();\n        REQUIRE(it == splitter.begin());\n        it = splitter.end();\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"No delimiters at all\") {\n        const std::string to_split = \"Hello world test 123\";\n        auto splitter = lz::sv_split(to_split, ' ');\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n\n        auto it = splitter.begin();\n        REQUIRE(it == splitter.begin());\n        it = splitter.end();\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"Multiple delimiters in the middle\") {\n        const std::string to_split = \"Hello  world  test  123\";\n        auto splitter = lz::sv_split(to_split, ' ');\n        std::vector<std::string> expected = { \"Hello\", \"\", \"world\", \"\", \"test\", \"\", \"123\" };\n        REQUIRE(lz::equal(splitter, expected));\n\n        auto it = splitter.begin();\n        REQUIRE(it == splitter.begin());\n        it = splitter.end();\n        REQUIRE(it == splitter.end());\n    }\n}\n\nTEST_CASE(\"One element splitter changing and creating elements\") {\n    const std::string to_split = \"Hello world test 123 \";\n    auto splitter = lz::sv_split(to_split, ' ');\n\n    SUBCASE(\"Should split on delimiter\") {\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(splitter, expected));\n    }\n\n    SUBCASE(\"Should be correct value type\") {\n        static_assert(std::is_same<decltype(*lz::sv_split(to_split, ' ').begin()), lz::string_view>::value,\n                      \"should be the same\");\n        static_assert(std::is_same<decltype(*lz::s_split(to_split, ' ').begin()), std::string>::value, \"should be the same\");\n        static_assert(\n            std::is_same<decltype(*lz::split(to_split, ' ').begin()), lz::basic_iterable<decltype(to_split.begin())>>::value,\n            \"should be the same\");\n    }\n}\n\nTEST_CASE(\"Empty or one element string one element splitter\") {\n    SUBCASE(\"Empty\") {\n        std::string to_split;\n        auto splitter = lz::split(to_split, ' ');\n        REQUIRE(lz::empty(splitter));\n        REQUIRE_FALSE(lz::has_many(splitter));\n        REQUIRE_FALSE(lz::has_one(splitter));\n    }\n\n    SUBCASE(\"One element\") {\n        std::string to_split = \"Hello\";\n        auto splitter = lz::split(to_split, ' ');\n        REQUIRE_FALSE(lz::empty(splitter));\n        REQUIRE_FALSE(lz::has_many(splitter));\n        REQUIRE(lz::has_one(splitter));\n    }\n\n    SUBCASE(\"One element with delimiter\") {\n        std::string to_split = \"Hello \";\n        auto splitter = lz::split(to_split, ' ');\n        REQUIRE_FALSE(lz::empty(splitter));\n        REQUIRE(lz::has_many(splitter));\n        REQUIRE_FALSE(lz::has_one(splitter));\n    }\n}\n\nTEST_CASE(\"One element splitter binary operations\") {\n    std::string to_split = \" Hello world test 123 \";\n    auto splitter = to_split | lz::sv_split(' ');\n    auto it = splitter.begin();\n\n    REQUIRE(*it == \"\");\n\n    SUBCASE(\"Operator++\") {\n        REQUIRE(*it == \"\");\n        ++it;\n        REQUIRE(*it == \"Hello\");\n        ++it;\n        REQUIRE(*it == \"world\");\n        ++it;\n        REQUIRE(*it == \"test\");\n        ++it;\n        REQUIRE(*it == \"123\");\n        ++it;\n        REQUIRE(*it == \"\");\n        REQUIRE(it != splitter.end());\n        ++it;\n        REQUIRE(it == splitter.end());\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        REQUIRE(it != splitter.end());\n        while (it != splitter.end()) {\n            ++it;\n        }\n        REQUIRE(it == splitter.end());\n    }\n}\n\nTEST_CASE(\"One element splitter to containers\") {\n    std::string to_split = \"Hello world test 123 \";\n    auto splitter = lz::sv_split(to_split, ' ');\n    auto cmp = [](const std::string& a, const lz::string_view& b) {\n        return a == std::string{ b.data(), b.size() };\n    };\n\n    SUBCASE(\"To array\") {\n        std::array<std::string, 5> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(expected, splitter, cmp));\n    }\n\n    SUBCASE(\"To vector\") {\n        std::vector<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(expected, splitter, cmp));\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        std::list<std::string> expected = { \"Hello\", \"world\", \"test\", \"123\", \"\" };\n        REQUIRE(lz::equal(expected, splitter, cmp));\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = splitter | lz::map([](const lz::string_view v) {\n                          return std::make_pair(std::string{ v.data(), v.size() }, std::string{ v.data(), v.size() });\n                      }) |\n                      lz::to<std::map<std::string, std::string>>();\n\n        std::map<std::string, std::string> expected = {\n            std::make_pair(std::string(\"Hello\"), std::string(\"Hello\")),\n            std::make_pair(std::string(\"world\"), std::string(\"world\")),\n            std::make_pair(std::string(\"test\"), std::string(\"test\")),\n            std::make_pair(std::string(\"123\"), std::string(\"123\")),\n            std::make_pair(std::string(\"\"), std::string(\"\")),\n        };\n\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto actual = splitter | lz::map([](const lz::string_view v) {\n                          return std::make_pair(std::string{ v.data(), v.size() }, std::string{ v.data(), v.size() });\n                      }) |\n                      lz::to<std::unordered_map<std::string, std::string>>();\n        std::unordered_map<std::string, std::string> expected = { std::make_pair(std::string(\"Hello\"), std::string(\"Hello\")),\n                                                                  std::make_pair(std::string(\"world\"), std::string(\"world\")),\n                                                                  std::make_pair(std::string(\"test\"), std::string(\"test\")),\n                                                                  std::make_pair(std::string(\"123\"), std::string(\"123\")),\n                                                                  std::make_pair(std::string(\"\"), std::string(\"\")) };\n\n        REQUIRE(actual == expected);\n    }\n}\n"
  },
  {
    "path": "tests/standalone.cpp",
    "content": "#define LZ_STANDALONE\n\n#include <Lz/split.hpp>\n#include <Lz/stream.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Formatting and compile tests\") {\n    SUBCASE(\"Compile test\") {\n        std::string to_split = \"Hello, World!\";\n        auto split = lz::sv_split(to_split, \", \");\n        static_assert(std::is_same<decltype(*split.begin()), lz::string_view>::value, \"Should be string_view\");\n    }\n\n    SUBCASE(\"Format test non empty\") {\n        std::vector<int> vec = { 1, 2, 3, 4 };\n        REQUIRE((vec | lz::format) == \"1, 2, 3, 4\");\n        REQUIRE(lz::format(vec) == \"1, 2, 3, 4\");\n\n        REQUIRE((vec | lz::format(\",\")) == \"1,2,3,4\");\n        REQUIRE(lz::format(vec, \",\") == \"1,2,3,4\");\n\n        std::streambuf* old_cout = std::cout.rdbuf();\n        std::ostringstream oss;\n        std::cout.rdbuf(oss.rdbuf());\n\n        lz::format(vec, std::cout);\n        REQUIRE(oss.str() == \"1, 2, 3, 4\");\n        oss.str(\"\");\n\n        lz::format(vec, std::cout, \",\");\n        REQUIRE(oss.str() == \"1,2,3,4\");\n        oss.str(\"\");\n\n        vec | lz::format(std::cout);\n        REQUIRE(oss.str() == \"1, 2, 3, 4\");\n        oss.str(\"\");\n\n        vec | lz::format(std::cout, \",\");\n        REQUIRE(oss.str() == \"1,2,3,4\");\n        oss.str(\"\");\n\n        std::cout.rdbuf(old_cout);\n\n#if defined(LZ_HAS_FORMAT)\n\n        REQUIRE((vec | lz::format(\", \", \"{}\")) == \"1, 2, 3, 4\");\n        REQUIRE(lz::format(vec, \", \", \"{}\") == \"1, 2, 3, 4\");\n\n        vec | lz::format(std::cout, \", \", \"{:02d}\");\n        REQUIRE(oss.str() == \"01, 02, 03, 04\");\n        oss.str(\"\");\n\n#endif\n    }\n\n    SUBCASE(\"Format empty\") {\n        std::vector<int> vec = {};\n        REQUIRE((vec | lz::format) == \"\");\n        REQUIRE(lz::format(vec) == \"\");\n\n        REQUIRE((vec | lz::format(\",\")) == \"\");\n        REQUIRE(lz::format(vec, \",\") == \"\");\n\n#if defined(LZ_HAS_FORMAT)\n\n        REQUIRE((vec | lz::format(\", \", \"{}\")) == \"\");\n        REQUIRE(lz::format(vec, \", \", \"{}\") == \"\");\n\n#endif\n    }\n\n    SUBCASE(\"Format one\") {\n        std::vector<int> vec = { 1 };\n        REQUIRE((vec | lz::format) == \"1\");\n        REQUIRE(lz::format(vec) == \"1\");\n\n        REQUIRE((vec | lz::format(\",\")) == \"1\");\n        REQUIRE(lz::format(vec, \",\") == \"1\");\n\n#if defined(LZ_HAS_FORMAT)\n\n        REQUIRE((vec | lz::format(\", \", \"{}\")) == \"1\");\n        REQUIRE(lz::format(vec, \", \", \"{}\") == \"1\");\n\n#endif\n    }\n\n    SUBCASE(\"Ostream test non empty\") {\n        std::array<int, 4> arr = { 1, 2, 3, 4 };\n        lz::basic_iterable<std::array<int, 4>::iterator> iterable(arr);\n        std::ostringstream oss;\n        oss << iterable;\n        REQUIRE(oss.str() == \"1, 2, 3, 4\");\n    }\n\n    SUBCASE(\"Ostream test empty\") {\n        std::array<int, 0> arr = {};\n        lz::basic_iterable<std::array<int, 0>::iterator> iterable(arr);\n        std::ostringstream oss;\n        oss << iterable;\n        REQUIRE(oss.str() == \"\");\n    }\n\n    SUBCASE(\"Ostream test one element\") {\n        std::array<int, 1> arr = { 1 };\n        lz::basic_iterable<std::array<int, 1>::iterator> iterable(arr);\n        std::ostringstream oss;\n        oss << iterable;\n        REQUIRE(oss.str() == \"1\");\n    }\n\n    SUBCASE(\"std::cout test non empty\") {\n        std::streambuf* old_cout = std::cout.rdbuf();\n        std::ostringstream oss;\n        std::cout.rdbuf(oss.rdbuf());\n\n        std::array<int, 4> arr = { 1, 2, 3, 4 };\n        lz::basic_iterable<std::array<int, 4>::iterator> iterable(arr);\n        std::cout << iterable;\n        REQUIRE(oss.str() == \"1, 2, 3, 4\");\n\n        std::cout.rdbuf(old_cout);\n    }\n\n    SUBCASE(\"std::cout test empty\") {\n        std::streambuf* old_cout = std::cout.rdbuf();\n        std::ostringstream oss;\n        std::cout.rdbuf(oss.rdbuf());\n\n        std::array<int, 0> arr = {};\n        lz::basic_iterable<std::array<int, 0>::iterator> iterable(arr);\n        std::cout << iterable;\n        REQUIRE(oss.str() == \"\");\n\n        std::cout.rdbuf(old_cout);\n    }\n\n    SUBCASE(\"std::cout test one element\") {\n        std::cout.flush();\n        std::streambuf* old_cout = std::cout.rdbuf();\n        std::ostringstream oss;\n        std::cout.rdbuf(oss.rdbuf());\n\n        std::array<int, 1> arr = { 1 };\n        lz::basic_iterable<std::array<int, 1>::iterator> iterable(arr);\n        std::cout << iterable;\n        REQUIRE(oss.str() == \"1\");\n\n        std::cout.rdbuf(old_cout);\n    }\n}\n"
  },
  {
    "path": "tests/string_view.cpp",
    "content": "#ifdef __clang__\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunused-macros\"\n#endif\n\n#define LZ_STANDALONE\n\n#ifdef __clang__\n#pragma clang diagnostic pop\n#endif\n\n#include <Lz/util/string_view.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <doctest/doctest.h>\n\n#if !defined(LZ_HAS_STRING_VIEW)\n\n#include <Lz/util/string_view.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"String view basic functionality\") {\n    constexpr const char* cstr = \"Hello world!\";\n    constexpr lz::string_view view(\"Hello world!\");\n\n    SUBCASE(\"Should have correct size\") {\n        static_assert(view.size() == lz::detail::constexpr_str_len(cstr), \"view.size() != cstr size\");\n    }\n\n    SUBCASE(\"Should have correct data\") {\n        REQUIRE(view.data() == cstr);\n    }\n\n    SUBCASE(\"Should have correct length\") {\n        static_assert(view.length() == lz::detail::constexpr_str_len(cstr), \"view.length() != cstr length\");\n    }\n\n    SUBCASE(\"Should have correct begin\") {\n        REQUIRE(view.begin() == cstr);\n    }\n\n    SUBCASE(\"Should have correct end\") {\n        REQUIRE(view.end() == cstr + lz::detail::constexpr_str_len(cstr));\n    }\n\n    SUBCASE(\"Empty\") {\n        constexpr lz::string_view empty_view;\n        static_assert(empty_view.size() == 0, \"empty_view.size() != 0\");\n        static_assert(empty_view.length() == 0, \"empty_view.length() != 0\");\n        static_assert(empty_view.data() == nullptr, \"empty_view.data() != nullptr\");\n        static_assert(empty_view.begin() == nullptr, \"empty_view.begin() != nullptr\");\n        static_assert(empty_view.end() == nullptr, \"empty_view.end() != nullptr\");\n        static_assert(empty_view.begin() == empty_view.end(), \"empty_view.begin() != empty_view.end()\");\n    }\n}\n#endif\n"
  },
  {
    "path": "tests/take.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/basic_iterable.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/slice.hpp>\n#include <Lz/take.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Take with sentinels\") {\n    const char* str = \"Hello, world!\";\n    auto c_string = lz::c_string(str);\n    lz::take_iterable<decltype(c_string)> take = c_string | lz::take(5);\n    static_assert(std::is_same<decltype(take.begin()), decltype(take.end())>::value, \"Should be sentinel\");\n    auto expected = lz::c_string(\"Hello\");\n    REQUIRE(lz::equal(take, expected));\n    auto vec = take | lz::to<std::vector<char>>();\n    REQUIRE(lz::equal(vec, expected));\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"with iterator forward\") {\n        std::forward_list<int> lst{ 1, 2, 3, 4, 5 };\n        auto taken = lz::take(lst.begin(), 3);\n        auto end = taken.begin();\n        end = taken.end(); // calls operator=(sentinel)\n        auto begin = taken.begin();\n        auto common = lz::make_basic_iterable(begin, end);\n        auto expected2 = { 1, 2, 3 };\n        REQUIRE(lz::equal(common, expected2));\n    }\n\n    SUBCASE(\"with iterator bidirectional\") {\n        std::list<int> lst{ 1, 2, 3, 4, 5 };\n        auto taken = lz::take(lst.begin(), 3);\n        auto end = taken.begin();\n        end = lz::default_sentinel; // calls operator=(sentinel)\n        auto begin = taken.begin();\n        auto common = lz::make_basic_iterable(begin, end);\n        auto expected2 = { 1, 2, 3 };\n        REQUIRE(lz::equal(common | lz::reverse, expected2 | lz::reverse));\n    }\n\n    SUBCASE(\"with iterator random access\") {\n        auto rep = lz::repeat(1, 5);\n        auto taken = lz::take(rep.begin(), 3);\n        auto end = taken.begin();\n        end = lz::default_sentinel; // calls operator=(sentinel)\n        auto begin = taken.begin();\n        auto common = lz::make_basic_iterable(begin, end);\n        auto expected = { 1, 1, 1 };\n        test_procs::test_operator_minus(common);\n        test_procs::test_operator_plus(common, expected);\n    }\n}\n\n// TODO check enable ifs in adaptors for ambiguity\nTEST_CASE(\"Take binary operations where n is smaller than size\") {\n    std::array<int, 10> array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n    auto take = lz::take(array, 4);\n    REQUIRE(take.size() == 4);\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 1, 2, 3, 4 };\n        REQUIRE(lz::equal(take, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto expected = { 4, 3, 2, 1 };\n        REQUIRE(lz::equal(take | lz::reverse, expected));\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        REQUIRE(take.begin() != take.end());\n        REQUIRE(take.end() != take.begin());\n        REQUIRE_FALSE(take.begin() == take.end());\n        REQUIRE_FALSE(take.end() == take.begin());\n        auto it = take.begin();\n        it = take.end();\n        REQUIRE(it == take.end());\n        REQUIRE(take.end() == it);\n        REQUIRE_FALSE(it != take.end());\n        REQUIRE_FALSE(take.end() != it);\n    }\n\n    SUBCASE(\"Operator+\") {\n        std::vector<int> expected = { 1, 2, 3, 4 };\n        test_procs::test_operator_plus(take, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(take);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto r = lz::repeat(1, 5) | lz::take(3);\n        test_procs::test_operator_minus(r);\n\n        auto rep = lz::repeat(0, 5);\n        auto r2 = lz::take(rep.begin(), 3);\n        test_procs::test_operator_minus(r2);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto r = lz::repeat(1, 5) | lz::take(3);\n        std::vector<int> expected = { 1, 1, 1 };\n        test_procs::test_operator_plus(r, expected);\n\n        r = lz::repeat(0, 5) | lz::take(3);\n        expected = { 0, 0, 0 };\n        test_procs::test_operator_plus(r, expected);\n\n        r = lz::repeat(0, 0) | lz::take(3);\n        expected = {};\n        test_procs::test_operator_plus(r, expected);\n    }\n}\n\nTEST_CASE(\"Take binary operations where n is larger than size\") {\n    std::array<int, 10> array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n    auto take = lz::take(array, 20);\n    REQUIRE(take.size() == 10);\n\n    SUBCASE(\"Operator++\") {\n        auto begin = take.begin();\n        ++begin;\n        REQUIRE(*begin == 2);\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto end = take.end();\n        --end;\n        REQUIRE(*end == 10);\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        REQUIRE(take.begin() != take.end());\n        REQUIRE_FALSE(take.begin() == take.end());\n        auto it = take.begin();\n        it = take.end();\n        REQUIRE(it == take.end());\n    }\n\n    SUBCASE(\"Operator+\") {\n        INFO(\"Operator+\");\n        std::vector<int> expected = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n        test_procs::test_operator_plus(take, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        INFO(\"Operator-\");\n        test_procs::test_operator_minus(take);\n    }\n}\n\nTEST_CASE(\"Empty or one element take\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> vec;\n        auto take = lz::take(vec, 0);\n        REQUIRE(take.size() == 0);\n        REQUIRE(lz::empty(take));\n        REQUIRE_FALSE(lz::has_one(take));\n        REQUIRE_FALSE(lz::has_many(take));\n    }\n\n    SUBCASE(\"One element\") {\n        std::vector<int> vec = { 1 };\n        auto take = lz::take(vec, 1);\n        REQUIRE(take.size() == 1);\n        REQUIRE_FALSE(lz::empty(take));\n        REQUIRE(lz::has_one(take));\n        REQUIRE_FALSE(lz::has_many(take));\n    }\n\n    SUBCASE(\"Empty iterator\") {\n        std::vector<int> vec;\n        auto take = lz::take(vec.begin(), 0);\n        REQUIRE(take.size() == 0);\n        REQUIRE(lz::empty(take));\n        REQUIRE_FALSE(lz::has_one(take));\n        REQUIRE_FALSE(lz::has_many(take));\n    }\n\n    SUBCASE(\"One element iterator\") {\n        std::vector<int> vec = { 1 };\n        auto take = lz::take(vec.begin(), 1);\n        REQUIRE(take.size() == 1);\n        REQUIRE_FALSE(lz::empty(take));\n        REQUIRE(lz::has_one(take));\n        REQUIRE_FALSE(lz::has_many(take));\n    }\n}\n\nTEST_CASE(\"Take with bidirectional iterators\") {\n    std::list<int> lst = { 1, 2, 3, 4, 5, 6, 7, 8 };\n    auto take = lz::take(lst, 6);\n    REQUIRE(take.size() == 6);\n    auto expected = { 1, 2, 3, 4, 5, 6 };\n    REQUIRE(lz::equal(take, expected));\n    REQUIRE(lz::equal(take | lz::reverse, expected | lz::reverse));\n}\n\nTEST_CASE(\"Take to containers\") {\n    constexpr std::size_t size = 8;\n    std::array<int, size> array = { 1, 2, 3, 4, 5, 6, 7, 8 };\n    auto take = lz::take(array, 4);\n    REQUIRE(take.size() == 4);\n\n    SUBCASE(\"To array\") {\n        auto arr = take | lz::to<std::array<int, 4>>();\n        REQUIRE(lz::equal(arr, take));\n    }\n\n    SUBCASE(\"To vector\") {\n        auto vec = take | lz::to<std::vector>(std::allocator<int>());\n        REQUIRE(lz::equal(vec, take));\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        auto lst = take | lz::to<std::list<int>>(std::allocator<int>());\n        REQUIRE(lz::equal(lst, take));\n    }\n\n    SUBCASE(\"To map\") {\n        auto map_obj = take | lz::map([](int i) { return std::make_pair(i, i); });\n        auto map = lz::to<std::map<int, int>>(map_obj);\n        REQUIRE(map.size() == 4);\n        std::map<int, int> expected = { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 } };\n        REQUIRE(map == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto map = take | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>();\n        REQUIRE(map.size() == 4);\n        std::unordered_map<int, int> expected = { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 } };\n        REQUIRE(map == expected);\n    }\n}\n\nTEST_CASE(\"Drop & slice\") {\n    SUBCASE(\"Drop iterator\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n        auto drop = vec | lz::drop(4);\n        REQUIRE(drop.size() == 4);\n        std::vector<int> expected = { 5, 6, 7, 8 };\n        REQUIRE(lz::equal(drop, expected));\n    }\n\n    SUBCASE(\"Empty drop\") {\n        std::vector<int> vec;\n        auto drop = vec | lz::drop(3);\n        REQUIRE(drop.size() == 0);\n        REQUIRE(lz::empty(drop));\n        REQUIRE(!lz::has_one(drop));\n        REQUIRE(!lz::has_many(drop));\n    }\n\n    SUBCASE(\"Drop where n is larger than size\") {\n        std::vector<int> vec = { 1, 2, 3 };\n        auto drop = vec | lz::drop(10);\n        REQUIRE(drop.size() == 0);\n        REQUIRE(lz::empty(drop));\n        REQUIRE(!lz::has_one(drop));\n        REQUIRE(!lz::has_many(drop));\n    }\n\n    SUBCASE(\"Drop where n is larger than size / 2, sized & random access\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n        auto drop = vec | lz::drop(6);\n        REQUIRE(drop.size() == 2);\n        std::vector<int> expected = { 7, 8 };\n        REQUIRE(lz::equal(drop, expected));\n    }\n\n    SUBCASE(\"Drop where n is larger than size / 2, sized & bidirectional\") {\n        std::list<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n        auto drop = vec | lz::drop(6);\n        REQUIRE(drop.size() == 2);\n        std::vector<int> expected = { 7, 8 };\n        REQUIRE(lz::equal(drop, expected));\n    }\n\n    SUBCASE(\"Drop where n is larger than size / 2, not sized & forward\") {\n        auto cstr = lz::c_string(\"Hello, world!\");\n        auto drop = cstr | lz::drop(7);\n        std::vector<char> expected = { 'w', 'o', 'r', 'l', 'd', '!' };\n        REQUIRE(lz::equal(drop, expected));\n    }\n\n    SUBCASE(\"Slice iterable\") {\n        std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n        auto slice = vec | lz::slice(2, 6);\n        REQUIRE(slice.size() == 4);\n        std::vector<int> expected = { 3, 4, 5, 6 };\n        auto result = lz::to<std::vector>(slice, std::allocator<int>());\n        REQUIRE(lz::equal(slice, expected));\n    }\n}\n\nTEST_CASE(\"Take with iterator only\") {\n    std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };\n    auto take = lz::take(vec.begin(), 4);\n    REQUIRE(take.size() == 4);\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 1, 2, 3, 4 };\n        REQUIRE(lz::equal(take, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto expected = { 4, 3, 2, 1 };\n        REQUIRE(lz::equal(take | lz::reverse, expected));\n    }\n\n    SUBCASE(\"Operator+\") {\n        auto expected = { 1, 2, 3, 4 };\n        test_procs::test_operator_plus(take, expected);\n    }\n\n    SUBCASE(\"Operator-\") {\n        test_procs::test_operator_minus(take);\n    }\n}\n"
  },
  {
    "path": "tests/take_every.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/basic_iterable.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/take_every.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"take_every_iterable with sentinels\") {\n    auto cstr = lz::c_string(\"Hello\");\n    lz::take_every_iterable<decltype(cstr)> take_every = lz::take_every(cstr, 2);\n    static_assert(!std::is_same<decltype(take_every.begin()), decltype(take_every.end())>::value, \"Should be sentinel\");\n    auto expected = lz::c_string(\"Hlo\");\n    REQUIRE(lz::equal(take_every, expected));\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        SUBCASE(\"forward\") {\n            std::forward_list<int> lst{ 1, 2, 3, 4, 5 };\n            auto take_every2 = lz::take_every(lst, 2);\n            auto common = make_sentinel_assign_op_tester(take_every2);\n            auto expected2 = { 1, 3, 5 };\n            REQUIRE(lz::equal(common, expected2));\n        }\n\n        SUBCASE(\"bidirectional\") {\n            std::list<int> list{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n            auto filtered = list | lz::filter([](int i) { return i % 2 == 0; });\n            // Make it so that it has a sentinel and is bidirectional\n            auto t = make_sized_bidi_sentinelled(filtered);\n            auto take_every2 = lz::take_every(t, 2);\n            auto common = make_sentinel_assign_op_tester(take_every2);\n            std::vector<int> expected2 = { 2, 6, 10 };\n            REQUIRE(lz::equal(common, expected2));\n        }\n\n        SUBCASE(\"random access\") {\n            auto repeater = lz::repeat(20, 5);\n            auto end = repeater.begin();\n            end = repeater.end(); // calls operator=(sentinel)\n            auto begin = repeater.begin();\n            auto common = lz::take_every(lz::make_basic_iterable(begin, end), 2);\n            auto expected2 = { 20, 20, 20 };\n            REQUIRE(lz::equal(common, expected2));\n        }\n    }\n}\n\nTEST_CASE(\"take_every_iterable changing and creating elements\") {\n    constexpr std::size_t size = 4;\n    std::array<int, size> array = { 1, 2, 3, 4 };\n    auto take_every = array | lz::take_every(2);\n    auto iterator = take_every.begin();\n\n    SUBCASE(\"take_every_iterable should be by reference\") {\n        *iterator = 0;\n        REQUIRE(array[0] == 0);\n    }\n\n    SUBCASE(\"take_every_iterable should select every amount-th\") {\n        REQUIRE(*iterator == 1);\n        ++iterator;\n        REQUIRE(*iterator == 3);\n        ++iterator;\n        REQUIRE(iterator == take_every.end());\n    }\n}\n\nTEST_CASE(\"Empty or one element take every\") {\n    SUBCASE(\"Empty 1\") {\n        std::vector<int> vec;\n        auto take_every = lz::take_every(vec, 2);\n        REQUIRE(take_every.size() == 0);\n        REQUIRE(lz::empty(take_every));\n        REQUIRE_FALSE(lz::has_many(take_every));\n        REQUIRE_FALSE(lz::has_one(take_every));\n    }\n\n    SUBCASE(\"Empty 2\") {\n        std::vector<int> vec;\n        auto take_every = lz::take_every(vec, 2, 3);\n        REQUIRE(take_every.size() == 0);\n        REQUIRE(lz::empty(take_every));\n        REQUIRE_FALSE(lz::has_many(take_every));\n        REQUIRE_FALSE(lz::has_one(take_every));\n    }\n\n    SUBCASE(\"Empty 3\") {\n        std::list<int> vec;\n        auto take_every = lz::take_every(vec, 2, 3);\n        REQUIRE(take_every.size() == 0);\n        REQUIRE(lz::empty(take_every));\n        REQUIRE_FALSE(lz::has_many(take_every));\n        REQUIRE_FALSE(lz::has_one(take_every));\n    }\n\n    SUBCASE(\"Empty 4\") {\n        std::list<int> vec;\n        auto take_every = lz::take_every(vec, 2);\n        REQUIRE(take_every.size() == 0);\n        REQUIRE(lz::empty(take_every));\n        REQUIRE_FALSE(lz::has_many(take_every));\n        REQUIRE_FALSE(lz::has_one(take_every));\n    }\n\n    SUBCASE(\"One element\") {\n        std::vector<int> vec = { 1 };\n        auto take_every = lz::take_every(vec, 2);\n        REQUIRE(take_every.size() == 1);\n        REQUIRE_FALSE(lz::empty(take_every));\n        REQUIRE_FALSE(lz::has_many(take_every));\n        REQUIRE(lz::has_one(take_every));\n    }\n\n    SUBCASE(\"One element 2\") {\n        std::vector<int> vec = { 1 };\n        auto take_every = lz::take_every(vec, 2, 3);\n        REQUIRE(take_every.size() == 0);\n        REQUIRE(lz::empty(take_every));\n        REQUIRE_FALSE(lz::has_many(take_every));\n        REQUIRE_FALSE(lz::has_one(take_every));\n    }\n\n    SUBCASE(\"One element 3\") {\n        std::list<int> vec = { 1 };\n        auto take_every = lz::take_every(vec, 2);\n        REQUIRE(take_every.size() == 1);\n        REQUIRE_FALSE(lz::empty(take_every));\n        REQUIRE_FALSE(lz::has_many(take_every));\n        REQUIRE(lz::has_one(take_every));\n    }\n\n    SUBCASE(\"One element 4\") {\n        std::list<int> vec = { 1 };\n        auto take_every = lz::take_every(vec, 2, 3);\n        REQUIRE(take_every.size() == 0);\n        REQUIRE(lz::empty(take_every));\n        REQUIRE_FALSE(lz::has_many(take_every));\n        REQUIRE_FALSE(lz::has_one(take_every));\n    }\n}\n\ntemplate<class Container1, class Container2>\nvoid operator_pp_test(const Container1& even_sized, const Container2& uneven_sized) {\n    INFO(\"Operator++\");\n    LZ_ASSERT(lz::eager_size(even_sized) == 4, \"Must be size 4\");\n    LZ_ASSERT(lz::eager_size(uneven_sized) == 5, \"Must be size 5\");\n\n    auto even_sized_even_take = lz::take_every(even_sized, 2);\n    auto even_sized_odd_take = lz::take_every(even_sized, 3);\n    auto uneven_sized_even_take = lz::take_every(uneven_sized, 2);\n    auto uneven_sized_odd_take = lz::take_every(uneven_sized, 3);\n\n    auto expected1 = { 1, 3 };\n    auto expected2 = { 1, 4 };\n    auto expected3 = { 1, 3, 5 };\n    auto expected4 = { 1, 4 };\n\n    REQUIRE(lz::equal(even_sized_even_take, expected1));\n    REQUIRE(lz::equal(even_sized_odd_take, expected2));\n    REQUIRE(lz::equal(uneven_sized_even_take, expected3));\n    REQUIRE(lz::equal(uneven_sized_odd_take, expected4));\n}\n\ntemplate<class Container1, class Container2>\nvoid operator_mm_test(const Container1 even_sized, const Container2& uneven_sized) {\n    INFO(\"Operator--\");\n    LZ_ASSERT(even_sized.size() == 4, \"Must be size 4\");\n    LZ_ASSERT(uneven_sized.size() == 5, \"Must be size 5\");\n\n    // even sized = { 1, 2, 3, 4 }\n    // uneven sized = { 1, 2, 3, 4, 5 }\n    auto even_sized_even_take = lz::take_every(even_sized, 2);     // {3, 1}\n    auto even_sized_odd_take = lz::take_every(even_sized, 3);      // {4, 1}\n    auto uneven_sized_even_take = lz::take_every(uneven_sized, 2); // {5, 3, 1}\n    auto uneven_sized_odd_take = lz::take_every(uneven_sized, 3);  // {4, 1}\n\n    auto expected1 = { 3, 1 };\n    auto expected2 = { 4, 1 };\n    auto expected3 = { 5, 3, 1 };\n    auto expected4 = { 4, 1 };\n\n    REQUIRE(lz::equal(lz::reverse(even_sized_even_take), expected1));\n    REQUIRE(lz::equal(lz::reverse(even_sized_odd_take), expected2));\n    REQUIRE(lz::equal(lz::reverse(uneven_sized_even_take), expected3));\n    REQUIRE(lz::equal(lz::reverse(uneven_sized_odd_take), expected4));\n}\n\nTEST_CASE(\"take_every_iterable binary operations\") {\n    SUBCASE(\"Operator++\") {\n        std::array<int, 4> even_sized = { 1, 2, 3, 4 };\n        std::array<int, 5> odd_sized = { 1, 2, 3, 4, 5 };\n        operator_pp_test(even_sized, odd_sized);\n\n        std::list<int> even_sized_list = { 1, 2, 3, 4 };\n        std::list<int> odd_sized_list = { 1, 2, 3, 4, 5 };\n        operator_pp_test(even_sized_list, odd_sized_list);\n\n        std::forward_list<int> even_sized_forward_list = { 1, 2, 3, 4 };\n        std::forward_list<int> odd_sized_forward_list = { 1, 2, 3, 4, 5 };\n        operator_pp_test(even_sized_forward_list, odd_sized_forward_list);\n    }\n\n    SUBCASE(\"Operator--\") {\n        std::array<int, 4> even_sized = { 1, 2, 3, 4 };\n        std::array<int, 5> odd_sized = { 1, 2, 3, 4, 5 };\n        operator_mm_test(even_sized, odd_sized);\n\n        std::list<int> even_sized_list = { 1, 2, 3, 4 };\n        std::list<int> odd_sized_list = { 1, 2, 3, 4, 5 };\n        operator_mm_test(even_sized_list, odd_sized_list);\n    }\n\n    SUBCASE(\"Operator+\") {\n        std::vector<int> even_sized = { 1, 2, 3, 4 };\n        std::vector<int> odd_sized = { 1, 2, 3, 4, 5 };\n\n        auto even_sized_even_take = lz::take_every(even_sized, 2);\n        auto even_sized_odd_take = lz::take_every(even_sized, 3);\n        auto uneven_sized_even_take = lz::take_every(odd_sized, 2);\n        auto uneven_sized_odd_take = lz::take_every(odd_sized, 3);\n\n        std::vector<int> expected = { 1, 3 };\n        test_procs::test_operator_plus(expected, even_sized_even_take);\n\n        expected = { 1, 4 };\n        test_procs::test_operator_plus(expected, even_sized_odd_take);\n\n        expected = { 1, 3, 5 };\n        test_procs::test_operator_plus(expected, uneven_sized_even_take);\n\n        expected = { 1, 4 };\n        test_procs::test_operator_plus(expected, uneven_sized_odd_take);\n    }\n\n    SUBCASE(\"Operator-\") {\n        std::vector<int> even_sized = { 1, 2, 3, 4 };\n        std::vector<int> odd_sized = { 1, 2, 3, 4, 5 };\n        auto even_sized_even_take = lz::take_every(even_sized, 2);\n        auto even_sized_odd_take = lz::take_every(even_sized, 3);\n        auto uneven_sized_even_take = lz::take_every(odd_sized, 2);\n        auto uneven_sized_odd_take = lz::take_every(odd_sized, 3);\n\n        test_procs::test_operator_minus(even_sized_even_take);\n        test_procs::test_operator_minus(even_sized_odd_take);\n        test_procs::test_operator_minus(uneven_sized_even_take);\n        test_procs::test_operator_minus(uneven_sized_odd_take);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto even_sized = lz::repeat(1, 4);\n        auto odd_sized = lz::repeat(1, 5);\n        auto even_sized_even_take = lz::take_every(even_sized, 2);\n        auto even_sized_odd_take = lz::take_every(even_sized, 3);\n        auto uneven_sized_even_take = lz::take_every(odd_sized, 2);\n        auto uneven_sized_odd_take = lz::take_every(odd_sized, 3);\n\n        test_procs::test_operator_minus(even_sized_even_take);\n        test_procs::test_operator_minus(even_sized_odd_take);\n        test_procs::test_operator_minus(uneven_sized_even_take);\n        test_procs::test_operator_minus(uneven_sized_odd_take);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto even_sized = lz::repeat(1, 4);\n        auto odd_sized = lz::repeat(1, 5);\n        auto even_sized_even_take = lz::take_every(even_sized, 2);\n        auto even_sized_odd_take = lz::take_every(even_sized, 3);\n        auto uneven_sized_even_take = lz::take_every(odd_sized, 2);\n        auto uneven_sized_odd_take = lz::take_every(odd_sized, 3);\n\n        std::vector<int> expected = { 1, 1 };\n        test_procs::test_operator_plus(even_sized_even_take, expected);\n\n        expected = { 1, 1 };\n        test_procs::test_operator_plus(even_sized_odd_take, expected);\n\n        expected = { 1, 1, 1 };\n        test_procs::test_operator_plus(uneven_sized_even_take, expected);\n\n        expected = { 1, 1 };\n        test_procs::test_operator_plus(uneven_sized_odd_take, expected);\n    }\n}\n\nTEST_CASE(\"take_every_iterable to containers\") {\n    constexpr std::size_t size = 4;\n    std::array<int, size> array = { 1, 2, 3, 4 };\n    constexpr std::size_t offset = 2;\n    auto take_every = lz::take_every(array, offset);\n    REQUIRE(take_every.size() == 2);\n\n    SUBCASE(\"To array\") {\n        std::array<int, size / offset> actual = std::move(take_every) | lz::to<std::array<int, offset>>();\n        REQUIRE(actual == std::array<int, offset>{ 1, 3 });\n    }\n\n    SUBCASE(\"To vector\") {\n        std::vector<int> actual = take_every | lz::to<std::vector>();\n        REQUIRE(actual == std::vector<int>{ 1, 3 });\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        std::list<int> actual = take_every | lz::to<std::list>();\n        REQUIRE(actual == std::list<int>{ 1, 3 });\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = take_every | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>();\n        std::map<int, int> expected = { std::make_pair(1, 1), std::make_pair(3, 3) };\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto actual =\n            take_every | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>();\n        std::unordered_map<int, int> expected = { std::make_pair(1, 1), std::make_pair(3, 3) };\n        REQUIRE(actual == expected);\n    }\n}\n\nTEST_CASE(\"Take every with start offset\") {\n    SUBCASE(\"Start offset 1\") {\n        std::array<int, 7> arr = { 1, 2, 3, 4, 5, 6, 7 };\n        auto take_every = arr | lz::take_every(2, 3);\n        REQUIRE(take_every.size() == 2);\n        REQUIRE(*take_every.begin() == 4);\n        REQUIRE(*(take_every.begin() + 1) == 6);\n        REQUIRE((take_every.begin() + 2) == take_every.end());\n    }\n\n    SUBCASE(\"Start offset 2\") {\n        std::list<int> lst = { 1, 2, 3 };\n        auto take_every2 = lst | lz::take_every(2, 1);\n        REQUIRE(take_every2.size() == 1);\n        REQUIRE(*take_every2.begin() == 2);\n        REQUIRE(std::next(take_every2.begin()) == take_every2.end());\n    }\n\n    SUBCASE(\"Start offset 3\") {\n        std::array<int, 6> arr2 = { 1, 2, 3, 4, 5, 6 };\n        auto take_every3 = arr2 | lz::take_every(2, 3);\n        REQUIRE(take_every3.size() == 2);\n        REQUIRE(*take_every3.begin() == 4);\n        REQUIRE(*(take_every3.begin() + 1) == 6);\n        REQUIRE((take_every3.begin() + 2) == take_every3.end());\n    }\n\n    SUBCASE(\"Start offset with bidirectional sized iterable where start > size / 2\") {\n        std::list<int> lst2 = { 1, 2, 3, 4, 5, 6 };\n        auto take_every4 = lst2 | lz::take_every(2, 4);\n        REQUIRE(take_every4.size() == 1);\n        REQUIRE(*take_every4.begin() == 5);\n        REQUIRE(std::next(take_every4.begin()) == take_every4.end());\n    }\n\n    SUBCASE(\"Start offset with bidirectional non sized iterable\") {\n        std::list<int> lst2 = { 1, 2, 3, 4, 5, 6 };\n        auto take_every4 = lst2 | lz::filter([](int) { return true; }) | lz::take_every(2, 4);\n        REQUIRE(*take_every4.begin() == 5);\n        REQUIRE(std::next(take_every4.begin()) == take_every4.end());\n    }\n}\n"
  },
  {
    "path": "tests/take_while.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/drop_while.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/take_while.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Take while with sentinels\") {\n    auto cstr = lz::c_string(\"Hello, World!\");\n    std::function<bool(char)> condition = [](char c) {\n        return c != 'W';\n    };\n    lz::take_while_iterable<decltype(cstr), decltype(condition)> take_while = lz::take_while(cstr, std::move(condition));\n    static_assert(!std::is_same<decltype(take_while.begin()), decltype(take_while.end())>::value, \"Should be sentinel\");\n    auto c_str_expected = lz::c_string(\"Hello, \");\n    REQUIRE(lz::equal(take_while, c_str_expected));\n}\n\nTEST_CASE(\"Operator=\") {\n    std::forward_list<int> lst{ 1, 2, 3, 4, 5 };\n    auto take_while2 = lz::take_while(lst, [](int i) { return i < 4; });\n    auto common = make_sentinel_assign_op_tester(take_while2);\n    auto expected = { 1, 2, 3 };\n    REQUIRE(lz::equal(common, expected));\n}\n\nTEST_CASE(\"Empty or one element drop while\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> vec;\n        auto drop_while = lz::drop_while(vec, [](int i) { return i == 2; });\n        REQUIRE(lz::empty(drop_while));\n        REQUIRE_FALSE(lz::has_many(drop_while));\n        REQUIRE_FALSE(lz::has_one(drop_while));\n        REQUIRE(lz::size(drop_while) == 0);\n    }\n\n    SUBCASE(\"One element true\") {\n        std::vector<int> vec = { 1 };\n        auto drop_while = lz::drop_while(vec, [](int i) { return i == 1; });\n        REQUIRE(lz::empty(drop_while));\n        REQUIRE_FALSE(lz::has_many(drop_while));\n        REQUIRE_FALSE(lz::has_one(drop_while));\n        REQUIRE(lz::size(drop_while) == 0);\n    }\n\n    SUBCASE(\"One element false\") {\n        std::vector<int> vec = { 1 };\n        auto drop_while = lz::drop_while(vec, [](int i) { return i != 1; });\n        REQUIRE_FALSE(lz::empty(drop_while));\n        REQUIRE_FALSE(lz::has_many(drop_while));\n        REQUIRE(lz::has_one(drop_while));\n        REQUIRE(lz::size(drop_while) == 1);\n    }\n\n    SUBCASE(\"Many elements both true\") {\n        std::vector<int> vec = { 1, 2 };\n        auto drop_while = lz::drop_while(vec, [](int i) { return i < 3; });\n        REQUIRE(lz::empty(drop_while));\n        REQUIRE_FALSE(lz::has_many(drop_while));\n        REQUIRE_FALSE(lz::has_one(drop_while));\n        REQUIRE(lz::size(drop_while) == 0);\n    }\n\n    SUBCASE(\"Many elements first true\") {\n        std::vector<int> vec = { 1, 2 };\n        auto drop_while = lz::drop_while(vec, [](int i) { return i == 1; });\n        REQUIRE_FALSE(lz::empty(drop_while));\n        REQUIRE_FALSE(lz::has_many(drop_while));\n        REQUIRE(lz::has_one(drop_while));\n        REQUIRE(lz::size(drop_while) == 1);\n    }\n\n    SUBCASE(\"Many elements first false\") {\n        std::vector<int> vec = { 1, 2 };\n        auto drop_while = lz::drop_while(vec, [](int i) { return i > 1; });\n        REQUIRE_FALSE(lz::empty(drop_while));\n        REQUIRE(lz::has_many(drop_while));\n        REQUIRE_FALSE(lz::has_one(drop_while));\n        REQUIRE(lz::size(drop_while) == 2);\n    }\n\n    SUBCASE(\"Many elements all false\") {\n        std::vector<int> vec = { 1, 2 };\n        auto drop_while = lz::drop_while(vec, [](int i) { return i > 2; });\n        REQUIRE_FALSE(lz::empty(drop_while));\n        REQUIRE(lz::has_many(drop_while));\n        REQUIRE_FALSE(lz::has_one(drop_while));\n        REQUIRE(lz::size(drop_while) == 2);\n    }\n}\n\nTEST_CASE(\"take_while_iterable binary operations random access\") {\n    constexpr size_t size = 10;\n    std::array<int, size> array{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n    auto take_while = lz::take_while(array, [](int element) { return element < 5; });\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 1, 2, 3, 4 };\n        REQUIRE(lz::equal(take_while, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto expected = { 4, 3, 2, 1 };\n        REQUIRE(lz::equal(take_while | lz::reverse, expected));\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        auto it = take_while.begin();\n        REQUIRE(it == take_while.begin());\n        REQUIRE(it != take_while.end());\n        REQUIRE(take_while.begin() == it);\n        REQUIRE(take_while.end() != it);\n        it = take_while.end();\n        REQUIRE(it == take_while.end());\n        REQUIRE(it != take_while.begin());\n        REQUIRE(take_while.end() == it);\n        REQUIRE(take_while.begin() != it);\n    }\n}\n\nTEST_CASE(\"take_while_iterable binary operations bidirectional access\") {\n    std::list<int> lst{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n    auto take_while = lz::take_while(lst, [](int element) { return element < 5; });\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 1, 2, 3, 4 };\n        REQUIRE(lz::equal(take_while, expected));\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        auto it = take_while.begin();\n        REQUIRE(it == take_while.begin());\n        REQUIRE(it != take_while.end());\n        REQUIRE(take_while.begin() == it);\n        REQUIRE(take_while.end() != it);\n        it = take_while.end();\n        REQUIRE(it == take_while.end());\n        REQUIRE(it != take_while.begin());\n        REQUIRE(take_while.end() == it);\n        REQUIRE(take_while.begin() != it);\n    }\n\n    SUBCASE(\"Operator--\") {\n        auto expected = { 4, 3, 2, 1 };\n        REQUIRE(lz::equal(take_while | lz::reverse, expected));\n    }\n}\n\nTEST_CASE(\"take_while_iterable binary operations forward access\") {\n    std::forward_list<int> fl{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n    auto take_while = lz::take_while(fl, [](int element) { return element < 5; });\n\n    SUBCASE(\"Operator++\") {\n        auto expected = { 1, 2, 3, 4 };\n        REQUIRE(lz::equal(take_while, expected));\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        auto it = take_while.begin();\n        REQUIRE(it == take_while.begin());\n        REQUIRE(it != take_while.end());\n        REQUIRE(take_while.begin() == it);\n        REQUIRE(take_while.end() != it);\n        it = take_while.end();\n        REQUIRE(it == take_while.end());\n        REQUIRE(it != take_while.begin());\n        REQUIRE(take_while.end() == it);\n        REQUIRE(take_while.begin() != it);\n    }\n}\n\nTEST_CASE(\"take_while_iterable to containers\") {\n    constexpr size_t size = 10;\n    std::array<int, size> array{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n    auto take_while = lz::take_while(array, [](int element) { return element < 5; });\n\n    SUBCASE(\"To array\") {\n        REQUIRE(lz::distance(take_while) == 4);\n        auto arr = take_while | lz::to<std::array<int, 4>>();\n        auto expected = { 1, 2, 3, 4 };\n        REQUIRE(lz::equal(arr, expected));\n    }\n\n    SUBCASE(\"To vector\") {\n        auto vec = take_while | lz::to<std::vector>();\n        REQUIRE(lz::equal(vec, take_while));\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        auto lst = take_while | lz::to<std::list>();\n        REQUIRE(lz::equal(lst, take_while));\n    }\n\n    SUBCASE(\"To map\") {\n        auto map = take_while | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>();\n        REQUIRE(map.size() == 4);\n        std::map<int, int> expected = { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 } };\n        REQUIRE(map == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto map = take_while | lz::map([](int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>();\n        REQUIRE(map.size() == 4);\n        std::unordered_map<int, int> expected = { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 } };\n        REQUIRE(map == expected);\n    }\n}\n"
  },
  {
    "path": "tests/unique.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/unique.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Unique using sentinels\") {\n    auto str = lz::c_string(\"aabbcccddefgghhj\");\n    lz::unique_iterable<decltype(str)> unique = lz::unique(str);\n    static_assert(!std::is_same<decltype(unique.begin()), decltype(unique.end())>::value, \"Should be sentinel\");\n    auto expected = lz::c_string(\"abcdefghj\");\n    REQUIRE(lz::equal(unique, expected));\n\n    SUBCASE(\"Operator=(default_sentinel_t)\") {\n        std::forward_list<int> lst{ 1, 1, 2, 2, 3, 4, 4 };\n        auto unique2 = lz::unique(lst);\n        auto common = make_sentinel_assign_op_tester(unique2);\n        auto expected2 = { 1, 2, 3, 4 };\n        REQUIRE(lz::equal(common, expected2));\n    }\n}\n\nTEST_CASE(\"Unique changing and creating elements\") {\n    std::array<int, 4> arr = { 3, 2, 3, 1 };\n    std::sort(arr.begin(), arr.end());\n    auto unique = arr | lz::unique;\n    auto beg = unique.begin();\n    constexpr std::size_t size = 3;\n\n    REQUIRE(*beg == 1);\n    REQUIRE(static_cast<std::size_t>(std::distance(beg, unique.end())) == size);\n\n    SUBCASE(\"Should be unique\") {\n        std::array<int, size> expected = { 1, 2, 3 };\n        REQUIRE(expected == (unique | lz::to<std::array<int, size>>()));\n    }\n\n    SUBCASE(\"Should be unique too, using >\") {\n        std::array<int, size> expected = { 3, 2, 1 };\n        auto unique_greater = expected | lz::unique(std::greater<int>());\n        REQUIRE(expected == (unique_greater | lz::to<std::array<int, size>>()));\n        REQUIRE(std::is_sorted(expected.begin(), expected.end(), std::greater<int>()));\n    }\n}\n\nTEST_CASE(\"Unique binary operations\") {\n    std::array<int, 5> arr = { 1, 2, 2, 3, 3 };\n    auto expected = { 1, 2, 3 };\n\n    SUBCASE(\"Operator++\") {\n        REQUIRE(lz::equal(lz::unique(arr), expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        REQUIRE(lz::equal(arr | lz::unique | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"Operator==, operator!=\") {\n        auto unique = lz::unique(arr);\n        auto beg = unique.begin();\n        REQUIRE(beg != unique.end());\n        beg = unique.end();\n        REQUIRE(beg == unique.end());\n    }\n}\n\nTEST_CASE(\"Empty or one element unique\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> vec;\n        auto unique = lz::unique(vec);\n        REQUIRE(lz::empty(unique));\n        REQUIRE_FALSE(lz::has_many(unique));\n        REQUIRE_FALSE(lz::has_one(unique));\n    }\n\n    SUBCASE(\"One element\") {\n        std::vector<int> vec = { 1 };\n        auto unique = lz::unique(vec);\n        REQUIRE(lz::has_one(unique));\n        REQUIRE_FALSE(lz::has_many(unique));\n        REQUIRE_FALSE(lz::empty(unique));\n    }\n}\n\nTEST_CASE(\"Unique to container\") {\n    std::array<int, 4> arr = { 3, 2, 3, 1 };\n    std::sort(arr.begin(), arr.end());\n    constexpr std::size_t size = 3;\n    auto unique = lz::unique(arr);\n\n    SUBCASE(\"To array\") {\n        auto unique_array = unique | lz::to<std::array<int, size>>();\n        std::array<int, size> expected = { 1, 2, 3 };\n        REQUIRE(unique_array == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        auto unique_vec = unique | lz::to<std::vector>();\n        std::vector<int> expected = { 1, 2, 3 };\n        REQUIRE(unique_vec == expected);\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        auto unique_list = unique | lz::to<std::list<int>>();\n        std::list<int> expected = { 1, 2, 3 };\n        REQUIRE(unique_list == expected);\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = unique | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::map<int, int>>();\n\n        std::map<int, int> expected = {\n            std::make_pair(1, 1),\n            std::make_pair(2, 2),\n            std::make_pair(3, 3),\n        };\n\n        REQUIRE(expected == actual);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        auto actual = unique | lz::map([](const int i) { return std::make_pair(i, i); }) | lz::to<std::unordered_map<int, int>>();\n\n        std::unordered_map<int, int> expected = {\n            std::make_pair(1, 1),\n            std::make_pair(2, 2),\n            std::make_pair(3, 3),\n        };\n\n        REQUIRE(expected == actual);\n    }\n}\n"
  },
  {
    "path": "tests/zip.cpp",
    "content": "#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/algorithm/empty.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/zip.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Zip with sentinels\") {\n    auto cstr = lz::c_string(\"Hello\");\n    auto cstr2 = lz::c_string(\"World!!\");\n    lz::zip_iterable<decltype(cstr), decltype(cstr2)> zip = lz::zip(cstr, cstr2);\n    std::vector<std::tuple<char, char>> expected = { std::make_tuple('H', 'W'), std::make_tuple('e', 'o'),\n                                                     std::make_tuple('l', 'r'), std::make_tuple('l', 'l'),\n                                                     std::make_tuple('o', 'd') };\n    REQUIRE(lz::equal(zip, expected));\n    static_assert(!std::is_same<decltype(zip.begin()), decltype(zip.end())>::value, \"Should be sentinel-like\");\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> a = { 1, 2, 3 };\n        std::forward_list<int> b = { 4, 5, 6, 7, 8 };\n        auto zipped = lz::zip(a, b);\n        auto common = make_sentinel_assign_op_tester(zipped);\n        auto expected2 = { std::make_tuple(1, 4), std::make_tuple(2, 5), std::make_tuple(3, 6) };\n        REQUIRE(lz::equal(common, expected2));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::list<int> a = { 1, 2, 3 };\n        std::list<int> b = { 4, 5, 6, 7, 8 };\n        auto zipped = lz::zip(make_sized_bidi_sentinelled(a), b);\n        auto common = make_sentinel_assign_op_tester(zipped);\n        auto expected2 = { std::make_tuple(1, 4), std::make_tuple(2, 5), std::make_tuple(3, 6) };\n        REQUIRE(lz::equal(common | lz::reverse, expected2 | lz::reverse));\n    }\n\n    SUBCASE(\"random access\") {\n        auto a = lz::repeat(1, 5);\n        auto b = lz::repeat(2, 3);\n        auto zipped = lz::zip(a, b);\n        auto common = make_sentinel_assign_op_tester(zipped);\n        std::vector<std::tuple<int, int>> expected2 = { std::make_tuple(1, 2), std::make_tuple(1, 2), std::make_tuple(1, 2) };\n        test_procs::test_operator_minus(common);\n        test_procs::test_operator_plus(common, expected2);\n    }\n}\n\nTEST_CASE(\"zip_iterable changing and creating elements\") {\n    std::vector<int> a = { 1, 2, 3, 4 };\n    std::vector<float> b = { 1.f, 2.f, 3.f, 4.f };\n\n    SUBCASE(\"Unequal lengths\") {\n        std::vector<int> ints = { 1, 2, 3, 4, 5 };\n        std::vector<size_t> floats = { 1, 3 };\n\n        auto zipper = ints | lz::zip(floats);\n        static_assert(std::is_same<typename decltype(zipper.begin())::reference, std::tuple<int&, size_t&>>::value,\n                      \"should be tuple ref\");\n        std::array<std::tuple<int, size_t>, 2> expected = { std::make_tuple(2, size_t{ 3 }), std::make_tuple(1, size_t{ 1 }) };\n        REQUIRE(lz::equal(lz::reverse(zipper), expected));\n        REQUIRE(lz::equal(zipper, expected | lz::reverse));\n    }\n}\n\nTEST_CASE(\"Empty or one element zip\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> empty;\n        auto empty2 = lz::c_string(\"\");\n        auto zipper = lz::zip(empty, empty2);\n        static_assert(!std::is_same<decltype(zipper.end()), decltype(zipper.begin())>::value, \"should be sentinel like\");\n        REQUIRE(lz::empty(zipper));\n        REQUIRE_FALSE(lz::has_many(zipper));\n        REQUIRE_FALSE(lz::has_one(zipper));\n    }\n\n    SUBCASE(\"One element 1\") {\n        std::vector<int> one = { 1 };\n        std::vector<int> empty;\n        auto zipper = lz::zip(one, empty);\n        REQUIRE(lz::empty(zipper));\n        REQUIRE_FALSE(lz::has_many(zipper));\n        REQUIRE_FALSE(lz::has_one(zipper));\n    }\n\n    SUBCASE(\"One element 2\") {\n        std::vector<int> empty;\n        std::vector<int> one = { 1 };\n        auto zipper = lz::zip(empty, one);\n        REQUIRE(lz::empty(zipper));\n        REQUIRE_FALSE(lz::has_many(zipper));\n        REQUIRE_FALSE(lz::has_one(zipper));\n    }\n\n    SUBCASE(\"One element 3\") {\n        std::vector<int> one = { 1 };\n        std::vector<int> one2 = { 1 };\n        auto zipper = lz::zip(one, one2);\n        REQUIRE_FALSE(lz::empty(zipper));\n        REQUIRE_FALSE(lz::has_many(zipper));\n        REQUIRE(lz::has_one(zipper));\n    }\n}\n\nTEST_CASE(\"zip_iterable binary operations\") {\n    constexpr std::size_t size = 4;\n    std::vector<int> a = { 1, 2, 3, 4 };\n    std::vector<size_t> b = { 1, 2, 3, 4 };\n    std::array<short, size> c = { 1, 2, 3, 4 };\n\n    std::vector<std::tuple<int, size_t, short>> expected = { std::make_tuple(1, size_t{ 1 }, short(1)),\n                                                             std::make_tuple(2, size_t{ 2 }, short(2)),\n                                                             std::make_tuple(3, size_t{ 3 }, short(3)),\n                                                             std::make_tuple(4, size_t{ 4 }, short(4)) };\n\n    auto zipper = lz::zip(a, b, c);\n    auto begin = zipper.begin();\n\n    SUBCASE(\"Operator++\") {\n        REQUIRE(lz::equal(zipper, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        REQUIRE(lz::equal(zipper | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        REQUIRE(begin != zipper.end());\n        REQUIRE(zipper.end() != begin);\n        begin = zipper.end();\n        REQUIRE(begin == zipper.end());\n        REQUIRE(zipper.end() == begin);\n    }\n\n    SUBCASE(\"Operator+(int)\") {\n        test_procs::test_operator_plus(zipper, expected);\n    }\n\n    SUBCASE(\"Operator-(Iterator)\") {\n        test_procs::test_operator_minus(zipper);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel_t)\") {\n        auto first = lz::repeat(1, 5), second = lz::repeat(2, 5);\n        test_procs::test_operator_minus(lz::zip(first, second));\n        second = lz::repeat(2, 3);\n        test_procs::test_operator_minus(lz::zip(first, second));\n    }\n\n    SUBCASE(\"Operator+(default_sentinel_t)\") {\n        auto first = lz::repeat(1, 4), second = lz::repeat(2, 5);\n        std::vector<std::tuple<int, int>> expected2 = {\n            std::make_tuple(1, 2),\n            std::make_tuple(1, 2),\n            std::make_tuple(1, 2),\n            std::make_tuple(1, 2),\n        };\n\n        test_procs::test_operator_plus(lz::zip(first, second), expected2);\n    }\n}\n\nTEST_CASE(\"zip_iterable to containers\") {\n    constexpr std::size_t size = 4;\n    std::vector<int> a = { 1, 2, 3, 4 };\n    std::vector<float> b = { 1.f, 2.f, 3.f, 4.f };\n    std::array<short, size> c = { 1, 2, 3, 4 };\n\n    using tup = std::tuple<int, float, short>;\n\n    SUBCASE(\"To array\") {\n        auto array = lz::zip(a, b, c) | lz::to<std::array<tup, size>>();\n        std::array<tup, size> expected = { std::make_tuple(1, 1.f, static_cast<short>(1)),\n                                           std::make_tuple(2, 2.f, static_cast<short>(2)),\n                                           std::make_tuple(3, 3.f, static_cast<short>(3)),\n                                           std::make_tuple(4, 4.f, static_cast<short>(4)) };\n        REQUIRE(array == expected);\n    }\n\n    SUBCASE(\"To vector\") {\n        auto vector = lz::zip(a, b, c) | lz::to<std::vector>();\n        std::vector<tup> expected = { std::make_tuple(1, 1.f, static_cast<short>(1)),\n                                      std::make_tuple(2, 2.f, static_cast<short>(2)),\n                                      std::make_tuple(3, 3.f, static_cast<short>(3)),\n                                      std::make_tuple(4, 4.f, static_cast<short>(4)) };\n        REQUIRE(vector == expected);\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        auto list = lz::zip(a, b, c) | lz::to<std::list<tup>>();\n        std::list<tup> expected = { std::make_tuple(1, 1.f, static_cast<short>(1)),\n                                    std::make_tuple(2, 2.f, static_cast<short>(2)),\n                                    std::make_tuple(3, 3.f, static_cast<short>(3)),\n                                    std::make_tuple(4, 4.f, static_cast<short>(4)) };\n        REQUIRE(list == expected);\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = lz::zip(a, b, c) | lz::map([](const tup& t) { return std::make_pair(std::get<0>(t), t); }) |\n                      lz::to<std::map<int, tup>>();\n\n        std::map<int, tup> expected = { std::make_pair(1, std::make_tuple(1, 1.f, static_cast<short>(1))),\n                                        std::make_pair(2, std::make_tuple(2, 2.f, static_cast<short>(2))),\n                                        std::make_pair(3, std::make_tuple(3, 3.f, static_cast<short>(3))),\n                                        std::make_pair(4, std::make_tuple(4, 4.f, static_cast<short>(4))) };\n\n        REQUIRE(actual == expected);\n    }\n\n    SUBCASE(\"To map\") {\n        auto actual = lz::zip(a, b, c) | lz::map([](const tup& t) { return std::make_pair(std::get<0>(t), t); }) |\n                      lz::to<std::unordered_map<int, tup>>();\n\n        std::unordered_map<int, tup> expected = { std::make_pair(1, std::make_tuple(1, 1.f, static_cast<short>(1))),\n                                                  std::make_pair(2, std::make_tuple(2, 2.f, static_cast<short>(2))),\n                                                  std::make_pair(3, std::make_tuple(3, 3.f, static_cast<short>(3))),\n                                                  std::make_pair(4, std::make_tuple(4, 4.f, static_cast<short>(4)))\n\n        };\n\n        REQUIRE(actual == expected);\n    }\n}\n"
  },
  {
    "path": "tests/zip_longest.cpp",
    "content": "#include <Lz/algorithm/empty.hpp>\n#include <Lz/algorithm/equal.hpp>\n#include <Lz/algorithm/has_many.hpp>\n#include <Lz/algorithm/has_one.hpp>\n#include <Lz/c_string.hpp>\n#include <Lz/filter.hpp>\n#include <Lz/map.hpp>\n#include <Lz/procs/to.hpp>\n#include <Lz/repeat.hpp>\n#include <Lz/reverse.hpp>\n#include <Lz/zip_longest.hpp>\n#include <cpp-lazy-ut-helper/pch.hpp>\n#include <cpp-lazy-ut-helper/test_procs.hpp>\n#include <cpp-lazy-ut-helper/ut_helper.hpp>\n#include <doctest/doctest.h>\n\nTEST_CASE(\"Zip longest with sentinels\") {\n    auto cstr = lz::c_string(\"Hello\");\n    auto cstr2 = lz::c_string(\"Hello1\");\n    auto cstr3 = lz::c_string(\"S\");\n    lz::zip_longest_iterable<decltype(cstr), decltype(cstr2), decltype(cstr3)> longest = lz::zip_longest(cstr, cstr2, cstr3);\n    static_assert(!std::is_same<decltype(longest.begin()), decltype(longest.end())>::value, \"should be sentinel\");\n    REQUIRE(lz::distance(longest) == static_cast<std::ptrdiff_t>(std::strlen(\"Hello1\")));\n\n    using tup = std::tuple<lz::optional<char>, lz::optional<char>, lz::optional<char>>;\n    std::vector<tup> expected = { std::make_tuple(lz::optional<char>('H'), lz::optional<char>('H'), lz::optional<char>('S')),\n                                  std::make_tuple(lz::optional<char>('e'), lz::optional<char>('e'), lz::nullopt),\n                                  std::make_tuple(lz::optional<char>('l'), lz::optional<char>('l'), lz::nullopt),\n                                  std::make_tuple(lz::optional<char>('l'), lz::optional<char>('l'), lz::nullopt),\n                                  std::make_tuple(lz::optional<char>('o'), lz::optional<char>('o'), lz::nullopt),\n                                  std::make_tuple(lz::nullopt, lz::optional<char>('1'), lz::nullopt) };\n\n    REQUIRE(lz::equal(longest, expected));\n}\n\nTEST_CASE(\"Operator=(default_sentinel_t)\") {\n    SUBCASE(\"forward\") {\n        std::forward_list<int> a = { 1, 2, 3 };\n        std::forward_list<int> b = { 4, 5, 6, 7, 8 };\n        auto zipped = lz::zip_longest(a, b);\n\n        auto common = make_sentinel_assign_op_tester(zipped);\n\n        auto expected = { std::make_tuple(lz::optional<int>{ 1 }, lz::optional<int>{ 4 }),\n                           std::make_tuple(lz::optional<int>{ 2 }, lz::optional<int>{ 5 }),\n                           std::make_tuple(lz::optional<int>{ 3 }, lz::optional<int>{ 6 }),\n                           std::make_tuple(lz::optional<int>{}, lz::optional<int>{ 7 }),\n                           std::make_tuple(lz::optional<int>{}, lz::optional<int>{ 8 }) };\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"bidirectional\") {\n        std::vector<int> a = { 1, 2, 3 };\n        std::vector<int> b = { 4, 5, 6, 7, 8 };\n        auto zipped = lz::zip_longest(make_sized_bidi_sentinelled(a), b);\n\n        auto common = make_sentinel_assign_op_tester(zipped);\n\n        auto expected = { std::make_tuple(lz::optional<int>{ 1 }, lz::optional<int>{ 4 }),\n                           std::make_tuple(lz::optional<int>{ 2 }, lz::optional<int>{ 5 }),\n                           std::make_tuple(lz::optional<int>{ 3 }, lz::optional<int>{ 6 }),\n                           std::make_tuple(lz::optional<int>{}, lz::optional<int>{ 7 }),\n                           std::make_tuple(lz::optional<int>{}, lz::optional<int>{ 8 }) };\n        REQUIRE(lz::equal(common | lz::reverse, expected | lz::reverse));\n        REQUIRE(lz::equal(common, expected));\n    }\n\n    SUBCASE(\"random access\") {\n        auto a = lz::repeat(1, 3);\n        auto b = lz::repeat(4, 5);\n        auto zipped = lz::zip_longest(a, b);\n\n        auto common = make_sentinel_assign_op_tester(zipped);\n\n        auto expected = { std::make_tuple(lz::optional<int>{ 1 }, lz::optional<int>{ 4 }),\n                           std::make_tuple(lz::optional<int>{ 1 }, lz::optional<int>{ 4 }),\n                           std::make_tuple(lz::optional<int>{ 1 }, lz::optional<int>{ 4 }),\n                           std::make_tuple(lz::optional<int>{}, lz::optional<int>{ 4 }),\n                           std::make_tuple(lz::optional<int>{}, lz::optional<int>{ 4 }) };\n        test_procs::test_operator_minus(common);\n        test_procs::test_operator_plus(common, expected);\n    }\n}\n\nTEST_CASE(\"zip_longest_iterable changing and creating elements\") {\n    std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7 };\n    const std::list<char> v2 = { 'a', 'b', 'c' };\n    std::vector<char> v3 = { 'a', 'b', 'c', 'd' };\n\n    auto bidi = lz::zip_longest(v, v2, v3);\n    auto ra = lz::zip_longest(v, v3);\n    static_assert(lz::detail::is_bidi<decltype(bidi.begin())>::value, \"\");\n    static_assert(lz::detail::is_ra<decltype(ra.begin())>::value, \"\");\n    REQUIRE(ra.size() == v.size());\n    REQUIRE(bidi.size() == v.size());\n\n    SUBCASE(\"Unequal lengths\") {\n        REQUIRE(std::distance(bidi.begin(), bidi.end()) == 7);\n        REQUIRE(std::distance(ra.begin(), ra.end()) == 7);\n        REQUIRE(bidi.size() == 7);\n        REQUIRE(ra.size() == 7);\n    }\n\n    SUBCASE(\"Should be by ref\") {\n        static_assert(\n            std::is_same<decltype(*bidi.begin()),\n                         std::tuple<lz::optional<std::reference_wrapper<int>>, lz::optional<std::reference_wrapper<const char>>,\n                                    lz::optional<std::reference_wrapper<char>>>>::value,\n            \"\");\n    }\n}\n\nTEST_CASE(\"Zip longest bidi and not sized\") {\n    std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7 };\n    auto filter = v | lz::filter([](int i) { return i % 2 == 0; });\n    auto zip = lz::zip_longest(v, filter);\n    static_assert(lz::detail::is_bidi<decltype(zip.begin())>::value, \"Should not be bidi\");\n    static_assert(!lz::detail::is_sized<decltype(zip.begin())>::value, \"Should not be sized\");\n\n    std::tuple<lz::optional<int>, lz::optional<int>> expected[] = { { 1, 2 },           { 2, 4 },           { 3, 6 },\n                                                                    { 4, lz::nullopt }, { 5, lz::nullopt }, { 6, lz::nullopt },\n                                                                    { 7, lz::nullopt } };\n    REQUIRE(lz::equal(zip, expected));\n}\n\nTEST_CASE(\"zip_longest_iterable binary operations\") {\n    std::vector<int> a = { 1, 2, 3, 4 };\n    std::vector<unsigned> b = { 1, 2, 3, 4, 5 };\n    std::array<short, 2> c = { 1, 2 };\n    auto zipper = a | lz::zip_longest(b, c);\n\n    using tuple = std::tuple<lz::optional<int>, lz::optional<unsigned>, lz::optional<short>>;\n    auto expected = std::vector<tuple>{\n        { 1, 1, short{ 1 } }, { 2, 2, short{ 2 } }, { 3, 3, lz::nullopt }, { 4, 4, lz::nullopt }, { lz::nullopt, 5, lz::nullopt }\n    };\n\n    SUBCASE(\"Operator++\") {\n        REQUIRE(lz::equal(zipper, expected));\n    }\n\n    SUBCASE(\"Operator--\") {\n        REQUIRE(lz::equal(zipper | lz::reverse, expected | lz::reverse));\n    }\n\n    SUBCASE(\"Operator== & Operator!=\") {\n        auto begin = zipper.begin();\n        REQUIRE(begin != zipper.end());\n        begin = zipper.end();\n        REQUIRE(begin == zipper.end());\n    }\n\n    SUBCASE(\"Operator+\") {\n        test_procs::test_operator_plus(zipper, expected);\n    }\n\n    SUBCASE(\"Operator-(Iterator)\") {\n        test_procs::test_operator_minus(zipper);\n    }\n\n    SUBCASE(\"Operator-(default_sentinel)\") {\n        auto first = lz::repeat(1, 5), second = lz::repeat(1, 4);\n        auto l = lz::zip_longest(first, second);\n        REQUIRE(lz::size(l) == 5);\n        test_procs::test_operator_minus(l);\n    }\n\n    SUBCASE(\"Operator+(default_sentinel)\") {\n        auto first = lz::repeat(1, 5), second = lz::repeat(1, 4);\n        auto l = lz::zip_longest(first, second);\n        std::vector<std::tuple<lz::optional<int>, lz::optional<int>>> expected2 = {\n            { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, lz::nullopt }\n        };\n        test_procs::test_operator_plus(l, expected2);\n    }\n}\n\nTEST_CASE(\"Empty and one element zip longest\") {\n    SUBCASE(\"Empty\") {\n        std::vector<int> a;\n        auto b = lz::c_string(\"\");\n        auto zipper = lz::zip_longest(a, b);\n        static_assert(std::is_same<decltype(zipper.end()), lz::default_sentinel_t>::value, \"should be default sentinel\");\n        REQUIRE(lz::empty(zipper));\n        REQUIRE_FALSE(lz::has_one(zipper));\n        REQUIRE_FALSE(lz::has_many(zipper));\n    }\n\n    SUBCASE(\"One element 1\") {\n        std::vector<int> a = { 1 };\n        std::vector<float> b;\n        auto zipper = lz::zip_longest(a, b);\n        REQUIRE_FALSE(lz::empty(zipper));\n        REQUIRE(lz::has_one(zipper));\n        REQUIRE_FALSE(lz::has_many(zipper));\n    }\n\n    SUBCASE(\"One element 2\") {\n        std::vector<int> a;\n        std::vector<float> b = { 1 };\n        auto zipper = lz::zip_longest(a, b);\n        REQUIRE_FALSE(lz::empty(zipper));\n        REQUIRE(lz::has_one(zipper));\n        REQUIRE_FALSE(lz::has_many(zipper));\n    }\n}\n\nTEST_CASE(\"Zip longest iterable to container\") {\n    std::array<int, 4> a = { 1, 2, 3, 4 };\n    std::list<unsigned> b = { 1, 2, 3, 4, 5 };\n    std::vector<char> c = { 'a', 'b', 'c', 'd', 'f', 'g' };\n    auto zipper = lz::zip_longest(a, b, c);\n\n    SUBCASE(\"To array\") {\n        using optional_tuple = std::tuple<lz::optional<int>, lz::optional<unsigned>, lz::optional<char>>;\n        auto to_arr = zipper | lz::to<std::array<optional_tuple, 6>>();\n        std::array<optional_tuple, 6> expected = {\n            std::make_tuple(1, 1, 'a'), std::make_tuple(2, 2, 'b'),           std::make_tuple(3, 3, 'c'),\n            std::make_tuple(4, 4, 'd'), std::make_tuple(lz::nullopt, 5, 'f'), std::make_tuple(lz::nullopt, lz::nullopt, 'g')\n        };\n        REQUIRE(lz::equal(to_arr, expected));\n    }\n\n    SUBCASE(\"To vector\") {\n        using optional_tuple = std::tuple<lz::optional<int>, lz::optional<unsigned>, lz::optional<char>>;\n        auto to_vec = zipper | lz::to<std::vector<optional_tuple>>();\n        std::vector<optional_tuple> expected = {\n            std::make_tuple(1, 1, 'a'), std::make_tuple(2, 2, 'b'),           std::make_tuple(3, 3, 'c'),\n            std::make_tuple(4, 4, 'd'), std::make_tuple(lz::nullopt, 5, 'f'), std::make_tuple(lz::nullopt, lz::nullopt, 'g')\n        };\n        REQUIRE(lz::equal(to_vec, expected));\n    }\n\n    SUBCASE(\"To other container using to<>()\") {\n        using optional_tuple = std::tuple<lz::optional<int>, lz::optional<unsigned>, lz::optional<char>>;\n        auto to_list = zipper | lz::to<std::list<optional_tuple>>();\n        std::list<optional_tuple> expected = {\n            std::make_tuple(1, 1, 'a'), std::make_tuple(2, 2, 'b'),           std::make_tuple(3, 3, 'c'),\n            std::make_tuple(4, 4, 'd'), std::make_tuple(lz::nullopt, 5, 'f'), std::make_tuple(lz::nullopt, lz::nullopt, 'g')\n        };\n        REQUIRE(lz::equal(to_list, expected));\n    }\n\n    SUBCASE(\"To map\") {\n        using tuple_ref = lz::detail::ref_t<decltype(zipper.begin())>;\n        using map_type = std::map<char, lz::optional<unsigned>>;\n\n        auto to_map = zipper |\n                      lz::map([](const tuple_ref& tup) { return std::make_pair(std::get<2>(tup).value(), std::get<1>(tup)); }) |\n                      lz::to<map_type>();\n        map_type expected = { { 'a', 1 }, { 'b', 2 }, { 'c', 3 }, { 'd', 4 }, { 'f', 5 }, { 'g', lz::nullopt } };\n        REQUIRE(to_map == expected);\n    }\n\n    SUBCASE(\"To unordered map\") {\n        using tuple_ref = lz::detail::ref_t<decltype(zipper.begin())>;\n        using map_type = std::unordered_map<char, lz::optional<unsigned>>;\n\n        auto to_unordered =\n            zipper | lz::map([](const tuple_ref& tup) { return std::make_pair(std::get<2>(tup).value(), std::get<1>(tup)); }) |\n            lz::to<map_type>();\n        map_type expected = { { 'a', 1 }, { 'b', 2 }, { 'c', 3 }, { 'd', 4 }, { 'f', 5 }, { 'g', lz::nullopt } };\n        REQUIRE(to_unordered == expected);\n    }\n}\n"
  }
]