├── .clang-format ├── .github └── workflows │ ├── codecov.yml │ ├── codeql-analysis.yml │ ├── docs.yml │ ├── generate-single-header.yml │ ├── linux.yml │ ├── macos.yml │ ├── santizers.yml │ └── windows.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE_1_0.txt ├── README.md ├── benchmark ├── CMakeLists.txt ├── internal_iteration_benchmark.cpp ├── multidimensional_memset_benchmark.cpp ├── multidimensional_memset_benchmark_kernels.cpp ├── ranges_cartesian_product.hpp ├── ranges_concat.hpp └── sort_benchmark.cpp ├── cmake ├── FindSphinx.cmake └── flux-config.cmake.in ├── docs ├── .gitignore ├── CMakeLists.txt ├── conf.py ├── index.rst ├── reference.rst ├── reference │ ├── adaptors.rst │ ├── algorithms.rst │ ├── concepts.rst │ ├── config.rst │ ├── factories.rst │ └── sequence_access.rst └── requirements.txt ├── example ├── CMakeLists.txt ├── calendar.cpp ├── config_parser.cpp ├── docs │ ├── adjacent.cpp │ ├── adjacent_filter.cpp │ ├── all.cpp │ ├── any.cpp │ ├── assert.hpp │ ├── cartesian_power.cpp │ ├── cartesian_power_map.cpp │ ├── cartesian_product.cpp │ ├── cartesian_product_map.cpp │ ├── compare.cpp │ ├── contains.cpp │ ├── count.cpp │ ├── cursors.cpp │ ├── cycle.cpp │ ├── drop.cpp │ ├── ends_with.cpp │ ├── find_max.cpp │ ├── find_min.cpp │ ├── find_minmax.cpp │ ├── mask.cpp │ ├── prescan.cpp │ ├── read_only.cpp │ ├── repeat.cpp │ ├── scan.cpp │ ├── scan_first.cpp │ ├── set_difference.cpp │ ├── set_intersection.cpp │ ├── set_symmetric_difference.cpp │ ├── set_union.cpp │ ├── split.cpp │ ├── starts_with.cpp │ └── unfold.cpp ├── histogram.cpp ├── merge_intervals.cpp ├── moving_average.cpp ├── prime_numbers.cpp ├── shortest_path.cpp ├── top10 │ ├── 01_trapping_rain_water.cpp │ ├── 02_max_consecutive_ones.cpp │ ├── 03_longest_continuous_increasing_subsequence.cpp │ ├── 04_maximum_subarray_sum.cpp │ ├── 05_sushi_for_two.cpp │ ├── 06_max_gap.cpp │ ├── 07_max_gap_count.cpp │ ├── 08_three_consecutive_odds.cpp │ ├── 09_skyline.cpp │ ├── 10_ocean_view.cpp │ └── README.md └── word_count.cpp ├── include ├── flux.hpp └── flux │ ├── adaptor.hpp │ ├── adaptor │ ├── adjacent.hpp │ ├── adjacent_filter.hpp │ ├── cache_last.hpp │ ├── cartesian_base.hpp │ ├── cartesian_power.hpp │ ├── cartesian_power_map.hpp │ ├── cartesian_product.hpp │ ├── cartesian_product_map.hpp │ ├── chain.hpp │ ├── chunk.hpp │ ├── chunk_by.hpp │ ├── cursors.hpp │ ├── cycle.hpp │ ├── drop.hpp │ ├── drop_while.hpp │ ├── filter.hpp │ ├── filter_map.hpp │ ├── flatten.hpp │ ├── flatten_with.hpp │ ├── map.hpp │ ├── mask.hpp │ ├── read_only.hpp │ ├── reverse.hpp │ ├── scan.hpp │ ├── scan_first.hpp │ ├── set_adaptors.hpp │ ├── slide.hpp │ ├── split.hpp │ ├── split_string.hpp │ ├── stride.hpp │ ├── take.hpp │ ├── take_while.hpp │ ├── unchecked.hpp │ └── zip.hpp │ ├── algorithm.hpp │ ├── algorithm │ ├── all_any_none.hpp │ ├── compare.hpp │ ├── contains.hpp │ ├── count.hpp │ ├── detail │ │ ├── heap_ops.hpp │ │ └── pdqsort.hpp │ ├── ends_with.hpp │ ├── equal.hpp │ ├── fill.hpp │ ├── find.hpp │ ├── find_min_max.hpp │ ├── fold.hpp │ ├── for_each.hpp │ ├── inplace_reverse.hpp │ ├── minmax.hpp │ ├── output_to.hpp │ ├── search.hpp │ ├── sort.hpp │ ├── starts_with.hpp │ ├── swap_elements.hpp │ ├── to.hpp │ ├── write_to.hpp │ └── zip_algorithms.hpp │ ├── core.hpp │ ├── core │ ├── assert.hpp │ ├── concepts.hpp │ ├── config.hpp │ ├── default_impls.hpp │ ├── detail │ │ └── jtckdint.h │ ├── functional.hpp │ ├── inline_sequence_base.hpp │ ├── numeric.hpp │ ├── operation_requirements.hpp │ ├── optional.hpp │ ├── ref.hpp │ ├── sequence_access.hpp │ ├── sequence_iterator.hpp │ ├── slice.hpp │ └── utils.hpp │ ├── macros.hpp │ ├── sequence.hpp │ └── sequence │ ├── array_ptr.hpp │ ├── bitset.hpp │ ├── empty.hpp │ ├── generator.hpp │ ├── getlines.hpp │ ├── iota.hpp │ ├── istream.hpp │ ├── istreambuf.hpp │ ├── range.hpp │ ├── repeat.hpp │ ├── single.hpp │ └── unfold.hpp ├── module ├── CMakeLists.txt └── flux.cpp ├── single_include └── flux.hpp ├── test ├── CMakeLists.txt ├── num │ ├── test_casts.cpp │ ├── test_checked_ops.cpp │ ├── test_concepts.cpp │ ├── test_default_ops.cpp │ ├── test_overflowing_ops.cpp │ ├── test_unchecked_ops.cpp │ └── test_wrapping_ops.cpp ├── test_adjacent.cpp ├── test_adjacent_filter.cpp ├── test_adjacent_map.cpp ├── test_all_any_none.cpp ├── test_apply.cpp ├── test_array_ptr.cpp ├── test_bitset.cpp ├── test_bounds_checked.cpp ├── test_cache_last.cpp ├── test_cartesian_power.cpp ├── test_cartesian_power_map.cpp ├── test_cartesian_product.cpp ├── test_cartesian_product_map.cpp ├── test_chain.cpp ├── test_chunk.cpp ├── test_chunk_by.cpp ├── test_compare.cpp ├── test_concepts.cpp ├── test_contains.cpp ├── test_count.cpp ├── test_count_if.cpp ├── test_cursors.cpp ├── test_cycle.cpp ├── test_drop.cpp ├── test_drop_while.cpp ├── test_empty.cpp ├── test_ends_with.cpp ├── test_equal.cpp ├── test_fill.cpp ├── test_filter.cpp ├── test_filter_map.cpp ├── test_find.cpp ├── test_find_if.cpp ├── test_find_if_not.cpp ├── test_find_min_max.cpp ├── test_flatten.cpp ├── test_flatten_with.cpp ├── test_fold.cpp ├── test_for_each.cpp ├── test_from_range.cpp ├── test_front_back.cpp ├── test_generator.cpp ├── test_getlines.cpp ├── test_iota.cpp ├── test_istream.cpp ├── test_istreambuf.cpp ├── test_map.cpp ├── test_mask.cpp ├── test_minmax.cpp ├── test_module_import.cpp ├── test_optional.cpp ├── test_output_to.cpp ├── test_predicates.cpp ├── test_range_iface.cpp ├── test_read_only.cpp ├── test_repeat.cpp ├── test_reverse.cpp ├── test_scan.cpp ├── test_set_adaptors.cpp ├── test_single.cpp ├── test_slide.cpp ├── test_sort.cpp ├── test_split.cpp ├── test_starts_with.cpp ├── test_stride.cpp ├── test_take.cpp ├── test_take_while.cpp ├── test_to.cpp ├── test_unchecked.cpp ├── test_unfold.cpp ├── test_utils.hpp ├── test_write_to.cpp ├── test_zip.cpp ├── test_zip_algorithms.cpp └── test_zip_map.cpp └── tools ├── CMakeLists.txt └── make_single_header.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | AlignAfterOpenBracket: Align 4 | AlignEscapedNewlines: Left 5 | #AllowBreakBeforeNoexceptSpecifier: OnlyWithParen 6 | AllowShortCaseLabelsOnASingleLine: true 7 | AlwaysBreakTemplateDeclarations: Yes 8 | AttributeMacros: 9 | - FLUX_NO_UNIQUE_ADDRESS 10 | BreakBeforeConceptDeclarations: Always 11 | BreakConstructorInitializers: BeforeColon 12 | ColumnLimit: 100 13 | Cpp11BracedListStyle: true 14 | FixNamespaceComments: true 15 | ForEachMacros: 16 | - FLUX_FOR 17 | IndentPPDirectives: AfterHash 18 | InsertBraces: true 19 | NamespaceIndentation: None 20 | PackConstructorInitializers: CurrentLine 21 | SortIncludes: Never 22 | TabWidth: 4 23 | -------------------------------------------------------------------------------- /.github/workflows/codecov.yml: -------------------------------------------------------------------------------- 1 | 2 | name: "Codecov" 3 | 4 | on: 5 | push: 6 | branches: [ main ] 7 | pull_request: 8 | branches: [ main ] 9 | 10 | jobs: 11 | run: 12 | name: Codecov 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@master 18 | 19 | - name: Set up Homebrew 20 | uses: Homebrew/actions/setup-homebrew@master 21 | 22 | - name: Build 23 | run: | 24 | brew install gcc@14 lcov ninja binutils 25 | brew link --force binutils 26 | cmake -E make_directory ${{runner.workspace}}/build 27 | cd ${{runner.workspace}}/build 28 | cmake ${GITHUB_WORKSPACE} -GNinja -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_CXX_FLAGS="--coverage" -DCMAKE_EXE_LINKER_FLAGS="--coverage" 29 | cmake --build . 30 | 31 | - name: Run tests 32 | working-directory: ${{runner.workspace}}/build 33 | run: ctest 34 | 35 | - name: Generate coverage report 36 | working-directory: ${{runner.workspace}}/build/test/CMakeFiles/test-flux.dir 37 | run: | 38 | lcov --directory . --capture --gcov gcov-14 --output-file coverage.info 39 | lcov --remove coverage.info '*/test/*' --output-file coverage.info 40 | 41 | - name: Upload coverage report 42 | uses: codecov/codecov-action@v4 43 | with: 44 | files: ${{runner.workspace}}/build/test/CMakeFiles/test-flux.dir/coverage.info 45 | token: ${{secrets.CODECOV_TOKEN}} 46 | verbose: true 47 | 48 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '28 13 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-24.04 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'cpp' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@master 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v3 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v3 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | #- run: | 71 | # ${{matrix.install}} 72 | # cmake -E make_directory ${{runner.workspace}}/build 73 | # cd ${{runner.workspace}}/build 74 | # cmake ${GITHUB_WORKSPACE} -GNinja -DCMAKE_CXX_COMPILER=g++-13 75 | # cmake --build . 76 | 77 | 78 | - name: Perform CodeQL Analysis 79 | uses: github/codeql-action/analyze@v3 80 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@master 14 | - name: Build documentation with Sphinx 15 | uses: ammaraskar/sphinx-action@master 16 | with: 17 | build-command: "sphinx-build -b html . html" 18 | docs-folder: 'docs/' 19 | - name: Upload pages artifact 20 | if: github.ref_name == 'main' 21 | uses: actions/upload-pages-artifact@v3 22 | with: 23 | path: docs/html/ 24 | 25 | deploy: 26 | if: github.ref_name == 'main' 27 | 28 | needs: build 29 | 30 | permissions: 31 | pages: write 32 | id-token: write 33 | 34 | environment: 35 | name: github-pages 36 | url: ${{ steps.deployment.outputs.page_url }} 37 | 38 | runs-on: ubuntu-latest 39 | steps: 40 | - name: Deploy to Github Pages 41 | id: deployment 42 | uses: actions/deploy-pages@v4 43 | -------------------------------------------------------------------------------- /.github/workflows/generate-single-header.yml: -------------------------------------------------------------------------------- 1 | 2 | name: "Generate Single Header" 3 | 4 | on: 5 | push: 6 | branches: [ main ] 7 | 8 | jobs: 9 | run: 10 | name: Generate 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | contents: write 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@master 19 | with: 20 | token: ${{ secrets.PAT }} 21 | 22 | - name: Set up Homebrew 23 | uses: Homebrew/actions/setup-homebrew@master 24 | 25 | - name: Build 26 | run: | 27 | brew install gcc@14 binutils 28 | brew link --force binutils 29 | cmake -E make_directory ${{runner.workspace}}/build 30 | cd ${{runner.workspace}}/build 31 | cmake ${GITHUB_WORKSPACE} -DCMAKE_CXX_COMPILER=g++-14 -DFLUX_BUILD_TOOLS=On 32 | cmake --build . --target make_single_header 33 | 34 | - name: Run 35 | working-directory: ${{runner.workspace}}/build 36 | run: | 37 | ./tools/make_single_header ${GITHUB_WORKSPACE}/include/flux.hpp ${GITHUB_WORKSPACE}/single_include/flux.hpp 38 | 39 | - name: Upload 40 | uses: stefanzweifel/git-auto-commit-action@v4 41 | with: 42 | commit_message: Update single header 43 | file_pattern: 'single_include/*' 44 | commit_user_name: Github Actions Bot 45 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | # Based on macos.yml from {fmt} 2 | # https://github.com/fmtlib/fmt/blob/master/.github/workflows/macos.yml 3 | 4 | name: macos 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | 12 | jobs: 13 | build: 14 | runs-on: macos-15 15 | strategy: 16 | matrix: 17 | compiler: [AppleClang, LLVM-Clang-16, LLVM-Clang-17, LLVM-Clang-18, LLVM-Clang-19, LLVM-Clang-20] 18 | test_with: [Headers, Module] 19 | build_type: [Debug, Release] 20 | 21 | exclude: 22 | - compiler: AppleClang 23 | test_with: Module 24 | - compiler: LLVM-Clang-16 25 | test_with: Module 26 | 27 | include: 28 | - compiler: AppleClang 29 | cxx: clang++ 30 | install: | 31 | brew install ninja 32 | - compiler: LLVM-Clang-16 33 | cxx: $(brew --prefix llvm@16)/bin/clang++ 34 | install: | 35 | brew install llvm@16 ninja 36 | - compiler: LLVM-Clang-17 37 | cxx: $(brew --prefix llvm@17)/bin/clang++ 38 | install: | 39 | brew install llvm@17 ninja 40 | - compiler: LLVM-Clang-18 41 | cxx: $(brew --prefix llvm@18)/bin/clang++ 42 | install: | 43 | brew install llvm@18 ninja 44 | - compiler: LLVM-Clang-19 45 | cxx: $(brew --prefix llvm@19)/bin/clang++ 46 | install: | 47 | brew install llvm@19 ninja 48 | - compiler: LLVM-Clang-20 49 | cxx: $(brew --prefix llvm@20)/bin/clang++ 50 | install: | 51 | brew update 52 | brew install llvm@20 ninja 53 | 54 | 55 | steps: 56 | - uses: actions/checkout@master 57 | 58 | - name: Create Build Environment 59 | run: | 60 | ${{matrix.install}} 61 | cmake -E make_directory ${{runner.workspace}}/build 62 | 63 | - name: Configure 64 | working-directory: ${{runner.workspace}}/build 65 | run: | 66 | cmake -GNinja -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.cxx}} \ 67 | -DFLUX_BUILD_BENCHMARKS=${{matrix.build_type == 'Release'}} \ 68 | -DFLUX_BUILD_MODULE=${{matrix.test_with == 'Module'}} \ 69 | -DFLUX_BUILD_TESTS_USING_MODULE=${{matrix.test_with == 'Module'}} \ 70 | $GITHUB_WORKSPACE 71 | 72 | - name: Build 73 | working-directory: ${{runner.workspace}}/build 74 | run: cmake --build . --config ${{matrix.build_type}} 75 | 76 | - name: Test 77 | working-directory: ${{runner.workspace}}/build 78 | run: ctest -C ${{matrix.build_type}} 79 | env: 80 | CTEST_OUTPUT_ON_FAILURE: True 81 | -------------------------------------------------------------------------------- /.github/workflows/santizers.yml: -------------------------------------------------------------------------------- 1 | name: sanitizers 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | sanitizer: [ubsan, asan] 15 | build_type: [Debug, Release] 16 | include: 17 | - cxx: g++-14 18 | install: | 19 | brew install gcc@14 ninja binutils 20 | brew link --force binutils 21 | 22 | steps: 23 | - uses: actions/checkout@master 24 | 25 | - name: Set up Homebrew 26 | uses: Homebrew/actions/setup-homebrew@master 27 | 28 | - name: Create Build Environment 29 | run: | 30 | ${{matrix.install}} 31 | cmake -E make_directory ${{runner.workspace}}/build 32 | 33 | - name: Configure 34 | working-directory: ${{runner.workspace}}/build 35 | run: | 36 | cmake -GNinja \ 37 | -DFLUX_ENABLE_ASAN=${{matrix.sanitizer == 'asan'}} \ 38 | -DFLUX_ENABLE_UBSAN=${{matrix.sanitizer == 'ubsan'}} \ 39 | -DCMAKE_CXX_COMPILER=g++-14 \ 40 | $GITHUB_WORKSPACE 41 | 42 | - name: Build 43 | working-directory: ${{runner.workspace}}/build 44 | run: cmake --build . 45 | 46 | - name: Test 47 | working-directory: ${{runner.workspace}}/build 48 | run: ctest -C ${{matrix.build_type}} 49 | env: 50 | CTEST_OUTPUT_ON_FAILURE: True 51 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-2022 12 | strategy: 13 | matrix: 14 | platform: [x86, x64] 15 | build_type: [Debug, Release] 16 | 17 | steps: 18 | - uses: actions/checkout@master 19 | - uses: seanmiddleditch/gha-setup-ninja@master 20 | - uses: TheMrMilchmann/setup-msvc-dev@v3 21 | with: 22 | arch: ${{matrix.platform}} 23 | 24 | - name: Create Build Environment 25 | run: cmake -E make_directory ${{runner.workspace}}/build 26 | 27 | - name: Configure 28 | # Use a bash shell for $GITHUB_WORKSPACE. 29 | shell: bash 30 | working-directory: ${{runner.workspace}}/build 31 | run: | 32 | cmake -G Ninja \ 33 | -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ 34 | -DFLUX_BUILD_BENCHMARKS=${{matrix.build_type == 'Release'}} \ 35 | $GITHUB_WORKSPACE 36 | 37 | - name: Build 38 | working-directory: ${{runner.workspace}}/build 39 | run: cmake --build . --config ${{matrix.build_type}} 40 | 41 | - name: Test 42 | working-directory: ${{runner.workspace}}/build 43 | run: ctest -C ${{matrix.build_type}} -V 44 | env: 45 | CTEST_OUTPUT_ON_FAILURE: True 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Common CMake build directories 3 | build*/ 4 | !build2/ 5 | 6 | # QtCreator 7 | CMakeLists.txt.user 8 | 9 | # CLion 10 | .idea/ 11 | cmake-build*/ 12 | 13 | # VS Code 14 | .vscode/ 15 | .cache/ 16 | 17 | # VS 18 | .vs/ 19 | CMakeSettings.json 20 | CMakeUserPresets.json 21 | out/ 22 | 23 | # conan 24 | .conan/test_package/build 25 | 26 | # Stuff I want to ignore 27 | junk/ 28 | 29 | # Build2 stuff 30 | .bdep/ 31 | 32 | # Local default options files. 33 | # 34 | .build2/local/ 35 | 36 | # Compiler/linker output. 37 | # 38 | *.d 39 | *.t 40 | *.i 41 | *.i.* 42 | *.ii 43 | *.ii.* 44 | *.o 45 | *.obj 46 | *.gcm 47 | *.pcm 48 | *.ifc 49 | *.so 50 | *.dll 51 | *.a 52 | *.lib 53 | *.exp 54 | *.pdb 55 | *.ilk 56 | *.exe 57 | *.exe.dlls/ 58 | *.exe.manifest 59 | *.pc 60 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | include(FetchContent) 3 | 4 | FetchContent_Declare( 5 | nanobench 6 | GIT_REPOSITORY https://github.com/martinus/nanobench.git 7 | GIT_TAG v4.3.11 8 | ) 9 | 10 | FetchContent_MakeAvailable(nanobench) 11 | 12 | add_executable(benchmark-internal-iteration internal_iteration_benchmark.cpp) 13 | target_link_libraries(benchmark-internal-iteration PUBLIC nanobench::nanobench flux) 14 | 15 | add_executable(benchmark-sort sort_benchmark.cpp) 16 | target_link_libraries(benchmark-sort PUBLIC nanobench::nanobench flux) 17 | 18 | add_executable(benchmark-multidimensional-memset multidimensional_memset_benchmark.cpp multidimensional_memset_benchmark_kernels.cpp) 19 | target_link_libraries(benchmark-multidimensional-memset PUBLIC nanobench::nanobench flux) 20 | -------------------------------------------------------------------------------- /benchmark/multidimensional_memset_benchmark_kernels.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 NVIDIA Corporation (reply-to: brycelelbach@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #if __cpp_lib_ranges_cartesian_product >= 202207L 13 | #include 14 | #else 15 | #include "ranges_cartesian_product.hpp" 16 | #endif 17 | 18 | #include 19 | #include 20 | 21 | void memset_2d_reference(double* A, flux::distance_t N, flux::distance_t M) 22 | { 23 | for (flux::distance_t i = 0; i != N; ++i) 24 | for (flux::distance_t j = 0; j != M; ++j) 25 | A[i * M + j] = 0.0; 26 | } 27 | 28 | void memset_2d_std_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M) 29 | { 30 | std::ranges::for_each( 31 | std::views::cartesian_product(std::views::iota(0, N), std::views::iota(0, M)), 32 | flux::unpack([&] (auto i, auto j) { 33 | A[i * M + j] = 0.0; 34 | })); 35 | } 36 | 37 | void memset_2d_flux_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M) 38 | { 39 | flux::for_each( 40 | flux::cartesian_product(flux::ints(0, N), flux::ints(0, M)), 41 | flux::unpack([&] (auto i, auto j) { 42 | A[i * M + j] = 0.0; 43 | })); 44 | } 45 | 46 | void memset_diagonal_2d_reference(double* A, flux::distance_t N, flux::distance_t M) 47 | { 48 | for (flux::distance_t i = 0; i != N; ++i) 49 | for (flux::distance_t j = 0; j != M; ++j) 50 | if (i == j) A[i * M + j] = 0.0; 51 | } 52 | 53 | void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M) 54 | { 55 | std::ranges::for_each( 56 | std::views::cartesian_product(std::views::iota(0, N), std::views::iota(0, M)) 57 | | std::views::filter(flux::unpack([] (auto i, auto j) { return i == j; })), 58 | flux::unpack([&] (auto i, auto j) { 59 | A[i * M + j] = 0.0; 60 | })); 61 | } 62 | 63 | void memset_diagonal_2d_flux_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M) 64 | { 65 | flux::for_each( 66 | flux::cartesian_product(flux::ints(0, N), flux::ints(0, M)) 67 | .filter(flux::unpack([] (auto i, auto j) { return i == j; })), 68 | flux::unpack([&] (auto i, auto j) { 69 | A[i * M + j] = 0.0; 70 | })); 71 | } 72 | 73 | -------------------------------------------------------------------------------- /cmake/FindSphinx.cmake: -------------------------------------------------------------------------------- 1 | #Look for an executable called sphinx-build 2 | find_program(SPHINX_EXECUTABLE 3 | NAMES sphinx-build 4 | DOC "Path to sphinx-build executable") 5 | 6 | include(FindPackageHandleStandardArgs) 7 | 8 | #Handle standard arguments to find_package like REQUIRED and QUIET 9 | find_package_handle_standard_args(Sphinx 10 | "Failed to find sphinx-build executable" 11 | SPHINX_EXECUTABLE) 12 | -------------------------------------------------------------------------------- /cmake/flux-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | include("${CMAKE_CURRENT_LIST_DIR}/flux-targets.cmake") 3 | 4 | check_required_components(flux) 5 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | html/ 2 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | find_package(Sphinx REQUIRED) 3 | 4 | set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) 5 | set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/html) 6 | set(SPHINX_INDEX_FILE ${SPHINX_BUILD}/index.html) 7 | 8 | file(GLOB_RECURSE SPHINX_RST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.rst) 9 | 10 | add_custom_command( 11 | OUTPUT ${SPHINX_INDEX_FILE} 12 | COMMAND 13 | ${SPHINX_EXECUTABLE} -b html ${SPHINX_SOURCE} ${SPHINX_BUILD} 14 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 15 | DEPENDS 16 | ${SPHINX_RST_FILES} 17 | MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/conf.py 18 | COMMENT "Generating documentation with Sphinx" 19 | ) 20 | 21 | add_custom_target(Sphinx ALL DEPENDS ${SPHINX_INDEX_FILE}) 22 | 23 | include(GNUInstallDirs) 24 | install( 25 | DIRECTORY ${SPHINX_BUILD} 26 | DESTINATION ${CMAKE_INSTALL_DOCDIR} 27 | ) -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'Flux' 21 | copyright = '2023, Tristan Brindle' 22 | author = 'Tristan Brindle' 23 | 24 | 25 | # -- General configuration --------------------------------------------------- 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be 28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 29 | # ones. 30 | extensions = [ 31 | # 'myst_parser', 32 | 'sphinx_copybutton' 33 | ] 34 | 35 | # Add any paths that contain templates here, relative to this directory. 36 | templates_path = ['_templates'] 37 | 38 | # List of patterns, relative to source directory, that match files and 39 | # directories to ignore when looking for source files. 40 | # This pattern also affects html_static_path and html_extra_path. 41 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 42 | 43 | # The default domain is C++ 44 | primary_domain = 'cpp' 45 | 46 | # The default language for syntax highlighting in code blocks is C++ 47 | highlight_language = 'cpp' 48 | 49 | # Strip "flux::" namespace from entries in the index of symbols 50 | cpp_index_common_prefix = ['flux::'] 51 | 52 | 53 | # -- Options for HTML output ------------------------------------------------- 54 | 55 | # The theme to use for HTML and HTML Help pages. See the documentation for 56 | # a list of builtin themes. 57 | # 58 | html_theme = 'furo' 59 | 60 | # Add any paths that contain custom static files (such as style sheets) here, 61 | # relative to this directory. They are copied after the builtin static files, 62 | # so a file named "default.css" will overwrite the builtin "default.css". 63 | html_static_path = ['_static'] 64 | 65 | # Configuration for the theme 66 | html_theme_options = { 67 | "source_repository": "https://github.com/tcbrindle/flux/", 68 | "source_branch": "main", 69 | "source_directory": "docs/", 70 | } 71 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Flux documentation master file, created by 2 | sphinx-quickstart on Tue Feb 28 16:42:37 2023. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Flux Documentation 7 | ================== 8 | 9 | Things of this World are in so constant a Flux, that nothing remains long in the same State. 10 | -- John Locke (1632-1704), English philosopher 11 | 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | :caption: Contents: 16 | 17 | reference 18 | 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`search` 26 | -------------------------------------------------------------------------------- /docs/reference.rst: -------------------------------------------------------------------------------- 1 | 2 | Reference Documentation 3 | ======================= 4 | 5 | .. toctree:: 6 | :maxdepth: 3 7 | 8 | reference/config 9 | reference/concepts 10 | reference/sequence_access 11 | reference/adaptors 12 | reference/algorithms 13 | reference/factories 14 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx >= 5.3.0 2 | sphinx-copybutton 3 | furo 4 | 5 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | function(ADD_EXAMPLE NAME SOURCE) 3 | add_executable(${NAME} ${SOURCE}) 4 | target_link_libraries(${NAME} flux-internal) 5 | if(${${NAME}_SKIP_TEST}) 6 | #pass 7 | else() 8 | add_test(NAME ${NAME} COMMAND ${NAME} ${${NAME}_ARGS}) 9 | endif() 10 | endfunction() 11 | 12 | add_example(example-config-parser config_parser.cpp) 13 | add_example(example-calendar calendar.cpp) 14 | add_example(example-merge-intervals merge_intervals.cpp) 15 | add_example(example-histogram histogram.cpp) 16 | set(example-word-count_SKIP_TEST ON) 17 | add_example(example-word-count word_count.cpp) 18 | add_example(example-prime-numbers prime_numbers.cpp) 19 | add_example(example-shortest-path shortest_path.cpp) 20 | add_example(example-moving-average moving_average.cpp) 21 | 22 | add_example(example-docs-adjacent docs/adjacent.cpp) 23 | add_example(example-docs-adjacent-filter docs/adjacent_filter.cpp) 24 | add_example(example-docs-all docs/all.cpp) 25 | add_example(example-docs-any docs/any.cpp) 26 | add_example(example-docs-cartesian-power docs/cartesian_power.cpp) 27 | add_example(example-docs-cartesian-power-map docs/cartesian_power_map.cpp) 28 | add_example(example-docs-cartesian-product docs/cartesian_product.cpp) 29 | add_example(example-docs-cartesian-product-map docs/cartesian_product_map.cpp) 30 | add_example(example-docs-compare docs/compare.cpp) 31 | add_example(example-docs-contains docs/contains.cpp) 32 | add_example(example-docs-count docs/count.cpp) 33 | add_example(example-docs-cursors docs/cursors.cpp) 34 | add_example(example-docs-cycle docs/cycle.cpp) 35 | add_example(example-docs-drop docs/drop.cpp) 36 | add_example(example-docs-ends-with docs/ends_with.cpp) 37 | add_example(example-docs-find-max docs/find_max.cpp) 38 | add_example(example-docs-find-min docs/find_min.cpp) 39 | add_example(example-docs-find-minmax docs/find_minmax.cpp) 40 | add_example(example-docs-mask docs/mask.cpp) 41 | add_example(example-docs-prescan docs/prescan.cpp) 42 | add_example(example-docs-read-only docs/read_only.cpp) 43 | add_example(example-docs-repeat docs/repeat.cpp) 44 | add_example(example-docs-scan docs/scan.cpp) 45 | add_example(example-docs-set-difference docs/set_difference.cpp) 46 | add_example(example-docs-set-intersection docs/set_intersection.cpp) 47 | add_example(example-docs-set-symmetric-difference docs/set_symmetric_difference.cpp) 48 | add_example(example-docs-set-union docs/set_union.cpp) 49 | add_example(example-docs-scan-first docs/scan_first.cpp) 50 | add_example(example-docs-split docs/split.cpp) 51 | add_example(example-docs-starts-with docs/starts_with.cpp) 52 | add_example(example-docs-unfold docs/unfold.cpp) 53 | 54 | add_example(example-top10-rain-water top10/01_trapping_rain_water.cpp) 55 | add_example(example-top10-mco top10/02_max_consecutive_ones.cpp) 56 | add_example(example-top10-lcis top10/03_longest_continuous_increasing_subsequence.cpp) 57 | add_example(example-top10-kadanes top10/04_maximum_subarray_sum.cpp) 58 | add_example(example-top10-sushi top10/05_sushi_for_two.cpp) 59 | add_example(example-top10-max-gap top10/06_max_gap.cpp) 60 | add_example(example-top10-max-gap-count top10/07_max_gap_count.cpp) 61 | add_example(example-top10-tco top10/08_three_consecutive_odds.cpp) 62 | add_example(example-top10-skyline top10/09_skyline.cpp) 63 | add_example(example-top10-ocean-view top10/10_ocean_view.cpp) 64 | 65 | -------------------------------------------------------------------------------- /example/config_parser.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | struct comment_t 18 | { 19 | std::string content; 20 | }; 21 | 22 | struct section_t 23 | { 24 | std::string name; 25 | }; 26 | 27 | struct option_t 28 | { 29 | std::string key; 30 | std::string value; 31 | }; 32 | 33 | using token_t = std::variant; 34 | using config_t = std::map>; 35 | 36 | struct context_t 37 | { 38 | std::string curr_section = "root"; 39 | config_t config; 40 | }; 41 | 42 | template struct overloaded : Ts... 43 | { 44 | using Ts::operator()...; 45 | }; 46 | 47 | template overloaded(Ts...) -> overloaded; 48 | 49 | bool not_blank_line(const std::string& line) 50 | { 51 | auto space = [](auto c) { return std::isspace(c); }; 52 | return not(line.empty() or flux::all(line, space)); 53 | } 54 | 55 | token_t parse_line(const std::string& line) 56 | { 57 | if (line.starts_with("#")) 58 | return comment_t{line.substr(1)}; 59 | else if (line.starts_with("[") and line.ends_with("]")) 60 | return section_t{line.substr(1, line.size() - 2)}; 61 | 62 | auto items = flux::ref(line).split_string("=").to(); 63 | if (items.size() != 2) 64 | throw std::runtime_error("parse error"); 65 | return option_t{std::string{items[0]}, std::string{items[1]}}; 66 | } 67 | 68 | context_t add_to_config(context_t ctx, token_t tok) 69 | { 70 | std::visit(overloaded{[&ctx](const section_t& s) { ctx.curr_section = s.name; }, 71 | [&ctx](const option_t& o) { ctx.config[ctx.curr_section + "." + o.key] = o.value; }, 72 | [](auto) { /* skip other tokens */}}, 73 | tok); 74 | return ctx; 75 | } 76 | 77 | int main() 78 | { 79 | std::stringstream ss; 80 | ss << "# This is example config file\n"; 81 | ss << "project=example\n"; 82 | ss << "language=C++\n"; 83 | ss << " \n"; 84 | ss << "# server configuration \n"; 85 | ss << "[server]\n"; 86 | ss << "host=localhost\n"; 87 | ss << "port=8080\n"; 88 | ss << "\n"; 89 | 90 | auto cfg = flux::getlines(ss) // read line by line 91 | .filter(not_blank_line) // skip all blank lines 92 | .map(parse_line) // convert line to one of supported tokens 93 | .fold(add_to_config, context_t{}) // fold tokens to the final variable map 94 | .config; 95 | 96 | assert(cfg["root.project"] == "example"); 97 | assert(cfg["root.language"] == "C++"); 98 | assert(cfg["server.host"] == "localhost"); 99 | assert(cfg["server.port"] == "8080"); 100 | 101 | return 0; 102 | } -------------------------------------------------------------------------------- /example/docs/adjacent.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | #include 12 | 13 | int main() 14 | { 15 | std::vector vec{1, 2, 3, 4, 5}; 16 | 17 | std::vector sums; 18 | 19 | // adjacent<3> yields 3-tuples which we can destructure 20 | for (auto [a, b, c] : flux::adjacent<3>(std::move(vec))) { 21 | sums.push_back(a + b + c); 22 | } 23 | 24 | assert((sums == std::vector{1 + 2 + 3, 25 | 2 + 3 + 4, 26 | 3 + 4 + 5})); 27 | } -------------------------------------------------------------------------------- /example/docs/adjacent_filter.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | #include 12 | 13 | int main() 14 | { 15 | std::array nums{1, 1, 2, 3, 3, 2, 2}; 16 | 17 | // The adjacent_filter adaptor applies the given predicate to each pair 18 | // of elements in the sequence, and if the predicate returns false then 19 | // the second element of the pair is discarded 20 | auto filtered1 = flux::adjacent_filter(nums, std::less{}); 21 | assert(flux::equal(filtered1, std::array{1, 2, 3})); 22 | 23 | // For the common case of removing adjacent equal elements, Flux provides 24 | // the dedup() function as shorthand for adjacent_filter(std::not_equal_to{}) 25 | auto filtered2 = flux::dedup(nums); 26 | assert(flux::equal(filtered2, std::array{1, 2, 3, 2})); 27 | 28 | // We can use adjacent_filter with a custom comparator as well 29 | auto compare = [](auto p1, auto p2) { return p1.first != p2.first; }; 30 | std::pair pairs[] = {{1, 2}, {1, 3}, {1, 4}, {2, 5}, {2, 6}}; 31 | 32 | auto filtered3 = flux::adjacent_filter(flux::ref(pairs), compare); 33 | assert(flux::equal(filtered3, 34 | std::array{std::pair{1, 2}, std::pair{2, 5}})); 35 | } -------------------------------------------------------------------------------- /example/docs/all.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | 11 | int main() 12 | { 13 | auto is_small = [](int i) { return i < 10; }; 14 | 15 | std::vector vec{1, 2, 3, 4, 5}; 16 | 17 | // Check whether every element is small 18 | bool all_small = flux::all(vec, is_small); 19 | assert(all_small); 20 | } -------------------------------------------------------------------------------- /example/docs/any.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | 11 | int main() 12 | { 13 | auto is_even = [](int i) { return i % 2 == 0; }; 14 | 15 | std::vector vec{1, 3, 4, 5, 7, 9}; 16 | 17 | bool b = flux::any(vec, is_even); 18 | assert(b == true); // There is an even element 19 | } -------------------------------------------------------------------------------- /example/docs/assert.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_EXAMPLE_DOCS_ASSERT_HPP_INCLUDED 7 | #define FLUX_EXAMPLE_DOCS_ASSERT_HPP_INCLUDED 8 | 9 | #ifdef NDEBUG 10 | #undef NDEBUG 11 | #endif 12 | 13 | #include 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /example/docs/cartesian_power.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | #include 12 | 13 | int main() 14 | { 15 | std::string_view str = "ab"; 16 | std::vector strings; 17 | 18 | // cartesian_power<3> gives us 3-tuples which we can destructure 19 | // The total number of elements in this case is size(str)^3 = 8 20 | for (auto [x, y, z] : flux::cartesian_power<3>(str)) { 21 | strings.push_back(std::string{x, y, z}); 22 | } 23 | 24 | assert((strings == std::vector{"aaa", "aab", "aba", "abb", 25 | "baa", "bab", "bba", "bbb"})); 26 | } -------------------------------------------------------------------------------- /example/docs/cartesian_power_map.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | #include 12 | 13 | using namespace std::string_view_literals; 14 | 15 | int main() 16 | { 17 | std::array nums{1, 2, 3}; 18 | 19 | // cartesian_power_map takes a sequence and a callable of N arguments 20 | // Here we are using N=2 and the binary function object std::plus 21 | auto sums = flux::cartesian_power_map<2>(nums, std::plus{}).to>(); 22 | 23 | assert((sums == std::vector{1 + 1, 1 + 2, 1 + 3, 24 | 2 + 1, 2 + 2, 2 + 3, 25 | 3 + 1, 3 + 2, 3 + 3})); 26 | } -------------------------------------------------------------------------------- /example/docs/cartesian_product.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | #include 12 | 13 | using namespace std::string_view_literals; 14 | 15 | int main() 16 | { 17 | std::string_view str = "abc"; 18 | std::array nums = {1, 2, 3}; 19 | 20 | // flux::cartesian_product(str, nums) yields all combinations of elements from 21 | // the two sequences as a tuple 22 | auto pairs = flux::cartesian_product(str, nums).to(); 23 | 24 | using P = std::tuple; 25 | 26 | assert((pairs == std::vector{P{'a', 1}, P{'a', 2}, P{'a', 3}, 27 | P{'b', 1}, P{'b', 2}, P{'b', 3}, 28 | P{'c', 1}, P{'c', 2}, P{'c', 3}})); 29 | 30 | // cartesian_product can take an arbitrary number of sequence arguments 31 | // The number of elements is the product of the sizes of the input sequences 32 | // It is bidirectional and random-access if all the input sequences 33 | // satisfy these concepts 34 | auto seq = flux::cartesian_product("xy"sv, 35 | std::array{1.0, 2.0}, 36 | std::vector{111, 222}).reverse(); 37 | 38 | using T = std::tuple; 39 | 40 | assert(flux::equal(seq, std::vector{{'y', 2.0, 222}, {'y', 2.0, 111}, 41 | {'y', 1.0, 222}, {'y', 1.0, 111}, 42 | {'x', 2.0, 222}, {'x', 2.0, 111}, 43 | {'x', 1.0, 222}, {'x', 1.0, 111}})); 44 | } -------------------------------------------------------------------------------- /example/docs/cartesian_product_map.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | #include 12 | 13 | int main() 14 | { 15 | std::array big = {10000L, 20000L}; 16 | std::array medium = {100.0f, 200.0f}; 17 | std::array small = {1, 2}; 18 | 19 | auto add3 = [](long a, float b, int c) { return double(a) + b + c; }; 20 | 21 | // Note that because cartesian_product_map takes a variadic number of 22 | // sequences, the function argument goes first 23 | auto vec = flux::cartesian_product_map(add3, big, medium, small) 24 | .to(); 25 | 26 | assert((vec == std::vector{10101.0, 10102.0, 27 | 10201.0, 10202.0, 28 | 20101.0, 20102.0, 29 | 20201.0, 20202.0})); 30 | } -------------------------------------------------------------------------------- /example/docs/compare.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | int main() 15 | { 16 | std::vector v1{1, 2, 3, 4, 5}; 17 | 18 | std::vector v2{1, 4, 9, 16, 25}; 19 | 20 | // Result is std::strong_ordering because ints have a total order, 21 | // and less because 2 < 4 22 | assert(flux::compare(v1, v2) == std::strong_ordering::less); 23 | 24 | // v1 compares equal to v1 25 | assert(std::is_eq(flux::compare(v1, v1))); 26 | 27 | std::vector v3{1, 2, 3, 4, 5}; 28 | std::vector v4{1, 2, 3}; 29 | 30 | // All elements compare equal, but v3 has more elements and so 31 | // is greater than v4 32 | assert(flux::compare(v3, v4) == std::strong_ordering::greater); 33 | 34 | // Note that sequences containing NaNs are unordered with the default comparator 35 | std::vector v5{1.0, 2.0, 3.0, 4.0, 5.0}; 36 | std::vector v6{1.0, 2.0, NAN, 4.0, 5.0}; 37 | assert(flux::compare(v5, v6) == std::partial_ordering::unordered); 38 | 39 | // On most systems we can use std::strong_order as a custom comparator 40 | // to get a total order for IEEE floats 41 | if constexpr (std::numeric_limits::is_iec559) { 42 | assert(flux::compare(v5, v6, std::strong_order) == 43 | std::strong_ordering::less); 44 | } 45 | } -------------------------------------------------------------------------------- /example/docs/contains.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | 11 | int main() 12 | { 13 | std::vector const simpsons{ 14 | "Homer", "Marge", "Bart", "Lisa", "Maggie" 15 | }; 16 | 17 | // Bart is a Simpson 18 | assert(flux::contains(simpsons, "Bart")); 19 | 20 | // Mr Burns is not a Simpson 21 | assert(not flux::contains(simpsons, "Mr Burns")); 22 | } -------------------------------------------------------------------------------- /example/docs/count.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | 12 | int main() 13 | { 14 | std::vector vec{1, 2, 3}; 15 | 16 | // std::vector is a sized_sequence, so this call is 17 | // equivalent to `flux::size(vec)` (and so constant time) 18 | assert(flux::count(vec) == 3); 19 | 20 | // A filtered sequence is never sized, so this call will 21 | // iterate over each element 22 | auto filtered = flux::filter(std::move(vec), flux::pred::odd); 23 | assert(flux::count(filtered) == 2); 24 | 25 | // An istream sequence is single-pass and not sized, so counting 26 | // the elements will "use up" the sequence 27 | std::istringstream iss("1 2 3"); 28 | auto seq = flux::from_istream(iss); 29 | assert(flux::count(seq) == 3); 30 | assert(flux::is_last(seq, flux::first(seq))); // No more elements! 31 | } -------------------------------------------------------------------------------- /example/docs/cursors.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std::string_view_literals; 13 | 14 | int main() 15 | { 16 | std::array const array{"alpha"sv, "bravo"sv, "charlie"sv, "delta"sv, "echo"sv}; 17 | 18 | auto long_words = flux::drop(array, 2); 19 | 20 | // We can use the cursors() adaptor to iterate over the cursors of the 21 | // sequence (in this case integer indices) and use those to read from the 22 | // original sequence 23 | for (auto idx : flux::cursors(long_words)) { 24 | std::cout << idx << ": " << long_words[idx] << '\n'; 25 | } 26 | // prints 27 | // 2: charlie 28 | // 3: delta 29 | // 4: echo 30 | } -------------------------------------------------------------------------------- /example/docs/cycle.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | 12 | int main() 13 | { 14 | std::array arr{1, 2, 3}; 15 | 16 | // cycle(seq) returns an infinite sequence. It's common to use this in 17 | // combination with take() to turn it back into a finite sequence: 18 | auto cycled1 = flux::take(flux::cycle(arr), 5); 19 | assert(flux::equal(cycled1, std::array{1, 2, 3, 1, 2})); 20 | 21 | // We can also use a cycled sequence as an argument to zip(): 22 | std::string_view letters = "ABCDE"; 23 | auto zipped = flux::zip(letters, flux::cycle(arr)); 24 | using P = std::pair; 25 | assert(flux::equal(zipped, std::array{ 26 | P{'A', 1}, P{'B', 2}, P{'C', 3}, P{'D', 1}, P{'E', 2} 27 | })); 28 | 29 | // Alternatively, we can provide a second argument to cycle(seq, n) to 30 | // get a finite sequence which repeats the source n times: 31 | auto cycled2 = flux::cycle(arr, 3); 32 | assert(flux::equal(cycled2, std::array{1, 2, 3, 1, 2, 3, 1, 2, 3})); 33 | assert(flux::sum(cycled2) == 18); 34 | 35 | // Note that both versions of cycle() only provide immutable access to their 36 | // elements. The following would be a compile error: 37 | // flux::fill(cycled2, 99); // ERROR: cannot assign to const reference 38 | } -------------------------------------------------------------------------------- /example/docs/drop.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | 12 | int main() 13 | { 14 | std::vector vec{1, 2, 3, 4, 5}; 15 | 16 | auto dropped = flux::drop(std::move(vec), 3); 17 | 18 | assert(flux::size(dropped) == 2); 19 | assert(flux::equal(dropped, std::vector{4, 5})); 20 | } -------------------------------------------------------------------------------- /example/docs/ends_with.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | 12 | using namespace std::literals; 13 | 14 | int main() 15 | { 16 | std::string_view greeting = "Hello world"; 17 | 18 | // greeting has "world" as a suffix 19 | assert(flux::ends_with(greeting, "world"sv)); 20 | 21 | std::vector numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 22 | 23 | // numbers ends with [8, 9, 10] 24 | assert(flux::ends_with(numbers, std::array{8, 9, 10})); 25 | } -------------------------------------------------------------------------------- /example/docs/find_max.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "assert.hpp" 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | struct Person { 11 | std::string name; 12 | int age; 13 | }; 14 | 15 | std::vector people{ 16 | {"Alice", 44}, 17 | {"Bob", 63}, 18 | {"Chris", 29}, 19 | {"Dani", 29}, 20 | {"Eddy", 63} 21 | }; 22 | 23 | // Get a cursor to the maximum of the people vector, according to age 24 | auto max_cur = flux::find_max(people, flux::proj(flux::cmp::compare, &Person::age)); 25 | 26 | // The oldest person is 63 27 | assert(flux::read_at(people, max_cur).age == 63); 28 | 29 | // Note that (unlike std::max_element) find_max() return a cursor to the 30 | // *last* of several equally-maximum elements 31 | assert(flux::read_at(people, max_cur).name == "Eddy"); 32 | } -------------------------------------------------------------------------------- /example/docs/find_min.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "assert.hpp" 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | struct Person { 11 | std::string name; 12 | int age; 13 | }; 14 | 15 | std::vector people{ 16 | {"Alice", 44}, 17 | {"Bob", 63}, 18 | {"Chris", 29}, 19 | {"Dani", 29}, 20 | {"Eddy", 63} 21 | }; 22 | 23 | // Get a cursor to the maximum of the people vector, according to age 24 | auto min_cur = flux::find_min(people, flux::proj(flux::cmp::compare, &Person::age)); 25 | 26 | // The youngest person is 29 27 | assert(flux::read_at(people, min_cur).age == 29); 28 | 29 | // Note that find_min() return a cursor to the first of several 30 | // equally-minimum elements 31 | assert(flux::read_at(people, min_cur).name == "Chris"); 32 | } -------------------------------------------------------------------------------- /example/docs/find_minmax.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "assert.hpp" 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | struct Person { 11 | std::string name; 12 | int age; 13 | }; 14 | 15 | std::vector people{ 16 | {"Alice", 44}, 17 | {"Bob", 63}, 18 | {"Chris", 29}, 19 | {"Dani", 29}, 20 | {"Eddy", 63} 21 | }; 22 | 23 | // find_minmax() returns a pair of cursors which we can destructure 24 | // Here we'll get the min and max of the people vector, according to age 25 | auto [min, max] = flux::find_minmax(people, flux::proj(flux::cmp::compare, &Person::age)); 26 | 27 | // The "minimum" is Chris. Dani is the same age, but Chris appears earlier 28 | // in the sequence 29 | assert(flux::read_at(people, min).name == "Chris"); 30 | 31 | // The "maximum" is Eddy. Bob is the same age, but Eddy appears later in the 32 | // sequence 33 | assert(flux::read_at(people, max).name == "Eddy"); 34 | } -------------------------------------------------------------------------------- /example/docs/mask.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | 12 | using namespace std::literals; 13 | 14 | int main() 15 | { 16 | std::array values{"one"sv, "two"sv, "three"sv, "four"sv, "five"sv}; 17 | 18 | std::array selectors{true, false, true, false, true}; 19 | 20 | // flux::mask() selects those elements of values for which the corresponding 21 | // element of selectors is true 22 | auto masked = flux::mask(values, selectors); 23 | assert(flux::equal(masked, std::array{"one"sv, "three"sv, "five"sv})); 24 | 25 | // Note that the selectors sequence can have any element type which is 26 | // explicitly convertible to bool 27 | std::array int_selectors{0, 0, 0, 1, 1}; 28 | 29 | auto masked2 = flux::mask(values, int_selectors); 30 | assert(flux::equal(masked2, std::array{"four"sv, "five"sv})); 31 | } -------------------------------------------------------------------------------- /example/docs/prescan.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | 11 | int main() 12 | { 13 | // We can compute the triangular numbers using prescan() 14 | std::array const ints{1, 2, 3, 4, 5}; 15 | 16 | // Note that unlike scan(), the initial value for prescan() is required, and 17 | // is the first element of the resulting sequence, which has one more element 18 | // than the input 19 | auto tri_nums = flux::prescan(ints, std::plus{}, 0); 20 | assert(flux::equal(tri_nums, std::array{0, 1, 3, 6, 10, 15})); 21 | } -------------------------------------------------------------------------------- /example/docs/read_only.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | 12 | // We can use the read_only_sequence concept to statically require a sequence 13 | // whose elements are immutable 14 | bool contains_a_two(flux::read_only_sequence auto&& seq) 15 | { 16 | for (auto&& elem : seq) { 17 | if (elem == 2) { // What if we wrote `elem = 2` (assignment) by mistake? 18 | return true; 19 | } 20 | } 21 | return false; 22 | } 23 | 24 | int main() 25 | { 26 | auto seq = flux::filter(std::vector{1, 2, 3, 4, 5}, flux::pred::even); 27 | 28 | // We cannot pass seq directly, as it yields mutable int& elements 29 | // contains_a_two(seq); // COMPILE ERROR 30 | 31 | // ...but we can use read_only() so that the sequence yields immutable 32 | // elements of type int const&. 33 | assert(contains_a_two(flux::read_only(std::move(seq)))); 34 | } 35 | -------------------------------------------------------------------------------- /example/docs/repeat.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | 12 | using namespace std::string_view_literals; 13 | 14 | int main() 15 | { 16 | // flux::repeat(val) is a random-access sequence which endlessly repeats 17 | // the given value 18 | auto seq = flux::repeat(3); 19 | 20 | auto cursor = flux::first(seq); 21 | assert(flux::read_at(seq, cursor) == 3); 22 | // fast-forward the cursor a lot... 23 | cursor = flux::next(seq, cursor, 1'000'000); 24 | assert(flux::read_at(seq, cursor) == 3); // still returning 3! 25 | 26 | // We could use the take adaptor to make a repeat sequence finite... 27 | auto taken = flux::take(seq, 5); 28 | assert(flux::equal(taken, std::array{3, 3, 3, 3, 3})); 29 | 30 | // ...but it's easier to use repeat(val, count) instead 31 | auto police = flux::repeat("hello"sv, 3); 32 | assert(flux::equal(police, std::array{"hello", "hello", "hello"})); 33 | } -------------------------------------------------------------------------------- /example/docs/scan.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | 11 | int main() 12 | { 13 | // We can compute the triangular numbers using scan() 14 | std::array const ints{1, 2, 3, 4, 5}; 15 | 16 | // Note that unlike prescan(), the initial value for scan() may be omitted 17 | // (here defaulting to int{}), and the resulting sequence has the same number 18 | // of elements as the original 19 | auto tri_nums = flux::scan(ints, std::plus{}); 20 | assert(flux::equal(tri_nums, std::array{1, 3, 6, 10, 15})); 21 | } -------------------------------------------------------------------------------- /example/docs/scan_first.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | 11 | int main() 12 | { 13 | // We can compute the triangular numbers using scan_first() 14 | std::array const ints{1, 2, 3, 4, 5}; 15 | 16 | // Note that unlike scan(), scan_first() never takes an initial value, 17 | // and instead the first element of the resulting sequence is the same as 18 | // the first element of the underlying sequence: the fold operation is only 19 | // applied to subsequent elements. 20 | auto tri_nums = flux::scan_first(ints, std::plus{}); 21 | assert(flux::equal(tri_nums, std::array{1, 3, 6, 10, 15})); 22 | } -------------------------------------------------------------------------------- /example/docs/set_difference.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | 12 | int main() 13 | { 14 | std::array arr1{0, 1, 2, 3, 4, 5}; 15 | std::array arr2{ 1, 3, 5}; 16 | 17 | auto merged = flux::set_difference(flux::ref(arr1), flux::ref(arr2)); 18 | 19 | assert(flux::equal(merged, std::array{0, 2, 4})); 20 | } -------------------------------------------------------------------------------- /example/docs/set_intersection.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | 12 | int main() 13 | { 14 | std::array arr1{0, 1, 2, 3, 4, 5}; 15 | std::array arr2{ 1, 3, 5}; 16 | 17 | auto merged = flux::set_intersection(flux::ref(arr1), flux::ref(arr2)); 18 | 19 | assert(flux::equal(merged, std::array{1, 3, 5})); 20 | } -------------------------------------------------------------------------------- /example/docs/set_symmetric_difference.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | 12 | int main() 13 | { 14 | std::array arr1{0, 1, 2, 3, 4, 5}; 15 | std::array arr2{ 1, 3, 5, 6, 7}; 16 | 17 | auto merged = flux::set_symmetric_difference(flux::ref(arr1), flux::ref(arr2)); 18 | 19 | assert(flux::equal(merged, std::array{0, 2, 4, 6, 7})); 20 | } -------------------------------------------------------------------------------- /example/docs/set_union.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | 7 | #include 8 | 9 | #include "assert.hpp" 10 | #include 11 | 12 | int main() 13 | { 14 | std::array arr1{1, 3, 5}; 15 | std::array arr2{1, 2, 4, 6}; 16 | 17 | auto merged = flux::set_union(flux::ref(arr1), flux::ref(arr2)); 18 | 19 | assert(flux::equal(merged, std::array{1, 2, 3, 4, 5, 6})); 20 | } -------------------------------------------------------------------------------- /example/docs/split.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std::string_view_literals; 15 | 16 | int main() 17 | { 18 | using flux::equal; 19 | 20 | // We can split a sequence using a single delimiter 21 | auto seq1 = flux::split("here are some words"sv, ' '); 22 | assert(equal(seq1, std::array{"here"sv, "are"sv, "some"sv, "words"sv})); 23 | 24 | 25 | // Consecutive delimiters will result in empty subsequences in the output 26 | auto seq2 = flux::split("some,,,commas"sv, ','); 27 | assert(equal(seq2, std::array{"some"sv, ""sv, ""sv, "commas"sv})); 28 | 29 | 30 | // If the sequence ends with a delimiter, the final subsequence will be empty 31 | auto seq3 = flux::split("Two. Sentences."sv, '.'); 32 | assert(equal(seq3, std::array{"Two"sv, " Sentences"sv, ""sv})); 33 | 34 | 35 | // We can also split a sequence with a pattern 36 | auto seq4 = flux::split(std::vector{1, 2, 3, 4, 5}, std::array{2, 3}); 37 | assert(equal(seq4, std::vector{std::vector{1}, std::vector{4, 5}})); 38 | 39 | 40 | // Repeated, non-overlapping patterns result in empty subsequences 41 | auto seq5 = flux::split("Hello!!!!World"sv, "!!"sv); 42 | assert(equal(seq5, std::array{"Hello"sv, ""sv, "World"sv})); 43 | 44 | 45 | // Overlapping patterns are only matched once 46 | auto seq6 = flux::split("Hello!!!World"sv, "!!"sv); 47 | assert(equal(seq6, std::array{"Hello"sv, "!World"sv})); 48 | 49 | 50 | // If the sequence begins with the pattern, the first subsequence will 51 | // be empty... 52 | auto seq7 = flux::split("!!Hello"sv, "!!"sv); 53 | assert(equal(seq7, std::array{""sv, "Hello"sv})); 54 | 55 | 56 | // ... and likewise if it ends with the pattern 57 | auto seq8 = flux::split("Hello!!"sv, "!!"sv); 58 | assert(equal(seq8, std::array{"Hello"sv, ""sv})); 59 | 60 | 61 | // Lastly, we can split using a predicate function 62 | auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; 63 | 64 | auto seq9 = flux::split("These1are2some3words"sv, is_digit); 65 | assert(equal(seq9, std::array{"These"sv, "are"sv, "some"sv, "words"sv})); 66 | 67 | 68 | // As usual, consecutive "true" elements in the input will produce 69 | // empty subsequences in the output 70 | auto seq10 = flux::split("A123B"sv, is_digit); 71 | assert(equal(seq10, std::array{"A"sv, ""sv, ""sv, "B"sv})); 72 | 73 | 74 | // It can be useful combine splitting with a "not empty" filter 75 | auto is_space = [](char c) { return std::isspace(static_cast(c)); }; 76 | 77 | auto seq11 = flux::split("Alpha Bravo\t\rCharlie \n"sv, is_space) 78 | .filter(std::not_fn(flux::is_empty)); 79 | assert(equal(seq11, std::array{"Alpha"sv, "Bravo"sv, "Charlie"sv})); 80 | } -------------------------------------------------------------------------------- /example/docs/starts_with.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | 12 | using namespace std::literals; 13 | 14 | int main() 15 | { 16 | std::string_view greeting = "Hello world"; 17 | 18 | // greeting has "Hello" as a prefix 19 | assert(flux::starts_with(greeting, "Hello"sv)); 20 | 21 | std::vector numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 22 | 23 | // numbers begins with [1, 2, 3] 24 | assert(flux::starts_with(numbers, std::array{1, 2, 3})); 25 | } -------------------------------------------------------------------------------- /example/docs/unfold.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "assert.hpp" 9 | #include 10 | #include 11 | 12 | using namespace std::string_view_literals; 13 | 14 | int main() 15 | { 16 | // We can use unfold() with the identity function to do the equivalent of 17 | // flux::repeat(): 18 | auto repeated = flux::unfold(std::identity{}, "hello"sv).take(3); 19 | 20 | assert(flux::equal(repeated, std::array{"hello"sv, "hello"sv, "hello"sv})); 21 | 22 | // We can combine unfold() with a mutable lambda to do more sophisticated 23 | // things, like generating the Fibonacci sequence: 24 | auto fibs = flux::unfold([next = 1u](unsigned cur) mutable { 25 | return std::exchange(next, cur + next); 26 | }, 0u); 27 | 28 | assert(flux::equal(std::move(fibs).take(10), 29 | std::array{0, 1, 1, 2, 3, 5, 8, 13, 21, 34})); 30 | } 31 | -------------------------------------------------------------------------------- /example/histogram.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 2 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | template 16 | flux::generator randu(T min, T max) 17 | { 18 | std::mt19937 rng(std::random_device{}()); 19 | std::uniform_int_distribution dist(min, max); 20 | 21 | while (true) { 22 | co_yield dist(rng); 23 | } 24 | } 25 | 26 | template 27 | flux::generator randn(T mean, T stddev = 1.0) 28 | { 29 | std::mt19937 rng(std::random_device{}()); 30 | std::normal_distribution dist(mean, stddev); 31 | 32 | while (true) { 33 | co_yield static_cast(std::round(dist(rng))); 34 | } 35 | } 36 | 37 | using hist_t = std::map; 38 | 39 | auto to_histogram = [](hist_t&& so_far, auto x) 40 | { 41 | ++so_far[x]; 42 | return std::move(so_far); 43 | }; 44 | 45 | void print_histogram(const hist_t& hist) 46 | { 47 | for (auto [bin, count]: hist){ 48 | std::cout << std::setw(2) << bin << ' ' << std::string(std::size_t(count)/200, '*') << '\n'; 49 | } 50 | }; 51 | 52 | int main() 53 | { 54 | std::cout << "Uniform distribution from 0 to 10\n"; 55 | print_histogram(randu(0, 10).take(10000).fold(to_histogram, hist_t{})); 56 | std::cout << '\n'; 57 | 58 | std::cout << "Normal distribution with mean 5 and stddev 2\n"; 59 | print_histogram(randn(5.0, 2.0).take(10000).fold(to_histogram, hist_t{})); 60 | std::cout << '\n'; 61 | } 62 | -------------------------------------------------------------------------------- /example/merge_intervals.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 2 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | struct interval_t 12 | { 13 | std::size_t begin; 14 | std::size_t end; 15 | }; 16 | 17 | std::ostream& operator<<(std::ostream& s, const interval_t& i) 18 | { 19 | return s << '(' << i.begin << ',' << i.end << ')'; 20 | } 21 | 22 | bool is_overlapped(interval_t a, interval_t b) 23 | { 24 | return a.end >= b.begin; 25 | } 26 | 27 | auto merge = [](flux::sequence auto seq) -> interval_t 28 | { 29 | auto begin = flux::front(seq)->begin; 30 | auto end = flux::max(seq, flux::proj(flux::cmp::compare, &interval_t::end))->end; 31 | return {begin, end}; 32 | }; 33 | 34 | int main() 35 | { 36 | std::vector intervals = {{2, 4}, {7, 9}, {11, 13}, {6, 7}, {0, 3}}; 37 | 38 | // sort intervals according to begin 39 | flux::sort(intervals, flux::proj(flux::cmp::compare, &interval_t::begin)); 40 | 41 | flux::ref(intervals) 42 | .chunk_by(is_overlapped) 43 | .map(merge) 44 | .write_to(std::cout); 45 | 46 | std::cout << std::endl; 47 | 48 | // prints [(0,4), (6,9), (11,13)] 49 | } -------------------------------------------------------------------------------- /example/moving_average.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 2 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct sliding_window_t 13 | { 14 | explicit sliding_window_t(std::size_t n): size(n) {} 15 | 16 | void push(int value) { 17 | if (window.size() < size) { 18 | sum += value; 19 | } else { 20 | sum += value - window.front(); 21 | window.pop_front(); 22 | } 23 | 24 | window.push_back(value); 25 | } 26 | 27 | int average() const { 28 | return sum / static_cast(window.size()); 29 | } 30 | 31 | std::size_t size; 32 | std::deque window; 33 | int sum = 0; 34 | }; 35 | 36 | auto sliding_window = [](sliding_window_t&& win, int next) { 37 | win.push(next); 38 | return std::move(win); 39 | }; 40 | 41 | int main() { 42 | std::vector intervals = {1, 5, 6, 1, 2, 9, 7, -1, 0}; 43 | 44 | // compute moving average by scan adaptor (more effective for large windows) 45 | auto ma = flux::ref(intervals) 46 | .scan(sliding_window, sliding_window_t(3)) 47 | .map(&sliding_window_t::average) 48 | .to(); 49 | 50 | assert(ma.size() == intervals.size()); 51 | assert(ma[0] == 1); 52 | assert(ma[1] == 3); // (1 + 5) / 2 53 | assert(ma[2] == 4); // (1 + 5 + 6) / 3 54 | assert(ma[3] == 4); // (5 + 6 + 1) / 3 55 | assert(ma.back() == 2); // (7 + -1 + 0) / 3 56 | 57 | // compute moving average by slide adaptor (less effective for large windows) 58 | auto ma2 = flux::ref(intervals) 59 | .slide(3) 60 | .map([](auto&& win) { return win.sum() / win.size(); }) 61 | .to(); 62 | 63 | assert(ma2.size() == intervals.size() - 2); 64 | assert(ma2[0] == 4); // (1 + 5 + 6) / 3 65 | assert(ma2[1] == 4); // (5 + 6 + 1) / 3 66 | assert(ma2.back() == 2); // (7 + -1 + 0) / 3 67 | } 68 | -------------------------------------------------------------------------------- /example/prime_numbers.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 2 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | flux::generator primes() 12 | { 13 | std::vector history; 14 | auto is_prime = [&](auto x) { 15 | return flux::none(history, [x](auto prime) { return (x % prime) == 0; }); 16 | }; 17 | 18 | for (auto p : flux::ints(2).filter(is_prime)) 19 | { 20 | history.push_back(p); 21 | co_yield p; 22 | } 23 | } 24 | 25 | int main() 26 | { 27 | // Prints all prime numbers less than 1000 28 | primes().take_while(flux::pred::lt(1000)).write_to(std::cout); 29 | std::cout << std::endl; 30 | } 31 | -------------------------------------------------------------------------------- /example/top10/01_trapping_rain_water.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * Given n non-negative integers representing an elevation map where the width 8 | * of each bar is 1, compute how much water it can trap after raining. 9 | * 10 | * https://leetcode.com/problems/trapping-rain-water/ 11 | * 12 | */ 13 | 14 | #include // for std::minus 15 | 16 | #include 17 | 18 | auto const rain_water = [](std::initializer_list heights) 19 | { 20 | // Find the index of the maximum height 21 | flux::cursor auto max_idx = flux::find_max(heights); 22 | 23 | // Split the input sequence into two at the max position 24 | auto left = flux::slice(heights, flux::first(heights), max_idx); 25 | auto right = flux::slice(heights, max_idx, flux::last(heights)); 26 | 27 | // To calculate the trapped rain water for each half, we sum up the 28 | // difference between each element and the maximum seen up to that point 29 | auto trapped = [](flux::sequence auto seq) { 30 | return flux::zip(flux::scan(seq, flux::cmp::max), seq) 31 | .map(flux::unpack(std::minus{})) 32 | .sum(); 33 | }; 34 | 35 | // The final answer is the total of the trapped rain water reading 36 | // left-to-right for the left half, and right-to-left for the right half 37 | return trapped(left) + trapped(flux::reverse(right)); 38 | }; 39 | 40 | static_assert(rain_water({0,1,0,2,1,0,1,3,2,1,2,1}) == 6); 41 | static_assert(rain_water({4,2,0,3,2,5}) == 9); 42 | 43 | int main() {} -------------------------------------------------------------------------------- /example/top10/02_max_consecutive_ones.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * Given a binary array nums, return the maximum number of consecutive 1's in the array. 8 | * 9 | * https://leetcode.com/problems/max-consecutive-ones/ 10 | */ 11 | 12 | #include 13 | 14 | namespace version1 { 15 | 16 | auto const mco = [](std::initializer_list nums) 17 | { 18 | return flux::ref(nums) 19 | .chunk_by(std::equal_to{}) 20 | .map(flux::sum) 21 | .max() 22 | .value_or(0); 23 | }; 24 | 25 | static_assert(mco({1,1,0,1,1,1}) == 3); 26 | static_assert(mco({1,0,1,1,0,1}) == 2); 27 | 28 | } 29 | 30 | namespace version2 { 31 | 32 | auto const mco = [](std::initializer_list nums) 33 | { 34 | return flux::ref(nums) 35 | .scan([](int count, int i) { return i * (count + i); }) 36 | .max() 37 | .value_or(0); 38 | }; 39 | 40 | static_assert(mco({1,1,0,1,1,1}) == 3); 41 | static_assert(mco({1,0,1,1,0,1}) == 2); 42 | 43 | } 44 | 45 | int main() {} -------------------------------------------------------------------------------- /example/top10/03_longest_continuous_increasing_subsequence.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * Given an unsorted array of integers nums, return the length of the longest 8 | * continuous increasing subsequence (i.e. subarray). The subsequence must be 9 | * strictly increasing. 10 | * 11 | * https://leetcode.com/problems/longest-continuous-increasing-subsequence/ 12 | */ 13 | 14 | #include 15 | 16 | auto const lcis = [](std::initializer_list nums) 17 | { 18 | return flux::ref(nums) 19 | .chunk_by(std::less{}) 20 | .map(flux::count) 21 | .max() 22 | .value_or(0); 23 | }; 24 | 25 | static_assert(lcis({}) == 0); 26 | static_assert(lcis({1, 3, 5, 4, 7}) == 3); // [1, 3, 5] 27 | static_assert(lcis({2, 2, 2, 2, 2}) == 1); // [2] (must be strictly increasing) 28 | 29 | int main() {} -------------------------------------------------------------------------------- /example/top10/04_maximum_subarray_sum.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * Given an integer array nums, find the subarray with the largest sum, and 8 | * return its sum. 9 | * 10 | * https://leetcode.com/problems/maximum-subarray/ 11 | * 12 | */ 13 | 14 | #include 15 | 16 | auto const kadanes = [](std::initializer_list nums) 17 | { 18 | return flux::ref(nums) 19 | .scan([](int sum, int i) { return std::max(i, sum + i); }) 20 | .max() 21 | .value_or(0); 22 | }; 23 | 24 | static_assert(kadanes({-2, 1, -3, 4, -1, 2, 1, -5, 4}) == 6); 25 | static_assert(kadanes({1}) == 1); 26 | static_assert(kadanes({5, 4, -1, 7, 8}) == 23); 27 | 28 | int main() {} -------------------------------------------------------------------------------- /example/top10/05_sushi_for_two.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * https://codeforces.com/contest/1138/problem/A 8 | */ 9 | 10 | #include 11 | 12 | auto const sushi_for_two = [](std::initializer_list sushi) 13 | { 14 | return 2 * flux::ref(sushi) 15 | .chunk_by(std::equal_to{}) 16 | .map(flux::count) 17 | .pairwise_map(flux::cmp::min) 18 | .max() 19 | .value_or(0); 20 | }; 21 | 22 | static_assert(sushi_for_two({}) == 0); 23 | static_assert(sushi_for_two({2, 2, 2, 1, 1, 2, 2}) == 4); 24 | static_assert(sushi_for_two({1, 2, 1, 2, 1, 2}) == 2); 25 | static_assert(sushi_for_two({2, 2, 1, 1, 1, 2, 2, 2, 2}) == 6); 26 | 27 | int main() {} -------------------------------------------------------------------------------- /example/top10/06_max_gap.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * https://leetcode.com/problems/maximum-gap/ 8 | * 9 | * Note that the original problem specifies that the solution should have O(N) 10 | * runtime and use O(N) extra space. We instead use an O(NlogN) solution as 11 | * discussed on ADSP episode 115 (https://adspthepodcast.com/2023/02/03/Episode-115.html) 12 | * The NlogN version still passes the time limit constraints on leetcode, however. 13 | */ 14 | 15 | #include 16 | 17 | namespace { 18 | 19 | // std::abs is not constexpr in C++20 20 | auto const abs = [](std::signed_integral auto i) { return i < 0 ? -i : i; }; 21 | 22 | auto const max_gap = [](std::vector nums) 23 | { 24 | flux::sort(nums); 25 | return flux::ref(nums) 26 | .pairwise_map([](int a, int b) { return abs(a - b); }) 27 | .max() 28 | .value_or(0); 29 | }; 30 | 31 | } 32 | 33 | int main() 34 | { 35 | FLUX_ASSERT(max_gap({3, 6, 9, 1}) == 3); 36 | FLUX_ASSERT(max_gap({10}) == 0); 37 | } 38 | -------------------------------------------------------------------------------- /example/top10/07_max_gap_count.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * https://theweeklychallenge.org/blog/perl-weekly-challenge-198/ 8 | * 9 | * Also discussed on ADSP episode 116 (https://adspthepodcast.com/2023/02/03/Episode-116.html) 10 | */ 11 | 12 | #include 13 | 14 | // std::abs is not constexpr in C++20 15 | auto const c_abs = [](std::signed_integral auto i) { return i < 0 ? -i : i; }; 16 | 17 | namespace version1 { 18 | 19 | // Sort + two passes 20 | auto const max_gap_count = [](std::vector nums) 21 | { 22 | flux::sort(nums); 23 | 24 | auto diffs = flux::ref(nums).pairwise_map([](int a, int b) { return c_abs(a - b); }); 25 | 26 | return diffs.count_eq(diffs.max().value_or(0)); 27 | }; 28 | 29 | static_assert(max_gap_count({2, 5, 8, 1}) == 2); 30 | static_assert(max_gap_count({3, 6, 9, 1}) == 2); 31 | static_assert(max_gap_count({10}) == 0); 32 | 33 | } 34 | 35 | namespace version2 { 36 | 37 | // Sort + one pass 38 | auto const max_gap_count = [](std::vector nums) 39 | { 40 | struct max_count { 41 | int value; 42 | int count; 43 | }; 44 | 45 | flux::sort(nums); 46 | return flux::ref(nums) 47 | .pairwise_map([](int a, int b) { return c_abs(a - b); }) 48 | .fold([](max_count max, int i) { 49 | if (i > max.value) { 50 | max = max_count{i, 1}; 51 | } else if (i == max.value ){ 52 | ++max.count; 53 | } 54 | return max; 55 | }, max_count{}) 56 | .count; 57 | }; 58 | 59 | static_assert(max_gap_count({2, 5, 8, 1}) == 2); 60 | static_assert(max_gap_count({3, 6, 9, 1}) == 2); 61 | static_assert(max_gap_count({10}) == 0); 62 | 63 | } 64 | 65 | int main() 66 | {} 67 | -------------------------------------------------------------------------------- /example/top10/08_three_consecutive_odds.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * Given an integer array arr, return true if there are three consecutive odd 8 | * numbers in the array. Otherwise, return false. 9 | * 10 | * https://leetcode.com/problems/three-consecutive-odds/ 11 | */ 12 | 13 | #include 14 | 15 | namespace version1 { 16 | 17 | auto const tco = [](std::initializer_list nums) 18 | { 19 | int odd_count = 0; 20 | auto idx = flux::for_each_while(nums, [&](int i) { 21 | if (i % 2 != 0) { 22 | ++odd_count; 23 | } else { 24 | odd_count = 0; 25 | } 26 | return odd_count < 3; // true => keep iterating 27 | }); 28 | return !flux::is_last(nums, idx); 29 | }; 30 | 31 | static_assert(tco({}) == false); 32 | static_assert(tco({2, 6, 4, 1}) == false); 33 | static_assert(tco({1, 2, 34, 3, 4, 5, 7, 23, 12}) == true); 34 | 35 | } 36 | 37 | namespace version2 { 38 | 39 | auto const tco = [](std::initializer_list nums) 40 | { 41 | auto all_true = [](auto... b) { return (b && ...); }; 42 | return flux::ref(nums) 43 | .map(flux::pred::odd) 44 | .adjacent_map<3>(all_true) 45 | .any(flux::pred::id); 46 | }; 47 | 48 | static_assert(tco({}) == false); 49 | static_assert(tco({2, 6, 4, 1}) == false); 50 | static_assert(tco({1, 2, 34, 3, 4, 5, 7, 23, 12}) == true); 51 | 52 | } 53 | 54 | namespace version3 { 55 | 56 | auto const tco = [](std::initializer_list nums) 57 | { 58 | return flux::ref(nums) 59 | .scan([](int count, int elem) { return (elem % 2 != 0) ? ++count : 0; }) 60 | .any(flux::pred::geq(3)); 61 | }; 62 | 63 | static_assert(tco({}) == false); 64 | static_assert(tco({2, 6, 4, 1}) == false); 65 | static_assert(tco({1, 2, 34, 3, 4, 5, 7, 23, 12}) == true); 66 | 67 | } 68 | 69 | int main() {} -------------------------------------------------------------------------------- /example/top10/09_skyline.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * Given an integer list of skyscraper heights from left to right, calculate how 8 | * many buildings can be seen by an observer standing at the left-most position. 9 | * 10 | * (Ignoring trigonometry...) 11 | * 12 | * https://youtu.be/6AWSPC6qQB4?t=560 13 | */ 14 | 15 | #include 16 | 17 | auto const skyline = [](std::initializer_list heights) 18 | { 19 | auto h = flux::ref(heights); 20 | 21 | return 1 + flux::zip(flux::drop(h, 1), flux::scan(h, flux::cmp::max)) 22 | .count_if([](auto p) { return p.first > p.second; }); 23 | }; 24 | 25 | static_assert(skyline({5, 5, 2, 10, 3, 15, 10}) == 3); 26 | 27 | int main() {} -------------------------------------------------------------------------------- /example/top10/10_ocean_view.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * There are n buildings in a line. You are given an integer array heights of 8 | * size n that represents the heights of the buildings in the line. 9 | * 10 | * The ocean is to the right of the buildings. A building has an ocean view if 11 | * the building can see the ocean without obstructions. Formally, a building has 12 | * an ocean view if all the buildings to its right have a smaller height. 13 | * 14 | * https://leetcode.ca/all/1762.html 15 | */ 16 | 17 | #include 18 | 19 | #include 20 | 21 | using index_vec = std::vector; 22 | 23 | auto const ocean_view = [](std::vector const& heights) -> index_vec 24 | { 25 | auto indices = flux::zip(flux::ref(heights).cursors().reverse(), 26 | flux::ref(heights).reverse().prescan(flux::cmp::max, 0)) 27 | .filter([&](auto pair) { return flux::read_at(heights, pair.first) > pair.second; }) 28 | .map([](auto pair) { return pair.first; }) 29 | .to(); 30 | 31 | flux::inplace_reverse(indices); 32 | 33 | return indices; 34 | }; 35 | 36 | int main() 37 | { 38 | FLUX_ASSERT(ocean_view({4,2,3,1}) == index_vec({0,2,3})); 39 | FLUX_ASSERT(ocean_view({4,3,2,1}) == index_vec({0,1,2,3})); 40 | FLUX_ASSERT(ocean_view({1,3,2,4}) == index_vec{3}); 41 | FLUX_ASSERT(ocean_view({2,2,2,2}) == index_vec{3}); 42 | } -------------------------------------------------------------------------------- /example/top10/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## The Top Ten Algorithms ## 3 | 4 | This folder has solutions for Conor Hoekstra's (@codereport) [Top 10 Algorithms Problems](https://github.com/codereport/top10), using the Flux library. 5 | 6 | Some of the files contain multiple solutions, demonstrating different ways of solving the problem. 7 | 8 | The problems are: 9 | 10 | 1. [Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/) 11 | 2. [Max Consecutive Ones](https://leetcode.com/problems/max-consecutive-ones/) 12 | 3. [Longest Continuous Increasing Subsequence](https://leetcode.com/problems/longest-continuous-increasing-subsequence/) 13 | 4. [Maximum Subarray Sum](https://leetcode.com/problems/maximum-subarray/) 14 | 5. [Sushi for Two](https://codeforces.com/contest/1138/problem/A) 15 | 6. [Maximum Gap](https://leetcode.com/problems/maximum-gap/) 16 | 7. [Maximum Gap Count](https://theweeklychallenge.org/blog/perl-weekly-challenge-198/) 17 | 8. [Three Consecutive Odds](https://leetcode.com/problems/three-consecutive-odds/) 18 | 9. [Skyline](https://youtu.be/6AWSPC6qQB4?t=560) 19 | 10. [Ocean View](https://leetcode.ca/all/1762.html) 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/word_count.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Jiri Nytra (jiri.nytra at gmail dot com) 2 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct stats_t 13 | { 14 | std::size_t lines = 0; 15 | std::size_t words = 0; 16 | std::size_t chars = 0; 17 | bool is_last_space = true; 18 | }; 19 | 20 | auto collect_stats = [](flux::sequence auto&& seq) 21 | { 22 | stats_t stats; 23 | flux::for_each(FLUX_FWD(seq), [&stats](char val) 24 | { 25 | stats.chars++; 26 | if (not stats.is_last_space and std::isspace(val)) { 27 | stats.words++; 28 | } 29 | if (val == '\n') { 30 | stats.lines++; 31 | } 32 | stats.is_last_space = std::isspace(val); 33 | }); 34 | 35 | if (not stats.is_last_space) { 36 | stats.words++; 37 | } 38 | 39 | return stats; 40 | }; 41 | 42 | void print_stats(const stats_t& stats) 43 | { 44 | std::cout << stats.lines << ' ' << stats.words << ' ' << stats.chars << '\n'; 45 | } 46 | 47 | int main() 48 | { 49 | // Print newline, word, and byte counts from std input (like gnu wc) 50 | std::noskipws(std::cin); 51 | auto result = flux::from_istream(std::cin)._(collect_stats); 52 | print_stats(result); 53 | } -------------------------------------------------------------------------------- /include/flux.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_HPP_INCLUDED 7 | #define FLUX_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /include/flux/adaptor.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #endif // FLUX_ADAPTOR_HPP_INCLUDED 45 | -------------------------------------------------------------------------------- /include/flux/adaptor/cache_last.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_CACHE_LAST_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_CACHE_LAST_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct cache_last_adaptor : inline_sequence_base> 17 | { 18 | private: 19 | Base base_; 20 | flux::optional> cached_last_{}; 21 | 22 | friend struct passthrough_traits_base; 23 | 24 | constexpr auto base() -> Base& { return base_; } 25 | 26 | public: 27 | constexpr explicit cache_last_adaptor(decays_to auto&& base) 28 | : base_(FLUX_FWD(base)) 29 | {} 30 | 31 | struct flux_sequence_traits : detail::passthrough_traits_base { 32 | 33 | using value_type = value_t; 34 | using self_t = cache_last_adaptor; 35 | 36 | static constexpr bool disable_multipass = !multipass_sequence; 37 | 38 | static constexpr auto is_last(self_t& self, cursor_t const& cur) 39 | { 40 | if (flux::is_last(self.base_, cur)) { 41 | self.cached_last_ = flux::optional(cur); 42 | return true; 43 | } else { 44 | return false; 45 | } 46 | } 47 | 48 | static constexpr auto last(self_t& self) 49 | { 50 | if (!self.cached_last_) { 51 | auto cur = flux::first(self); 52 | while (!is_last(self, cur)) { 53 | flux::inc(self.base_, cur); 54 | } 55 | FLUX_DEBUG_ASSERT(self.cached_last_.has_value()); 56 | } 57 | return self.cached_last_.value_unchecked(); 58 | } 59 | }; 60 | }; 61 | 62 | struct cache_last_fn { 63 | template 64 | requires bounded_sequence || 65 | (multipass_sequence && not infinite_sequence) 66 | [[nodiscard]] 67 | constexpr auto operator()(Seq&& seq) const 68 | { 69 | if constexpr (bounded_sequence) { 70 | return FLUX_FWD(seq); 71 | } else { 72 | return cache_last_adaptor>(FLUX_FWD(seq)); 73 | } 74 | } 75 | }; 76 | 77 | } // namespace detail 78 | 79 | FLUX_EXPORT inline constexpr auto cache_last = detail::cache_last_fn{}; 80 | 81 | template 82 | constexpr auto inline_sequence_base::cache_last() && 83 | requires bounded_sequence || 84 | (multipass_sequence && not infinite_sequence) 85 | { 86 | return flux::cache_last(std::move(derived())); 87 | } 88 | 89 | } // namespace flux 90 | 91 | #endif // FLUX_ADAPTOR_CACHE_LAST_HPP_INCLUDED 92 | -------------------------------------------------------------------------------- /include/flux/adaptor/cartesian_power.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Copyright (c) 2023 NVIDIA Corporation (reply-to: brycelelbach@gmail.com) 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef FLUX_ADAPTOR_CARTESIAN_POWER_HPP_INCLUDED 9 | #define FLUX_ADAPTOR_CARTESIAN_POWER_HPP_INCLUDED 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace flux { 18 | 19 | namespace detail { 20 | 21 | template 22 | struct cartesian_power_adaptor 23 | : inline_sequence_base> { 24 | private: 25 | FLUX_NO_UNIQUE_ADDRESS Base base_; 26 | 27 | public: 28 | constexpr explicit cartesian_power_adaptor(decays_to auto&& base) 29 | : base_(FLUX_FWD(base)) 30 | {} 31 | 32 | using flux_sequence_traits = cartesian_traits_base< 33 | PowN, 34 | cartesian_kind::power, 35 | read_kind::tuple, 36 | Base 37 | >; 38 | friend flux_sequence_traits::impl; 39 | }; 40 | 41 | 42 | 43 | template 44 | struct cartesian_power_fn { 45 | 46 | template 47 | requires multipass_sequence 48 | [[nodiscard]] 49 | constexpr auto operator()(Seq&& seq) const 50 | { 51 | if constexpr(PowN == 0) { 52 | return empty>; 53 | } else { 54 | return cartesian_power_adaptor>( 55 | FLUX_FWD(seq)); 56 | } 57 | } 58 | }; 59 | 60 | 61 | } // end namespace detail 62 | 63 | FLUX_EXPORT 64 | template 65 | requires (N >= 0) 66 | inline constexpr auto cartesian_power = detail::cartesian_power_fn{}; 67 | 68 | } // end namespace flux 69 | 70 | #endif // FLUX_ADAPTOR_CARTESIAN_POWER_HPP_INCLUDED 71 | -------------------------------------------------------------------------------- /include/flux/adaptor/cartesian_power_map.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_CARTESIAN_POWER_MAP_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_CARTESIAN_POWER_MAP_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct cartesian_power_map_adaptor 17 | : inline_sequence_base> { 18 | private: 19 | FLUX_NO_UNIQUE_ADDRESS Base base_; 20 | FLUX_NO_UNIQUE_ADDRESS Func func_; 21 | 22 | public: 23 | constexpr explicit cartesian_power_map_adaptor(decays_to auto&& base, decays_to auto&& func) 24 | : base_(FLUX_FWD(base)), 25 | func_(FLUX_FWD(func)) 26 | {} 27 | 28 | using flux_sequence_traits = cartesian_traits_base< 29 | PowN, 30 | cartesian_kind::power, 31 | read_kind::map, 32 | Base 33 | >; 34 | friend flux_sequence_traits::impl; 35 | }; 36 | 37 | template 38 | struct cartesian_power_map_fn 39 | { 40 | template 41 | requires multipass_sequence && 42 | detail::repeated_invocable, PowN> 43 | [[nodiscard]] 44 | constexpr auto operator()(Seq&& seq, Func func) const 45 | { 46 | if constexpr(PowN == 0) { 47 | return empty>; 48 | } else { 49 | return cartesian_power_map_adaptor, PowN, Func>( 50 | FLUX_FWD(seq), std::move(func)); 51 | } 52 | } 53 | }; 54 | 55 | } // namespace detail 56 | 57 | FLUX_EXPORT 58 | template 59 | requires (N >= 0) 60 | inline constexpr auto cartesian_power_map = detail::cartesian_power_map_fn{}; 61 | 62 | } // namespace flux 63 | 64 | #endif // FLUX_ADAPTOR_CARTESIAN_POWER_MAP_HPP_INCLUDED 65 | -------------------------------------------------------------------------------- /include/flux/adaptor/cartesian_product.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Copyright (c) 2023 NVIDIA Corporation (reply-to: brycelelbach@gmail.com) 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef FLUX_ADAPTOR_CARTESIAN_PRODUCT_HPP_INCLUDED 9 | #define FLUX_ADAPTOR_CARTESIAN_PRODUCT_HPP_INCLUDED 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace flux { 18 | 19 | namespace detail { 20 | 21 | template 22 | struct cartesian_product_adaptor 23 | : inline_sequence_base> { 24 | private: 25 | FLUX_NO_UNIQUE_ADDRESS std::tuple bases_; 26 | 27 | public: 28 | constexpr explicit cartesian_product_adaptor(decays_to auto&&... bases) 29 | : bases_(FLUX_FWD(bases)...) 30 | {} 31 | 32 | using flux_sequence_traits = cartesian_traits_base< 33 | sizeof...(Bases), 34 | cartesian_kind::product, 35 | read_kind::tuple, 36 | Bases... 37 | >; 38 | friend flux_sequence_traits::impl; 39 | }; 40 | 41 | struct cartesian_product_fn { 42 | template 43 | requires (multipass_sequence && ...) 44 | [[nodiscard]] 45 | constexpr auto operator()(Seq0&& seq0, Seqs&&... seqs) const 46 | { 47 | return cartesian_product_adaptor, std::decay_t...>( 48 | FLUX_FWD(seq0), FLUX_FWD(seqs)...); 49 | } 50 | }; 51 | 52 | } // end namespace detail 53 | 54 | FLUX_EXPORT inline constexpr auto cartesian_product = detail::cartesian_product_fn{}; 55 | 56 | 57 | } // end namespace flux 58 | 59 | #endif // FLUX_ADAPTOR_CARTESIAN_PRODUCT_HPP_INCLUDED 60 | -------------------------------------------------------------------------------- /include/flux/adaptor/cartesian_product_map.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_CARTESIAN_PRODUCT_MAP_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_CARTESIAN_PRODUCT_MAP_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct cartesian_product_map_adaptor 17 | : inline_sequence_base> { 18 | private: 19 | FLUX_NO_UNIQUE_ADDRESS std::tuple bases_; 20 | FLUX_NO_UNIQUE_ADDRESS Func func_; 21 | 22 | public: 23 | constexpr explicit cartesian_product_map_adaptor(decays_to auto&& func, decays_to auto&&... bases) 24 | : bases_(FLUX_FWD(bases)...), 25 | func_(FLUX_FWD(func)) 26 | {} 27 | 28 | using flux_sequence_traits = cartesian_traits_base< 29 | sizeof...(Bases), 30 | cartesian_kind::product, 31 | read_kind::map, 32 | Bases... 33 | >; 34 | friend flux_sequence_traits::impl; 35 | }; 36 | 37 | struct cartesian_product_map_fn 38 | { 39 | template 40 | requires (multipass_sequence && ...) && 41 | std::regular_invocable, element_t...> 42 | [[nodiscard]] 43 | constexpr auto operator()(Func func, Seq0&& seq0, Seqs&&... seqs) const 44 | { 45 | return cartesian_product_map_adaptor, std::decay_t...>( 46 | std::move(func), FLUX_FWD(seq0), FLUX_FWD(seqs)...); 47 | } 48 | }; 49 | 50 | 51 | 52 | } // namespace detail 53 | 54 | FLUX_EXPORT inline constexpr auto cartesian_product_map = detail::cartesian_product_map_fn{}; 55 | 56 | } // namespace flux 57 | 58 | #endif // FLUX_ADAPTOR_CARTESIAN_PRODUCT_MAP_HPP_INCLUDED 59 | -------------------------------------------------------------------------------- /include/flux/adaptor/drop.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_DROP_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_DROP_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | 12 | namespace flux { 13 | 14 | namespace detail { 15 | 16 | template 17 | struct drop_adaptor : inline_sequence_base> { 18 | private: 19 | FLUX_NO_UNIQUE_ADDRESS Base base_; 20 | distance_t count_; 21 | 22 | public: 23 | constexpr drop_adaptor(decays_to auto&& base, distance_t count) 24 | : base_(FLUX_FWD(base)), 25 | count_(count) 26 | {} 27 | 28 | constexpr Base& base() & { return base_; } 29 | constexpr Base const& base() const& { return base_; } 30 | 31 | struct flux_sequence_traits : passthrough_traits_base { 32 | using value_type = value_t; 33 | 34 | static constexpr bool disable_multipass = !multipass_sequence; 35 | 36 | static constexpr auto first(auto& self) -> cursor_t 37 | { 38 | auto cur = flux::first(self.base_); 39 | detail::advance(self.base_, cur, self.count_); 40 | return cur; 41 | } 42 | 43 | static constexpr auto size(auto& self) 44 | requires sized_sequence 45 | { 46 | return (cmp::max)(num::sub(flux::size(self.base()), self.count_), 47 | distance_t{0}); 48 | } 49 | 50 | static constexpr auto data(auto& self) 51 | requires contiguous_sequence && sized_sequence 52 | { 53 | return flux::data(self.base()) + (cmp::min)(self.count_, flux::size(self.base_)); 54 | } 55 | 56 | static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_t 57 | { 58 | return default_sequence_traits::for_each_while(self, FLUX_FWD(pred)); 59 | } 60 | }; 61 | }; 62 | 63 | struct drop_fn { 64 | template 65 | [[nodiscard]] 66 | constexpr auto operator()(Seq&& seq, num::integral auto count) const 67 | { 68 | auto count_ = num::checked_cast(count); 69 | if (count_ < 0) { 70 | runtime_error("Negative argument passed to drop()"); 71 | } 72 | 73 | return drop_adaptor>(FLUX_FWD(seq), count_); 74 | } 75 | 76 | }; 77 | 78 | } // namespace detail 79 | 80 | FLUX_EXPORT inline constexpr auto drop = detail::drop_fn{}; 81 | 82 | template 83 | constexpr auto inline_sequence_base::drop(num::integral auto count) && 84 | { 85 | return flux::drop(std::move(derived()), count); 86 | } 87 | 88 | } // namespace flux 89 | 90 | #endif // FLUX_ADAPTOR_DROP_HPP_INCLUDED 91 | -------------------------------------------------------------------------------- /include/flux/adaptor/drop_while.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_DROP_WHILE_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_DROP_WHILE_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct drop_while_adaptor : inline_sequence_base> { 17 | private: 18 | FLUX_NO_UNIQUE_ADDRESS Base base_; 19 | FLUX_NO_UNIQUE_ADDRESS Pred pred_; 20 | 21 | friend struct passthrough_traits_base; 22 | 23 | constexpr auto base() & -> Base& { return base_; } 24 | constexpr auto base() const& -> Base const& { return base_; } 25 | 26 | public: 27 | constexpr drop_while_adaptor(decays_to auto&& base, decays_to auto&& pred) 28 | : base_(FLUX_FWD(base)), 29 | pred_(FLUX_FWD(pred)) 30 | {} 31 | 32 | struct flux_sequence_traits : detail::passthrough_traits_base { 33 | using value_type = value_t; 34 | 35 | static constexpr bool disable_multipass = !multipass_sequence; 36 | 37 | static constexpr auto first(auto& self) 38 | { 39 | return flux::for_each_while(self.base_, std::ref(self.pred_)); 40 | } 41 | 42 | static constexpr auto data(auto& self) 43 | requires contiguous_sequence 44 | { 45 | return flux::data(self.base_) + 46 | flux::distance(self.base_, flux::first(self.base_), first(self)); 47 | } 48 | 49 | using default_sequence_traits::size; 50 | using default_sequence_traits::for_each_while; 51 | }; 52 | }; 53 | 54 | struct drop_while_fn { 55 | template 56 | requires std::predicate> 57 | [[nodiscard]] 58 | constexpr auto operator()(Seq&& seq, Pred pred) const 59 | { 60 | return drop_while_adaptor, Pred>(FLUX_FWD(seq), std::move(pred)); 61 | } 62 | }; 63 | 64 | } // namespace detail 65 | 66 | FLUX_EXPORT inline constexpr auto drop_while = detail::drop_while_fn{}; 67 | 68 | template 69 | template 70 | requires std::predicate> 71 | constexpr auto inline_sequence_base::drop_while(Pred pred) && 72 | { 73 | return flux::drop_while(std::move(derived()), std::move(pred)); 74 | }; 75 | 76 | } // namespace flux 77 | 78 | #endif // FLUX_ADAPTOR_DROP_WHILE_HPP_INCLUDED 79 | -------------------------------------------------------------------------------- /include/flux/adaptor/filter_map.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_FILTER_MAP_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_FILTER_MAP_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | 12 | namespace flux { 13 | 14 | namespace detail { 15 | 16 | struct filter_map_fn { 17 | // If dereffing the optional would give us an rvalue reference, 18 | // prevent a probable dangling reference by returning by value instead 19 | template 20 | using strip_rvalue_ref_t = std::conditional_t< 21 | std::is_rvalue_reference_v, std::remove_reference_t, T>; 22 | 23 | template 24 | requires (std::invocable> && 25 | optional_like>>>) 26 | constexpr auto operator()(Seq&& seq, Func func) const 27 | { 28 | return flux::map(FLUX_FWD(seq), std::move(func)) 29 | .filter([](auto&& opt) { return static_cast(opt); }) 30 | .map([](auto&& opt) -> strip_rvalue_ref_t { 31 | return *FLUX_FWD(opt); 32 | }); 33 | } 34 | }; 35 | 36 | } // namespace detail 37 | 38 | FLUX_EXPORT inline constexpr auto filter_map = detail::filter_map_fn{}; 39 | 40 | template 41 | template 42 | requires std::invocable> && 43 | detail::optional_like>> 44 | constexpr auto inline_sequence_base::filter_map(Func func) && 45 | { 46 | return flux::filter_map(derived(), std::move(func)); 47 | } 48 | 49 | namespace detail 50 | { 51 | 52 | struct filter_deref_fn { 53 | template 54 | requires optional_like> 55 | constexpr auto operator()(Seq&& seq) const 56 | { 57 | return filter_map(FLUX_FWD(seq), [](auto&& opt) -> decltype(auto) { return FLUX_FWD(opt); }); 58 | } 59 | }; 60 | 61 | } // namespace detail 62 | 63 | FLUX_EXPORT inline constexpr auto filter_deref = detail::filter_deref_fn{}; 64 | 65 | template 66 | constexpr auto inline_sequence_base::filter_deref() && requires detail::optional_like> 67 | { 68 | return flux::filter_deref(derived()); 69 | } 70 | } // namespace flux 71 | 72 | #endif // FLUX_ADAPTOR_FILTER_MAP_HPP_INCLUDED 73 | -------------------------------------------------------------------------------- /include/flux/adaptor/read_only.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_READ_ONLY_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_READ_ONLY_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | 12 | namespace flux { 13 | 14 | namespace detail { 15 | 16 | template 17 | struct cast_to_const { 18 | constexpr auto operator()(auto&& elem) const -> T { return FLUX_FWD(elem); } 19 | }; 20 | 21 | template 22 | requires (not read_only_sequence) 23 | struct read_only_adaptor : map_adaptor>> { 24 | private: 25 | using map = map_adaptor>>; 26 | 27 | public: 28 | constexpr explicit read_only_adaptor(decays_to auto&& base) 29 | : map(FLUX_FWD(base), cast_to_const>{}) 30 | {} 31 | 32 | struct flux_sequence_traits : map::flux_sequence_traits { 33 | private: 34 | using const_rvalue_element_t = std::common_reference_t< 35 | value_t const&&, rvalue_element_t>; 36 | 37 | public: 38 | using value_type = value_t; 39 | 40 | static constexpr auto move_at(auto& self, cursor_t const& cur) 41 | -> const_rvalue_element_t 42 | { 43 | return static_cast(flux::move_at(self.base(), cur)); 44 | } 45 | 46 | static constexpr auto move_at_unchecked(auto& self, cursor_t const& cur) 47 | -> const_rvalue_element_t 48 | { 49 | return static_cast(flux::move_at_unchecked(self.base(), cur)); 50 | } 51 | 52 | static constexpr auto data(auto& self) 53 | requires contiguous_sequence 54 | { 55 | using P = std::add_pointer_t>>; 56 | return static_cast

(flux::data(self.base())); 57 | } 58 | }; 59 | }; 60 | 61 | struct read_only_fn { 62 | template 63 | [[nodiscard]] 64 | constexpr auto operator()(Seq&& seq) const -> read_only_sequence auto 65 | { 66 | if constexpr (read_only_sequence) { 67 | return FLUX_FWD(seq); 68 | } else { 69 | return read_only_adaptor>(FLUX_FWD(seq)); 70 | } 71 | } 72 | }; 73 | 74 | 75 | } // namespace detail 76 | 77 | FLUX_EXPORT inline constexpr auto read_only = detail::read_only_fn{}; 78 | 79 | template 80 | constexpr auto inline_sequence_base::read_only() && 81 | { 82 | return flux::read_only(std::move(derived())); 83 | } 84 | 85 | } // namespace flux 86 | 87 | #endif // FLUX_ADAPTOR_READ_ONlY_HPP_INCLUDED 88 | -------------------------------------------------------------------------------- /include/flux/adaptor/split_string.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_STRING_SPLIT_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_STRING_SPLIT_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace flux { 15 | 16 | namespace detail { 17 | 18 | template 19 | concept character = any_of; 20 | 21 | struct to_string_view_fn { 22 | template 23 | requires sized_sequence && character> 24 | constexpr auto operator()(Seq&& seq) const 25 | { 26 | return std::basic_string_view>(flux::data(seq), flux::usize(seq)); 27 | } 28 | }; 29 | 30 | inline constexpr auto to_string_view = to_string_view_fn{}; 31 | 32 | struct split_string_fn { 33 | 34 | template 35 | requires character> && 36 | std::equality_comparable_with, element_t> 37 | constexpr auto operator()(Seq&& seq, Pattern&& pattern) const 38 | { 39 | return flux::split(FLUX_FWD(seq), FLUX_FWD(pattern)).map(to_string_view); 40 | } 41 | 42 | // Attempt to hijack string literal patterns to do the right thing 43 | template 44 | requires character> 45 | constexpr auto operator()(Seq&& seq, value_t const (&pattern)[N]) const 46 | { 47 | return flux::split(FLUX_FWD(seq), std::basic_string_view(pattern)) 48 | .map(to_string_view); 49 | } 50 | 51 | template 52 | requires character> 53 | constexpr auto operator()(Seq&& seq, value_t delim) const 54 | { 55 | return flux::split(FLUX_FWD(seq), delim).map(to_string_view); 56 | } 57 | }; 58 | 59 | } // namespace detail 60 | 61 | FLUX_EXPORT inline constexpr auto split_string = detail::split_string_fn{}; 62 | 63 | template 64 | constexpr auto inline_sequence_base::split_string(auto&& pattern) && 65 | { 66 | return flux::split_string(std::move(derived()), FLUX_FWD(pattern)); 67 | } 68 | 69 | 70 | } // namespace flux 71 | 72 | #endif // FLUX_ADAPTOR_SPLIT_STRING_HPP_INCLUDED 73 | -------------------------------------------------------------------------------- /include/flux/adaptor/take_while.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_TAKE_WHILE_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_TAKE_WHILE_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct take_while_adaptor : inline_sequence_base> { 17 | private: 18 | Base base_; 19 | Pred pred_; 20 | 21 | constexpr auto base() & -> Base& { return base_; } 22 | 23 | friend struct sequence_traits; 24 | friend struct passthrough_traits_base; 25 | 26 | public: 27 | constexpr take_while_adaptor(decays_to auto&& base, decays_to auto&& pred) 28 | : base_(FLUX_FWD(base)), 29 | pred_(FLUX_FWD(pred)) 30 | {} 31 | 32 | [[nodiscard]] constexpr auto base() const& -> Base const& { return base_; } 33 | [[nodiscard]] constexpr auto base() && -> Base { return std::move(base_); } 34 | }; 35 | 36 | struct take_while_fn { 37 | template 38 | requires std::predicate> 39 | [[nodiscard]] 40 | constexpr auto operator()(Seq&& seq, Pred pred) const 41 | { 42 | return take_while_adaptor, Pred>( 43 | FLUX_FWD(seq), std::move(pred)); 44 | } 45 | }; 46 | 47 | } // namespace detail 48 | 49 | template 50 | struct sequence_traits> 51 | : detail::passthrough_traits_base 52 | { 53 | using self_t = detail::take_while_adaptor; 54 | 55 | using value_type = value_t; 56 | 57 | static constexpr bool is_infinite = false; 58 | 59 | template 60 | static constexpr bool is_last(Self& self, cursor_t const& cur) 61 | requires std::predicate> 62 | { 63 | if (flux::is_last(self.base_, cur) || 64 | !std::invoke(self.pred_, flux::read_at(self.base_, cur))) { 65 | return true; 66 | } else { 67 | return false; 68 | } 69 | } 70 | 71 | void last() = delete; 72 | void size() = delete; 73 | 74 | static constexpr auto for_each_while(auto& self, auto&& func) 75 | { 76 | return flux::for_each_while(self.base_, [&](auto&& elem) { 77 | if (!std::invoke(self.pred_, elem)) { 78 | return false; 79 | } else { 80 | return std::invoke(func, FLUX_FWD(elem)); 81 | } 82 | }); 83 | } 84 | }; 85 | 86 | FLUX_EXPORT inline constexpr auto take_while = detail::take_while_fn{}; 87 | 88 | template 89 | template 90 | requires std::predicate> 91 | constexpr auto inline_sequence_base::take_while(Pred pred) && 92 | { 93 | return flux::take_while(std::move(derived()), std::move(pred)); 94 | } 95 | 96 | } // namespace flux 97 | 98 | #endif // FLUX_ADAPTOR_TAKE_WHILE_HPP_INCLUDED 99 | -------------------------------------------------------------------------------- /include/flux/adaptor/unchecked.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ADAPTOR_UNCHECKED_HPP_INCLUDED 7 | #define FLUX_ADAPTOR_UNCHECKED_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct unchecked_adaptor : inline_sequence_base> { 17 | private: 18 | Base base_; 19 | 20 | public: 21 | constexpr explicit unchecked_adaptor(decays_to auto&& base) 22 | : base_(FLUX_FWD(base)) 23 | {} 24 | 25 | constexpr auto base() & -> Base& { return base_; } 26 | constexpr auto base() const& -> Base const& { return base_; } 27 | 28 | struct flux_sequence_traits : passthrough_traits_base { 29 | 30 | using value_type = value_t; 31 | static constexpr bool disable_multipass = !multipass_sequence; 32 | static constexpr bool is_infinite = infinite_sequence; 33 | 34 | static constexpr auto read_at(auto& self, auto const& cur) 35 | -> element_t 36 | { 37 | return flux::read_at_unchecked(self.base(), cur); 38 | } 39 | 40 | static constexpr auto move_at(auto& self, auto const& cur) 41 | -> rvalue_element_t 42 | { 43 | return flux::move_at_unchecked(self.base(), cur); 44 | } 45 | }; 46 | }; 47 | 48 | struct unchecked_fn { 49 | template 50 | [[nodiscard]] 51 | constexpr auto operator()(Seq&& seq) const 52 | -> unchecked_adaptor> 53 | { 54 | return unchecked_adaptor>(FLUX_FWD(seq)); 55 | } 56 | }; 57 | 58 | } // namespace detail 59 | 60 | FLUX_EXPORT inline constexpr auto unchecked = detail::unchecked_fn{}; 61 | 62 | } // namespace flux 63 | 64 | #endif // FLUX_ADAPTOR_UNCHECKED_HPP_INCLUDED 65 | -------------------------------------------------------------------------------- /include/flux/algorithm.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #endif // FLUX_ALGORITHM_HPP_INCLUDED 32 | -------------------------------------------------------------------------------- /include/flux/algorithm/all_any_none.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_ALL_ANY_NONE_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_ALL_ANY_NONE_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace all_detail { 14 | 15 | struct fn { 16 | template 17 | requires std::predicate> 18 | constexpr bool operator()(Seq&& seq, Pred pred) const 19 | { 20 | return is_last(seq, for_each_while(seq, [&](auto&& elem) { 21 | return std::invoke(pred, FLUX_FWD(elem)); 22 | })); 23 | } 24 | }; 25 | 26 | } // namespace all_detail 27 | 28 | FLUX_EXPORT inline constexpr auto all = all_detail::fn{}; 29 | 30 | namespace none_detail { 31 | 32 | struct fn { 33 | template 34 | requires std::predicate> 35 | constexpr bool operator()(Seq&& seq, Pred pred) const 36 | { 37 | return is_last(seq, for_each_while(seq, [&](auto&& elem) { 38 | return !std::invoke(pred, FLUX_FWD(elem)); 39 | })); 40 | } 41 | }; 42 | 43 | } // namespace none_detail 44 | 45 | FLUX_EXPORT inline constexpr auto none = none_detail::fn{}; 46 | 47 | namespace any_detail { 48 | 49 | struct fn { 50 | template 51 | requires std::predicate> 52 | constexpr bool operator()(Seq&& seq, Pred pred) const 53 | { 54 | return !is_last(seq, for_each_while(seq, [&](auto&& elem) { 55 | return !std::invoke(pred, FLUX_FWD(elem)); 56 | })); 57 | } 58 | }; 59 | 60 | } // namespace any_detail 61 | 62 | FLUX_EXPORT inline constexpr auto any = any_detail::fn{}; 63 | 64 | template 65 | template 66 | requires std::predicate> 67 | constexpr auto inline_sequence_base::all(Pred pred) 68 | { 69 | return flux::all(derived(), std::move(pred)); 70 | } 71 | 72 | template 73 | template 74 | requires std::predicate> 75 | constexpr auto inline_sequence_base::any(Pred pred) 76 | { 77 | return flux::any(derived(), std::move(pred)); 78 | } 79 | 80 | template 81 | template 82 | requires std::predicate> 83 | constexpr auto inline_sequence_base::none(Pred pred) 84 | { 85 | return flux::none(derived(), std::move(pred)); 86 | } 87 | 88 | } // namespace flux 89 | 90 | #endif // FLUX_ALGORITHM_ALL_ANY_NONE_HPP_INCLUDED 91 | -------------------------------------------------------------------------------- /include/flux/algorithm/contains.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_CONTAINS_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_CONTAINS_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | struct contains_fn { 16 | template 17 | requires std::equality_comparable_with, Value const&> 18 | constexpr auto operator()(Seq&& seq, Value const& value) const 19 | -> bool 20 | { 21 | return !flux::is_last(seq, flux::for_each_while(seq, [&](auto&& elem) { 22 | return FLUX_FWD(elem) != value; 23 | })); 24 | } 25 | }; 26 | 27 | 28 | } // namespace detail 29 | 30 | FLUX_EXPORT inline constexpr auto contains = detail::contains_fn{}; 31 | 32 | template 33 | template 34 | requires std::equality_comparable_with, Value const&> 35 | constexpr auto inline_sequence_base::contains(Value const& value) -> bool 36 | { 37 | return flux::contains(derived(), value); 38 | } 39 | 40 | } // namespace flux 41 | 42 | #endif // FLUX_ALGORITHM_CONTAINS_HPP_INCLUDED 43 | -------------------------------------------------------------------------------- /include/flux/algorithm/count.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_COUNT_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_COUNT_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | struct count_fn { 16 | template 17 | [[nodiscard]] 18 | constexpr auto operator()(Seq&& seq) const -> distance_t 19 | { 20 | if constexpr (sized_sequence) { 21 | return flux::size(seq); 22 | } else { 23 | distance_t counter = 0; 24 | flux::for_each_while(seq, [&](auto&&) { 25 | ++counter; 26 | return true; 27 | }); 28 | return counter; 29 | } 30 | } 31 | }; 32 | 33 | struct count_eq_fn { 34 | template 35 | requires std::equality_comparable_with, Value const&> 36 | [[nodiscard]] 37 | constexpr auto operator()(Seq&& seq, Value const& value) const 38 | -> distance_t 39 | { 40 | distance_t counter = 0; 41 | flux::for_each_while(seq, [&](auto&& elem) { 42 | if (value == FLUX_FWD(elem)) { 43 | ++counter; 44 | } 45 | return true; 46 | }); 47 | return counter; 48 | } 49 | }; 50 | 51 | struct count_if_fn { 52 | template 53 | requires std::predicate> 54 | [[nodiscard]] 55 | constexpr auto operator()(Seq&& seq, Pred pred) const 56 | -> distance_t 57 | { 58 | distance_t counter = 0; 59 | flux::for_each_while(seq, [&](auto&& elem) { 60 | if (std::invoke(pred, FLUX_FWD(elem))) { 61 | ++counter; 62 | } 63 | return true; 64 | }); 65 | return counter; 66 | } 67 | }; 68 | 69 | } // namespace detail 70 | 71 | FLUX_EXPORT inline constexpr auto count = detail::count_fn{}; 72 | FLUX_EXPORT inline constexpr auto count_eq = detail::count_eq_fn{}; 73 | FLUX_EXPORT inline constexpr auto count_if = detail::count_if_fn{}; 74 | 75 | template 76 | constexpr auto inline_sequence_base::count() 77 | { 78 | return flux::count(derived()); 79 | } 80 | 81 | template 82 | template 83 | requires std::equality_comparable_with, Value const&> 84 | constexpr auto inline_sequence_base::count_eq(Value const& value) 85 | { 86 | return flux::count_eq(derived(), value); 87 | } 88 | 89 | template 90 | template 91 | requires std::predicate> 92 | constexpr auto inline_sequence_base::count_if(Pred pred) 93 | { 94 | return flux::count_if(derived(), std::move(pred)); 95 | } 96 | 97 | } // namespace flux 98 | 99 | #endif // FLUX_ALGORITHM_COUNT_HPP_INCLUDED 100 | -------------------------------------------------------------------------------- /include/flux/algorithm/fill.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 2 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #ifndef FLUX_ALGORITHM_FILL_HPP_INCLUDED 6 | #define FLUX_ALGORITHM_FILL_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace flux { 13 | 14 | namespace detail { 15 | 16 | struct fill_fn { 17 | private: 18 | template 19 | static constexpr auto impl(Seq& seq, Value const& value) 20 | { 21 | flux::for_each(seq, [&value](auto&& elem) { FLUX_FWD(elem) = value; }); 22 | } 23 | 24 | public: 25 | template Seq> 26 | constexpr void operator()(Seq&& seq, Value const& value) const 27 | { 28 | constexpr bool can_memset = 29 | contiguous_sequence && 30 | sized_sequence && 31 | std::same_as> && 32 | // only allow memset on single byte types 33 | sizeof(value_t) == 1 && 34 | std::is_trivially_copyable_v>; 35 | 36 | if constexpr (can_memset) { 37 | if (std::is_constant_evaluated()) { 38 | impl(seq, value); // LCOV_EXCL_LINE 39 | } else { 40 | auto size = flux::usize(seq); 41 | if(size == 0) { 42 | return; 43 | } 44 | 45 | FLUX_ASSERT(flux::data(seq) != nullptr); 46 | 47 | std::memset(flux::data(seq), value, 48 | size * sizeof(value_t)); 49 | } 50 | } else { 51 | impl(seq, value); 52 | } 53 | } 54 | }; 55 | 56 | } // namespace detail 57 | 58 | FLUX_EXPORT inline constexpr auto fill = detail::fill_fn{}; 59 | 60 | template 61 | template 62 | requires writable_sequence_of 63 | constexpr void inline_sequence_base::fill(Value const& value) 64 | { 65 | flux::fill(derived(), value); 66 | } 67 | 68 | } // namespace flux 69 | 70 | #endif // FLUX_ALGORITHM_FILL_HPP_INCLUDED 71 | -------------------------------------------------------------------------------- /include/flux/algorithm/for_each.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_FOR_EACH_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_FOR_EACH_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | struct for_each_fn { 16 | 17 | template 18 | requires (std::invocable> && 19 | !infinite_sequence) 20 | constexpr auto operator()(Seq&& seq, Func func) const -> Func 21 | { 22 | (void) flux::for_each_while(FLUX_FWD(seq), [&](auto&& elem) { 23 | std::invoke(func, FLUX_FWD(elem)); 24 | return true; 25 | }); 26 | return func; 27 | } 28 | }; 29 | 30 | } // namespace detail 31 | 32 | FLUX_EXPORT inline constexpr auto for_each = detail::for_each_fn{}; 33 | 34 | template 35 | template 36 | requires std::invocable> 37 | constexpr auto inline_sequence_base::for_each(Func func) -> Func 38 | { 39 | return flux::for_each(derived(), std::move(func)); 40 | } 41 | 42 | } // namespace flux 43 | 44 | #endif // FLUX_ALGORITHM_FOR_EACH_HPP_INCLUDED 45 | -------------------------------------------------------------------------------- /include/flux/algorithm/inplace_reverse.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_INPLACE_REVERSE_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_INPLACE_REVERSE_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | struct inplace_reverse_fn { 16 | template 17 | requires bounded_sequence && 18 | element_swappable_with 19 | constexpr void operator()(Seq&& seq) const 20 | { 21 | auto first = flux::first(seq); 22 | auto last = flux::last(seq); 23 | 24 | while (first != last && first != flux::dec(seq, last)) { 25 | flux::swap_at(seq, first, last); 26 | flux::inc(seq, first); 27 | } 28 | } 29 | }; 30 | 31 | } // namespace detail 32 | 33 | FLUX_EXPORT inline constexpr auto inplace_reverse = detail::inplace_reverse_fn{}; 34 | 35 | template 36 | constexpr auto inline_sequence_base::inplace_reverse() 37 | requires bounded_sequence && detail::element_swappable_with 38 | { 39 | return flux::inplace_reverse(derived()); 40 | } 41 | 42 | } // namespace flux 43 | 44 | #endif // FLUX_ALGORITHM_INPLACE_REVERSE_HPP_INCLUDED 45 | -------------------------------------------------------------------------------- /include/flux/algorithm/output_to.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_OUTPUT_TO_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_OUTPUT_TO_HPP_INCLUDED 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace flux { 15 | 16 | namespace detail { 17 | 18 | struct output_to_fn { 19 | private: 20 | template 21 | static constexpr auto impl(Seq& seq, Iter& iter) -> Iter 22 | { 23 | flux::for_each(seq, [&iter](auto&& elem) { 24 | *iter = FLUX_FWD(elem); 25 | ++iter; 26 | }); 27 | return iter; 28 | } 29 | 30 | public: 31 | template 32 | requires std::indirectly_writable> 33 | constexpr auto operator()(Seq&& seq, Iter iter) const -> Iter 34 | { 35 | constexpr bool can_memcpy = 36 | contiguous_sequence && 37 | sized_sequence && 38 | std::contiguous_iterator && 39 | std::is_trivially_copyable_v>; 40 | 41 | if constexpr (can_memcpy) { 42 | if (std::is_constant_evaluated()) { 43 | return impl(seq, iter); // LCOV_EXCL_LINE 44 | } else { 45 | auto size = flux::usize(seq); 46 | if (size == 0) { 47 | return iter; 48 | } 49 | FLUX_ASSERT(flux::data(seq) != nullptr); 50 | std::memmove(std::to_address(iter), flux::data(seq), 51 | size * sizeof(value_t)); 52 | return iter + num::checked_cast>(flux::size(seq)); 53 | } 54 | } else { 55 | return impl(seq, iter); 56 | } 57 | } 58 | }; 59 | 60 | } 61 | 62 | FLUX_EXPORT inline constexpr auto output_to = detail::output_to_fn{}; 63 | 64 | template 65 | template 66 | requires std::weakly_incrementable && 67 | std::indirectly_writable> 68 | constexpr auto inline_sequence_base::output_to(Iter iter) -> Iter 69 | { 70 | return flux::output_to(derived(), std::move(iter)); 71 | } 72 | 73 | } 74 | 75 | #endif // FLUX_ALGORITHM_OUTPUT_TO_HPP_INCLUDED 76 | -------------------------------------------------------------------------------- /include/flux/algorithm/search.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_SEARCH_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_SEARCH_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | struct search_fn { 16 | template 18 | requires std::predicate, element_t> 19 | constexpr auto operator()(Haystack&& h, Needle&& n, Cmp cmp = {}) const 20 | -> bounds_t 21 | { 22 | auto hfirst = flux::first(h); 23 | 24 | while(true) { 25 | auto cur1 = hfirst; 26 | auto cur2 = flux::first(n); 27 | 28 | while (true) { 29 | if (is_last(n, cur2)) { 30 | return {std::move(hfirst), std::move(cur1)}; 31 | } 32 | 33 | if (is_last(h, cur1)) { 34 | return {cur1, cur1}; 35 | } 36 | 37 | if (!std::invoke(cmp, read_at(h, cur1), read_at(n, cur2))) { 38 | break; 39 | } 40 | 41 | inc(h, cur1); 42 | inc(n, cur2); 43 | } 44 | 45 | inc(h, hfirst); 46 | } 47 | } 48 | 49 | }; 50 | 51 | } // namespace detail 52 | 53 | FLUX_EXPORT inline constexpr auto search = detail::search_fn{}; 54 | 55 | } // namespace flux 56 | 57 | #endif // FLUX_ALGORITHM_SEARCH_HPP_INCLUDED 58 | 59 | -------------------------------------------------------------------------------- /include/flux/algorithm/sort.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef FLUX_ALGORITHM_SORT_HPP_INCLUDED 3 | #define FLUX_ALGORITHM_SORT_HPP_INCLUDED 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace flux { 10 | 11 | namespace detail { 12 | 13 | struct sort_fn { 14 | template 15 | requires bounded_sequence && 16 | element_swappable_with && 17 | weak_ordering_for 18 | constexpr auto operator()(Seq&& seq, Cmp cmp = {}) const 19 | { 20 | auto wrapper = flux::unchecked(flux::from_fwd_ref(FLUX_FWD(seq))); 21 | detail::pdqsort(wrapper, cmp); 22 | } 23 | }; 24 | 25 | } // namespace detail 26 | 27 | FLUX_EXPORT inline constexpr auto sort = detail::sort_fn{}; 28 | 29 | template 30 | template 31 | requires random_access_sequence && 32 | bounded_sequence && 33 | detail::element_swappable_with && 34 | weak_ordering_for 35 | constexpr void inline_sequence_base::sort(Cmp cmp) 36 | { 37 | return flux::sort(derived(), std::ref(cmp)); 38 | } 39 | 40 | } // namespace flux 41 | 42 | #endif // FLUX_ALGORITHM_SORT_HPP_INCLUDED 43 | -------------------------------------------------------------------------------- /include/flux/algorithm/starts_with.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_STARTS_WITH_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_STARTS_WITH_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | struct starts_with_fn { 16 | template 17 | requires std::predicate, element_t> 18 | constexpr auto operator()(Haystack&& haystack, Needle&& needle, Cmp cmp = Cmp{}) const -> bool 19 | { 20 | if constexpr (sized_sequence && sized_sequence) { 21 | if (flux::size(haystack) < flux::size(needle)) { 22 | return false; 23 | } 24 | } 25 | 26 | auto h = flux::first(haystack); 27 | auto n = flux::first(needle); 28 | 29 | while (!flux::is_last(haystack, h) && !flux::is_last(needle, n)) { 30 | if (!std::invoke(cmp, flux::read_at(haystack, h), flux::read_at(needle, n))) { 31 | return false; 32 | } 33 | flux::inc(haystack, h); 34 | flux::inc(needle, n); 35 | } 36 | 37 | return flux::is_last(needle, n); 38 | } 39 | }; 40 | 41 | } // namespace detail 42 | 43 | FLUX_EXPORT inline constexpr auto starts_with = detail::starts_with_fn{}; 44 | 45 | template 46 | template 47 | requires std::predicate, element_t> 48 | constexpr auto inline_sequence_base::starts_with(Needle&& needle, Cmp cmp) -> bool 49 | { 50 | return flux::starts_with(derived(), FLUX_FWD(needle), std::move(cmp)); 51 | } 52 | 53 | 54 | } // namespace flux 55 | 56 | #endif // FLUX_ALGORITHM_STARTS_WITH_HPP_INCLUDED 57 | -------------------------------------------------------------------------------- /include/flux/algorithm/swap_elements.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_SWAP_ELEMENTS_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_SWAP_ELEMENTS_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | struct swap_elements_fn { 16 | template 17 | requires element_swappable_with 18 | constexpr void operator()(Seq1&& seq1, Seq2&& seq2) const 19 | { 20 | auto cur1 = flux::first(seq1); 21 | auto cur2 = flux::first(seq2); 22 | 23 | while (!flux::is_last(seq1, cur1) && !flux::is_last(seq2, cur2)) { 24 | flux::swap_with(seq1, cur1, seq2, cur2); 25 | flux::inc(seq1, cur1); 26 | flux::inc(seq2, cur2); 27 | } 28 | } 29 | }; 30 | 31 | } 32 | 33 | FLUX_EXPORT inline constexpr auto swap_elements = detail::swap_elements_fn{}; 34 | 35 | } 36 | 37 | #endif // FLUX_ALGORITHM_SWAP_ELEMENTS_HPP_INCLUDED 38 | -------------------------------------------------------------------------------- /include/flux/algorithm/write_to.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_ALGORITHM_WRITE_TO_HPP_INCLUDED 7 | #define FLUX_ALGORITHM_WRITE_TO_HPP_INCLUDED 8 | 9 | #include 10 | 11 | #include 12 | 13 | namespace flux { 14 | 15 | namespace detail { 16 | 17 | struct write_to_fn { 18 | 19 | template 20 | requires std::derived_from 21 | auto operator()(Seq&& seq, OStream& os) const 22 | -> std::ostream& 23 | { 24 | bool first = true; 25 | os << '['; 26 | 27 | flux::for_each(FLUX_FWD(seq), [&os, &first](auto&& elem) { 28 | if (first) { 29 | first = false; 30 | } else { 31 | os << ", "; 32 | } 33 | 34 | if constexpr (sequence>) { 35 | write_to_fn{}(FLUX_FWD(elem), os); 36 | } else { 37 | os << FLUX_FWD(elem); 38 | } 39 | }); 40 | 41 | os << ']'; 42 | return os; 43 | } 44 | }; 45 | 46 | } // namespace detail 47 | 48 | FLUX_EXPORT inline constexpr auto write_to = detail::write_to_fn{}; 49 | 50 | template 51 | auto inline_sequence_base::write_to(std::ostream& os) -> std::ostream& 52 | { 53 | return flux::write_to(derived(), os); 54 | } 55 | 56 | } // namespace flux 57 | 58 | #endif // FLUX_ALGORITHM_WRITE_TO_HPP_INCLUDED 59 | -------------------------------------------------------------------------------- /include/flux/core.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_CORE_HPP_INCLUDED 7 | #define FLUX_CORE_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #endif // FLUX_CORE_HPP_INCLUDED 22 | -------------------------------------------------------------------------------- /include/flux/core/operation_requirements.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_CORE_OPERATION_REQUIREMENTS_HPP_INCLUDED 7 | #define FLUX_CORE_OPERATION_REQUIREMENTS_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | FLUX_EXPORT 14 | template 15 | using fold_result_t = std::decay_t>>; 16 | 17 | namespace detail { 18 | 19 | template > 21 | concept foldable_ = 22 | std::invocable> && 23 | std::convertible_to && 24 | std::assignable_from>>; 25 | 26 | template 27 | struct repeated_invocable_helper { 28 | template 29 | using repeater = E; 30 | 31 | static inline constexpr bool value = [] (std::index_sequence) consteval { 32 | return std::regular_invocable...>; 33 | }(std::make_index_sequence{}); 34 | }; 35 | 36 | template 37 | concept repeated_invocable = repeated_invocable_helper::value; 38 | 39 | template 40 | concept flatten_with_compatible = 41 | std::common_reference_with, element_t> && 42 | std::common_reference_with, rvalue_element_t> && 43 | std::common_with, value_t>; 44 | 45 | } // namespace detail 46 | 47 | FLUX_EXPORT 48 | template 49 | concept foldable = 50 | sequence && 51 | std::invocable> && 52 | detail::foldable_; 53 | 54 | FLUX_EXPORT 55 | template 56 | concept weak_ordering_for = 57 | sequence && 58 | sequence && 59 | ordering_invocable, element_t, std::weak_ordering> && 60 | ordering_invocable&, element_t, std::weak_ordering> && 61 | ordering_invocable, value_t&, std::weak_ordering> && 62 | ordering_invocable&, value_t&, std::weak_ordering> && 63 | ordering_invocable, common_element_t, std::weak_ordering>; 64 | 65 | } // namespace flux 66 | 67 | #endif // FLUX_CORE_OPERATION_REQUIREMENTS_HPP_INCLUDED 68 | -------------------------------------------------------------------------------- /include/flux/core/utils.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_CORE_UTILS_HPP_INCLUDED 7 | #define FLUX_CORE_UTILS_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace flux { 16 | 17 | /* 18 | * Useful helpers 19 | */ 20 | FLUX_EXPORT 21 | template 22 | concept decays_to = std::same_as, To>; 23 | 24 | FLUX_EXPORT 25 | template 26 | concept same_decayed = std::same_as, 27 | std::remove_cvref_t>; 28 | 29 | namespace detail { 30 | 31 | struct copy_fn { 32 | template 33 | [[nodiscard]] 34 | constexpr auto operator()(T&& arg) const 35 | noexcept(std::is_nothrow_convertible_v>) 36 | -> std::decay_t 37 | { 38 | return FLUX_FWD(arg); 39 | } 40 | }; 41 | 42 | } // namespace detail 43 | 44 | FLUX_EXPORT inline constexpr auto copy = detail::copy_fn{}; 45 | 46 | namespace detail { 47 | 48 | template 49 | concept any_of = (std::same_as || ...); 50 | 51 | template 52 | concept compares_as = std::same_as, Cat>; 53 | 54 | template 55 | concept ordering_invocable_ = 56 | std::regular_invocable && 57 | compares_as>, Cat>; 58 | 59 | } // namespace detail 60 | 61 | FLUX_EXPORT 62 | template 63 | concept ordering_invocable = 64 | detail::ordering_invocable_ && 65 | detail::ordering_invocable_ && 66 | detail::ordering_invocable_ && 67 | detail::ordering_invocable_; 68 | 69 | } // namespace flux 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /include/flux/macros.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_MACROS_HPP_INCLUDED 7 | #define FLUX_MACROS_HPP_INCLUDED 8 | 9 | #include 10 | 11 | #define FLUX_VERSION_MAJOR 0 12 | #define FLUX_VERSION_MINOR 4 13 | #define FLUX_VERSION_PATCH 0 14 | #define FLUX_VERSION_DEVEL 1 // 0 => Release, 1 => development post Major.Minor.Patch 15 | 16 | #define FLUX_VERSION \ 17 | (FLUX_VERSION_MAJOR * 100'000 \ 18 | + FLUX_VERSION_MINOR * 1'000 \ 19 | + FLUX_VERSION_PATCH * 10 \ 20 | + FLUX_VERSION_DEVEL) 21 | 22 | #define FLUX_FWD(x) static_cast(x) 23 | 24 | #define FLUX_DECLVAL(...) ((static_cast<__VA_ARGS__(*)()noexcept>(nullptr))()) 25 | 26 | #if defined(__GNUC__) 27 | # define FLUX_ALWAYS_INLINE [[gnu::always_inline]] inline 28 | #elif defined(_MSC_VER) 29 | # define FLUX_ALWAYS_INLINE __forceinline 30 | #else 31 | # define FLUX_ALWAYS_INLINE inline 32 | #endif 33 | 34 | #define FLUX_NO_UNIQUE_ADDRESS [[no_unique_address]] 35 | 36 | #define FLUX_FOR(_flux_var_decl_, ...) \ 37 | if (auto&& _flux_seq_ = __VA_ARGS__; true) \ 38 | for (auto _flux_cur_ = ::flux::first(_flux_seq_); \ 39 | !::flux::is_last(_flux_seq_, _flux_cur_); \ 40 | ::flux::inc(_flux_seq_, _flux_cur_)) \ 41 | if (_flux_var_decl_ = ::flux::read_at(_flux_seq_, _flux_cur_); true) 42 | 43 | #define FLUX_ASSERT(cond) (::flux::assert_(cond, "assertion '" #cond "' failed")) 44 | 45 | #define FLUX_DEBUG_ASSERT(cond) (::flux::assert_(!::flux::config::enable_debug_asserts || (cond), "assertion '" #cond "' failed")); 46 | 47 | #ifdef FLUX_MODULE_INTERFACE 48 | #define FLUX_EXPORT export 49 | #else 50 | #define FLUX_EXPORT 51 | #endif 52 | 53 | #endif // FLUX_MACROS_HPP_INCLUDED 54 | -------------------------------------------------------------------------------- /include/flux/sequence.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_SEQUENCE_HPP_INCLUDED 7 | #define FLUX_SEQUENCE_HPP_INCLUDED 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #endif // FLUX_SEQUENCE_HPP_INCLUDED 23 | -------------------------------------------------------------------------------- /include/flux/sequence/bitset.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_SEQUENCE_BITSET_HPP_INCLUDED 7 | #define FLUX_SEQUENCE_BITSET_HPP_INCLUDED 8 | 9 | #include 10 | 11 | #include 12 | 13 | namespace flux { 14 | 15 | template 16 | struct sequence_traits> : default_sequence_traits { 17 | 18 | using value_type = bool; 19 | 20 | using self_t = std::bitset; 21 | 22 | static constexpr auto first(self_t const&) -> std::size_t { return 0; } 23 | 24 | static constexpr auto is_last(self_t const&, std::size_t idx) { return idx == N; } 25 | 26 | static constexpr auto read_at(self_t& self, std::size_t idx) 27 | -> typename std::bitset::reference 28 | { 29 | return self[idx]; 30 | } 31 | 32 | static constexpr auto read_at(self_t const& self, std::size_t idx) -> bool 33 | { 34 | return self[idx]; 35 | } 36 | 37 | static constexpr auto move_at(self_t const& self, std::size_t idx) -> bool 38 | { 39 | return self[idx]; 40 | } 41 | 42 | static constexpr auto inc(self_t const&, std::size_t& idx) -> std::size_t& 43 | { 44 | return ++idx; 45 | } 46 | 47 | static constexpr auto dec(self_t const&, std::size_t& idx) -> std::size_t& 48 | { 49 | return --idx; 50 | } 51 | 52 | static constexpr auto inc(self_t const&, std::size_t& idx, std::ptrdiff_t off) 53 | -> std::size_t& 54 | { 55 | return idx += static_cast(off); 56 | } 57 | 58 | static constexpr auto distance(self_t const&, std::size_t from, std::size_t to) 59 | -> std::ptrdiff_t 60 | { 61 | return static_cast(to) - static_cast(from); 62 | } 63 | 64 | static constexpr auto last(self_t const&) -> std::size_t { return N; } 65 | 66 | static constexpr auto size(self_t const&) -> std::ptrdiff_t { return N; } 67 | 68 | }; 69 | 70 | 71 | } // namespace flux 72 | 73 | #endif // FLUX_SEQUENCE_BITSET_HPP_INCLUDED 74 | -------------------------------------------------------------------------------- /include/flux/sequence/empty.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_SEQUENCE_EMPTY_HPP_INCLUDED 7 | #define FLUX_SEQUENCE_EMPTY_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct empty_sequence : inline_sequence_base> { 17 | struct flux_sequence_traits : default_sequence_traits { 18 | private: 19 | struct cursor_type { 20 | friend auto operator==(cursor_type, cursor_type) -> bool = default; 21 | friend auto operator<=>(cursor_type, cursor_type) = default; 22 | }; 23 | 24 | public: 25 | static constexpr auto first(empty_sequence) -> cursor_type { return {}; } 26 | static constexpr auto last(empty_sequence) -> cursor_type { return {}; } 27 | static constexpr auto is_last(empty_sequence, cursor_type) -> bool { return true; } 28 | 29 | static constexpr auto inc(empty_sequence, cursor_type& cur, distance_t = 0) 30 | -> cursor_type& 31 | { 32 | return cur; 33 | } 34 | 35 | static constexpr auto dec(empty_sequence, cursor_type& cur) -> cursor_type& 36 | { 37 | return cur; 38 | } 39 | 40 | static constexpr auto distance(empty_sequence, cursor_type, cursor_type) 41 | -> std::ptrdiff_t 42 | { 43 | return 0; 44 | } 45 | 46 | static constexpr auto size(empty_sequence) -> std::ptrdiff_t { return 0; } 47 | static constexpr auto data(empty_sequence) -> std::add_pointer_t requires std::is_object_v { return nullptr; } 48 | 49 | [[noreturn]] 50 | static constexpr auto read_at(empty_sequence, cursor_type) -> T& 51 | { 52 | runtime_error("Attempted read of flux::empty"); 53 | } 54 | }; 55 | }; 56 | 57 | } // namespace detail 58 | 59 | FLUX_EXPORT 60 | template 61 | inline constexpr auto empty = detail::empty_sequence{}; 62 | 63 | } // namespace flux 64 | 65 | #endif // FLUX_SEQUENCE_EMPTY_HPP_INCLUDED 66 | -------------------------------------------------------------------------------- /include/flux/sequence/istream.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_SEQUENCE_FROM_ISTREAM_HPP_INCLUDED 7 | #define FLUX_SEQUENCE_FROM_ISTREAM_HPP_INCLUDED 8 | 9 | #include 10 | 11 | #include 12 | 13 | namespace flux { 14 | 15 | namespace detail { 16 | 17 | template 18 | requires std::default_initializable 19 | class istream_adaptor : public inline_sequence_base> { 20 | using istream_type = std::basic_istream; 21 | istream_type* is_ = nullptr; 22 | T val_ = T(); 23 | 24 | friend struct sequence_traits; 25 | 26 | public: 27 | explicit istream_adaptor(istream_type& is) 28 | : is_(std::addressof(is)) 29 | {} 30 | 31 | }; 32 | 33 | template 34 | struct from_istream_fn { 35 | 36 | template 37 | [[nodiscard]] 38 | auto operator()(std::basic_istream& is) const 39 | { 40 | return istream_adaptor(is); 41 | } 42 | 43 | }; 44 | 45 | } // namespace detail 46 | 47 | template 48 | struct sequence_traits> : default_sequence_traits 49 | { 50 | private: 51 | struct cursor_type { 52 | cursor_type(cursor_type&&) = default; 53 | cursor_type& operator=(cursor_type&&) = default; 54 | private: 55 | friend struct sequence_traits; 56 | explicit cursor_type() = default; 57 | }; 58 | 59 | using self_t = detail::istream_adaptor; 60 | 61 | public: 62 | static auto first(self_t& self) -> cursor_type 63 | { 64 | cursor_type cur{}; 65 | inc(self, cur); 66 | return cur; 67 | } 68 | 69 | static auto is_last(self_t& self, cursor_type const&) -> bool 70 | { 71 | return !(self.is_ && static_cast(*self.is_)); 72 | } 73 | 74 | static auto read_at(self_t& self, cursor_type const&) -> T const& 75 | { 76 | return self.val_; 77 | } 78 | 79 | static auto inc(self_t& self, cursor_type& cur) -> cursor_type& 80 | { 81 | if (!(self.is_ && (*self.is_ >> self.val_))) { 82 | self.is_ = nullptr; 83 | } 84 | return cur; 85 | } 86 | }; 87 | 88 | FLUX_EXPORT 89 | template 90 | inline constexpr auto from_istream = detail::from_istream_fn{}; 91 | 92 | } // namespace flux 93 | 94 | #endif // FLUX_SEQUENCE_ISTREAM_HPP_INCLUDED 95 | -------------------------------------------------------------------------------- /include/flux/sequence/istreambuf.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_SEQUENCE_ISTREAMBUF_HPP_INCLUDED 7 | #define FLUX_SEQUENCE_ISTREAMBUF_HPP_INCLUDED 8 | 9 | #include 10 | 11 | #include 12 | 13 | namespace flux { 14 | 15 | namespace detail { 16 | 17 | template 18 | void derives_from_streambuf_test(std::basic_streambuf&); 19 | 20 | template 21 | concept derives_from_streambuf = requires (T& t) { derives_from_streambuf_test(t); }; 22 | 23 | struct from_istreambuf_fn { 24 | template 25 | [[nodiscard]] 26 | auto operator()(std::basic_streambuf* streambuf) const -> sequence auto 27 | { 28 | FLUX_ASSERT(streambuf != nullptr); 29 | return flux::mut_ref(*streambuf); 30 | } 31 | 32 | template 33 | [[nodiscard]] 34 | auto operator()(std::basic_istream& istream) const -> sequence auto 35 | { 36 | return flux::mut_ref(*istream.rdbuf()); 37 | } 38 | }; 39 | 40 | } // namespace detail 41 | 42 | template 43 | struct sequence_traits : default_sequence_traits 44 | { 45 | private: 46 | struct cursor_type { 47 | cursor_type(cursor_type&&) = default; 48 | cursor_type& operator=(cursor_type&&) = default; 49 | private: 50 | friend struct sequence_traits; 51 | cursor_type() = default; 52 | }; 53 | 54 | using traits_type = typename Streambuf::traits_type; 55 | using char_type = typename Streambuf::char_type; 56 | 57 | public: 58 | static auto first(Streambuf&) -> cursor_type { return {}; } 59 | 60 | static auto is_last(Streambuf& self, cursor_type const&) -> bool 61 | { 62 | return self.sgetc() == traits_type::eof(); 63 | } 64 | 65 | static auto inc(Streambuf& self, cursor_type& cur) -> cursor_type& 66 | { 67 | self.sbumpc(); 68 | return cur; 69 | } 70 | 71 | static auto read_at(Streambuf& self, cursor_type const&) -> char_type 72 | { 73 | return traits_type::to_char_type(self.sgetc()); 74 | } 75 | }; 76 | 77 | FLUX_EXPORT 78 | inline constexpr auto from_istreambuf = detail::from_istreambuf_fn{}; 79 | 80 | } // namespace flux 81 | 82 | #endif // FLUX_SEQUENCE_ISTREAMBUF_HPP_INCLUDED 83 | -------------------------------------------------------------------------------- /include/flux/sequence/unfold.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef FLUX_SEQUENCE_UNFOLD_HPP_INCLUDED 7 | #define FLUX_SEQUENCE_UNFOLD_HPP_INCLUDED 8 | 9 | #include 10 | 11 | namespace flux { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct unfold_sequence : inline_sequence_base> { 17 | private: 18 | R state_; 19 | Func func_; 20 | 21 | public: 22 | template 23 | requires std::constructible_from 24 | constexpr explicit unfold_sequence(Func&& func, T&& seed) 25 | : state_(FLUX_FWD(seed)), 26 | func_(std::move(func)) 27 | {} 28 | 29 | struct flux_sequence_traits : default_sequence_traits { 30 | private: 31 | struct cursor_type { 32 | friend struct flux_sequence_traits; 33 | cursor_type(cursor_type&&) = default; 34 | cursor_type& operator=(cursor_type&&) = default; 35 | private: 36 | cursor_type() = default; 37 | }; 38 | 39 | using self_t = unfold_sequence; 40 | 41 | public: 42 | static constexpr bool is_infinite = true; 43 | 44 | static constexpr auto first(self_t&) -> cursor_type { return {}; } 45 | 46 | static constexpr auto is_last(self_t&, cursor_type const&) -> bool { return false; } 47 | 48 | static constexpr auto inc(self_t& self, cursor_type&) -> void 49 | { 50 | self.state_ = std::invoke(self.func_, std::move(self.state_)); 51 | } 52 | 53 | static constexpr auto read_at(self_t& self, cursor_type const&) -> R const& 54 | { 55 | return self.state_; 56 | } 57 | 58 | static constexpr auto for_each_while(self_t& self, auto&& pred) -> cursor_type 59 | { 60 | while (true) { 61 | if (!std::invoke(pred, self.state_)) { 62 | break; 63 | } 64 | self.state_ = std::invoke(self.func_, std::move(self.state_)); 65 | } 66 | 67 | return {}; 68 | } 69 | }; 70 | }; 71 | 72 | struct unfold_fn { 73 | template >> 75 | requires std::constructible_from && 76 | std::invocable && 77 | std::assignable_from> 78 | [[nodiscard]] 79 | constexpr auto operator()(Func func, Seed&& seed) const -> sequence auto 80 | { 81 | return unfold_sequence(std::move(func), FLUX_FWD(seed)); 82 | } 83 | }; 84 | 85 | } // namespace detail 86 | 87 | FLUX_EXPORT inline constexpr auto unfold = detail::unfold_fn{}; 88 | 89 | } // namespace flux 90 | 91 | #endif // FLUX_SEQUENCE_UNFOLD_INCLUDED 92 | -------------------------------------------------------------------------------- /module/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(flux-mod) 3 | add_library(flux::module ALIAS flux-mod) 4 | 5 | target_sources(flux-mod PUBLIC 6 | FILE_SET CXX_MODULES 7 | BASE_DIRS ${PROJECT_SOURCE_DIR}/module 8 | FILES flux.cpp 9 | ) 10 | 11 | target_sources(flux-mod PUBLIC 12 | FILE_SET HEADERS 13 | BASE_DIRS ${PROJECT_SOURCE_DIR}/include 14 | FILES ${PROJECT_SOURCE_DIR}/include/flux/macros.hpp 15 | ) 16 | 17 | target_link_libraries(flux-mod PRIVATE flux) 18 | target_compile_features(flux-mod PUBLIC $,cxx_std_23,cxx_std_20>) 19 | set_target_properties(flux-mod PROPERTIES CXX_EXTENSIONS Off) 20 | -------------------------------------------------------------------------------- /module/flux.cpp: -------------------------------------------------------------------------------- 1 | 2 | module; 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | export module flux; 34 | 35 | #define FLUX_MODULE_INTERFACE 36 | 37 | // Silence Clang and MSVC warnings about #include inside a module's purview 38 | 39 | #ifdef _MSC_VER 40 | #pragma warning(push) 41 | #pragma warning(disable: 5244) 42 | #endif 43 | 44 | #ifdef __clang__ 45 | #pragma clang diagnostic push 46 | #if __has_warning("-Winclude-angled-in-module-purview") 47 | #pragma clang diagnostic ignored "-Winclude-angled-in-module-purview" 48 | #endif 49 | #endif 50 | 51 | #include 52 | 53 | #ifdef __clang__ 54 | #pragma clang diagnostic pop 55 | #endif 56 | 57 | #ifdef _MSC_VER 58 | #pragma warning(pop) 59 | #endif 60 | -------------------------------------------------------------------------------- /test/num/test_concepts.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include "../test_utils.hpp" 7 | 8 | /* 9 | * Built-in signed integers 10 | */ 11 | 12 | static_assert(flux::num::integral); 13 | static_assert(flux::num::integral); 14 | static_assert(flux::num::integral); 15 | static_assert(flux::num::integral); 16 | static_assert(flux::num::integral); 17 | 18 | static_assert(flux::num::signed_integral); 19 | static_assert(flux::num::signed_integral); 20 | static_assert(flux::num::signed_integral); 21 | static_assert(flux::num::signed_integral); 22 | static_assert(flux::num::signed_integral); 23 | 24 | static_assert(not flux::num::unsigned_integral); 25 | static_assert(not flux::num::unsigned_integral); 26 | static_assert(not flux::num::unsigned_integral); 27 | static_assert(not flux::num::unsigned_integral); 28 | static_assert(not flux::num::unsigned_integral); 29 | 30 | /* 31 | * Built-in unsigned integers 32 | */ 33 | 34 | static_assert(flux::num::integral); 35 | static_assert(flux::num::integral); 36 | static_assert(flux::num::integral); 37 | static_assert(flux::num::integral); 38 | static_assert(flux::num::integral); 39 | 40 | static_assert(not flux::num::signed_integral); 41 | static_assert(not flux::num::signed_integral); 42 | static_assert(not flux::num::signed_integral); 43 | static_assert(not flux::num::signed_integral); 44 | static_assert(not flux::num::signed_integral); 45 | 46 | static_assert(flux::num::unsigned_integral); 47 | static_assert(flux::num::unsigned_integral); 48 | static_assert(flux::num::unsigned_integral); 49 | static_assert(flux::num::unsigned_integral); 50 | static_assert(flux::num::unsigned_integral); 51 | 52 | /* 53 | * Types which should not be considered integers 54 | */ 55 | static_assert(not flux::num::integral); 56 | static_assert(not flux::num::integral); 57 | static_assert(not flux::num::integral); 58 | static_assert(not flux::num::integral); 59 | static_assert(not flux::num::integral); 60 | static_assert(not flux::num::integral); 61 | static_assert(not flux::num::integral); 62 | static_assert(not flux::num::integral); 63 | static_assert(not flux::num::integral); 64 | static_assert(not flux::num::integral); 65 | 66 | -------------------------------------------------------------------------------- /test/test_all_any_none.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef USE_MODULES 13 | import flux; 14 | #else 15 | #include 16 | #endif 17 | 18 | namespace { 19 | 20 | constexpr auto gt_zero = [](auto i) { return i > 0; }; 21 | 22 | template 23 | constexpr bool test_all(std::initializer_list ilist) 24 | { 25 | return flux::all(ilist, gt_zero) == std::all_of(ilist.begin(), ilist.end(), gt_zero); 26 | } 27 | static_assert(test_all({})); 28 | static_assert(test_all({1, 2, 3, 4, 5})); 29 | static_assert(test_all({1.0, 2.0, -3.0, 4.0})); 30 | 31 | } 32 | 33 | TEST_CASE("all with vector") 34 | { 35 | std::vector vec{1, 2, 3, 4, 5}; 36 | 37 | static_assert(flux::contiguous_sequence); 38 | 39 | bool req = flux::all(vec, gt_zero); 40 | 41 | REQUIRE(req); 42 | } 43 | -------------------------------------------------------------------------------- /test/test_apply.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "test_utils.hpp" 6 | 7 | // We don't have an in-place rotate yet, so let's borrow the STL's 8 | auto rotate_by = [](Seq&& seq, flux::distance_t places) 9 | -> Seq&& 10 | { 11 | auto const sz = flux::size(seq); 12 | 13 | while (places < 0) { 14 | places += sz; 15 | } 16 | while (places >= sz) { 17 | places -= sz; 18 | } 19 | 20 | std::ranges::rotate(seq, seq.begin() + places); 21 | 22 | return FLUX_FWD(seq); 23 | }; 24 | 25 | 26 | constexpr bool test_apply() 27 | { 28 | // Checking lvalue sequence 29 | { 30 | auto seq = flux::from(std::array{1, 2, 3, 4, 5}); 31 | auto& r = seq._(rotate_by, -1); 32 | STATIC_CHECK(&r == &seq); 33 | STATIC_CHECK(check_equal(seq, {5, 1, 2, 3, 4})); 34 | } 35 | 36 | // Checking rvalue sequence 37 | { 38 | auto seq = flux::from(std::array{1, 2, 3, 4, 5})._(rotate_by, -1).take(3); 39 | STATIC_CHECK(check_equal(seq, {5, 1, 2})); 40 | } 41 | 42 | // Checking const [l|r]value 43 | { 44 | auto sum = [](auto& seq) { 45 | int s = 0; 46 | FLUX_FOR(int i, seq) { s += i; } 47 | return s; 48 | }; 49 | 50 | auto const seq = flux::from(std::array{1, 2, 3, 4, 5}); 51 | STATIC_CHECK(seq._(sum) == 15); 52 | 53 | STATIC_CHECK(std::move(seq)._(sum) == 15); 54 | } 55 | 56 | return true; 57 | } 58 | static_assert(test_apply()); 59 | 60 | TEST_CASE("apply") 61 | { 62 | REQUIRE(test_apply()); 63 | } -------------------------------------------------------------------------------- /test/test_cache_last.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "test_utils.hpp" 5 | 6 | namespace { 7 | 8 | constexpr bool test_cache_last() 9 | { 10 | // cache_last turns an unbounded sequence into a bounded one 11 | { 12 | std::array arr{1, 2, 3, 4, 5}; 13 | auto seq = flux::take_while(flux::ref(arr), [](int){ return true; }); 14 | 15 | static_assert(not flux::bounded_sequence); 16 | 17 | auto cached = flux::cache_last(std::move(seq)); 18 | 19 | using C = decltype(cached); 20 | 21 | static_assert(flux::sequence); 22 | static_assert(flux::random_access_sequence); 23 | static_assert(flux::bounded_sequence); 24 | 25 | STATIC_CHECK(cached.count() == 5); 26 | 27 | static_assert(std::ranges::common_range); 28 | } 29 | 30 | // For a bounded sequence, cache_last is equivalent to flux::from 31 | { 32 | std::array arr{1, 2, 3, 4, 5}; 33 | 34 | auto seq = flux::ref(arr); 35 | auto cached = flux::cache_last(flux::ref(arr)); 36 | 37 | STATIC_CHECK(&seq.base() == &cached.base()); 38 | STATIC_CHECK(&cached.base() == &arr); 39 | } 40 | 41 | // Example from docs 42 | { 43 | std::array arr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 44 | 45 | flux::mut_ref(arr) 46 | .take_while([](int i) { return i <= 5; }) 47 | .cache_last() 48 | .inplace_reverse(); 49 | 50 | STATIC_CHECK(check_equal(arr, {5, 4, 3, 2, 1, 6, 7, 8, 9, 10})); 51 | } 52 | 53 | return true; 54 | } 55 | static_assert(test_cache_last()); 56 | 57 | } 58 | 59 | TEST_CASE("cache_last") 60 | { 61 | bool result = test_cache_last(); 62 | REQUIRE(result); 63 | } 64 | -------------------------------------------------------------------------------- /test/test_contains.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "test_utils.hpp" 9 | 10 | namespace { 11 | 12 | struct Test { 13 | constexpr Test(int i) : i(i) {} 14 | 15 | constexpr int get() { return i; } 16 | 17 | int i; 18 | }; 19 | 20 | constexpr bool test_contains() 21 | { 22 | // Basic contains 23 | { 24 | int arr[] = {0, 1, 2, 3, 4}; 25 | 26 | STATIC_CHECK(flux::contains(arr, 3)); 27 | STATIC_CHECK(not flux::contains(arr, 99)); 28 | 29 | std::string_view sv = "Hello World"; 30 | 31 | auto seq = flux::from(sv); 32 | 33 | STATIC_CHECK(seq.contains(' ')); 34 | STATIC_CHECK(not seq.contains('Z')); 35 | } 36 | 37 | return true; 38 | } 39 | static_assert(test_contains()); 40 | 41 | } 42 | 43 | TEST_CASE("contains") 44 | { 45 | bool result = test_contains(); 46 | REQUIRE(result); 47 | } -------------------------------------------------------------------------------- /test/test_count.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include "test_utils.hpp" 7 | 8 | namespace { 9 | 10 | struct S { 11 | 12 | constexpr S(int i) : i(i) {} 13 | 14 | constexpr int get() const { return i; } 15 | 16 | int i; 17 | }; 18 | 19 | constexpr bool test_count() 20 | { 21 | { 22 | int arr[] = {1, 2, 3, 4, 5}; 23 | STATIC_CHECK(flux::count(arr) == 5); 24 | 25 | auto seq = flux::take_while(flux::ref(arr), [](int) { return true; }); 26 | static_assert(not flux::sized_sequence); 27 | 28 | STATIC_CHECK(flux::count(seq) == 5); 29 | } 30 | 31 | { 32 | int arr[] = {1, 2, 3, 4, 5}; 33 | STATIC_CHECK(flux::ref(arr).count() == 5); 34 | 35 | auto seq = flux::take_while(flux::ref(arr), [](int) { return true; }); 36 | static_assert(not flux::sized_sequence); 37 | 38 | STATIC_CHECK(seq.count() == 5); 39 | } 40 | 41 | { 42 | int arr[] = {1, 2, 2, 2, 3, 4, 5}; 43 | STATIC_CHECK(flux::count_eq(arr, 2) == 3); 44 | STATIC_CHECK(flux::count_eq(arr, 99) == 0); 45 | 46 | auto seq = flux::ref(arr); 47 | STATIC_CHECK(seq.count_eq(2) == 3); 48 | STATIC_CHECK(seq.count_eq(99) == 0); 49 | } 50 | 51 | return true; 52 | } 53 | static_assert(test_count()); 54 | 55 | } 56 | 57 | TEST_CASE("count") 58 | { 59 | bool result = test_count(); 60 | REQUIRE(result); 61 | } -------------------------------------------------------------------------------- /test/test_count_if.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include "test_utils.hpp" 7 | 8 | 9 | namespace { 10 | 11 | struct S { 12 | 13 | constexpr S(int i) : i(i) {} 14 | 15 | constexpr int get() const { return i; } 16 | 17 | int i; 18 | }; 19 | 20 | constexpr auto is_even = [](int i) { return i % 2 == 0; }; 21 | 22 | constexpr bool test_count_if() 23 | { 24 | { 25 | int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 26 | 27 | STATIC_CHECK(flux::count_if(arr, is_even) == 5); 28 | 29 | STATIC_CHECK(flux::ref(arr).count_if(is_even) == 5); 30 | } 31 | 32 | { 33 | S arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 34 | 35 | STATIC_CHECK(flux::count_if(arr, flux::proj(is_even, &S::i))); 36 | 37 | STATIC_CHECK(flux::ref(arr).count_if(flux::proj(is_even, &S::i))); 38 | } 39 | 40 | return true; 41 | } 42 | static_assert(test_count_if()); 43 | 44 | } 45 | 46 | TEST_CASE("count_if") 47 | { 48 | bool result = test_count_if(); 49 | REQUIRE(result); 50 | } -------------------------------------------------------------------------------- /test/test_drop_while.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "test_utils.hpp" 9 | 10 | namespace { 11 | 12 | constexpr bool test_drop_while() 13 | { 14 | { 15 | int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 16 | 17 | auto seq = flux::drop_while(flux::ref(arr), [](int i) { return i < 5; }); 18 | 19 | using S = decltype(seq); 20 | 21 | static_assert(flux::contiguous_sequence); 22 | static_assert(flux::bounded_sequence); 23 | static_assert(flux::sized_sequence); // because bounded + RA 24 | 25 | static_assert(flux::contiguous_sequence); 26 | static_assert(flux::bounded_sequence); 27 | static_assert(flux::sized_sequence); 28 | 29 | STATIC_CHECK(seq.size() == 5); 30 | STATIC_CHECK(seq.data() == arr + 5); 31 | STATIC_CHECK(check_equal(seq, {5, 6, 7, 8, 9})); 32 | 33 | auto const& c_seq = seq; 34 | STATIC_CHECK(c_seq.size() == 5); 35 | STATIC_CHECK(c_seq.data() == arr + 5); 36 | STATIC_CHECK(check_equal(seq, {5, 6, 7, 8, 9})); 37 | } 38 | 39 | // Single-pass sequences are okay 40 | { 41 | int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 42 | 43 | auto seq = single_pass_only(flux::ref(arr)).drop_while([](int i) { return i < 5; }); 44 | 45 | using S = decltype(seq); 46 | 47 | static_assert(not flux::multipass_sequence); 48 | 49 | STATIC_CHECK(check_equal(seq, {5, 6, 7, 8, 9})); 50 | } 51 | 52 | // We don't filter longer than we should 53 | { 54 | int arr[] = {2, 2, 2, 3, 4, 5, 6, 7, 8, 9}; 55 | 56 | auto seq = flux::drop_while(flux::ref(arr), [](int i) { return i % 2 == 0; }); 57 | 58 | STATIC_CHECK(check_equal(seq, {3, 4, 5, 6, 7, 8, 9})); 59 | } 60 | 61 | // We can drop everything 62 | { 63 | auto yes = [](auto) { return true; }; 64 | 65 | auto seq = flux::drop_while(std::array{1, 2, 3, 4, 5, 6, 7, 8, 9}, yes); 66 | 67 | STATIC_CHECK(seq.is_empty()); 68 | } 69 | 70 | // We can drop nothing 71 | { 72 | auto no = [](auto) { return false; }; 73 | 74 | std::array arr{1, 2, 3, 4, 5, 6, 7, 8, 9}; 75 | 76 | STATIC_CHECK(check_equal(flux::drop_while(flux::ref(arr), no), arr)); 77 | } 78 | 79 | return true; 80 | } 81 | static_assert(test_drop_while()); 82 | 83 | } 84 | 85 | TEST_CASE("drop_while") 86 | { 87 | bool result = test_drop_while(); 88 | REQUIRE(result); 89 | } -------------------------------------------------------------------------------- /test/test_empty.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include 9 | 10 | #ifdef USE_MODULES 11 | import flux; 12 | #else 13 | #include 14 | #endif 15 | 16 | namespace { 17 | 18 | template 19 | consteval auto empty_test() { 20 | auto e = flux::empty; 21 | auto f = flux::empty; 22 | 23 | static_assert(flux::sized_sequence); 24 | static_assert(flux::bounded_sequence); 25 | static_assert(std::same_as, T&>); 26 | 27 | static_assert(e.first() == f.first()); 28 | static_assert(!(e.first() < f.first())); 29 | static_assert(e.first() == e.last()); 30 | static_assert(e.size() == 0); 31 | static_assert(e.distance(e.first(), e.last()) == 0); 32 | if constexpr(std::is_object_v) { 33 | static_assert(flux::contiguous_sequence); 34 | static_assert(e.data() == nullptr); 35 | } 36 | static_assert(flux::is_empty(e)); 37 | 38 | return true; 39 | } 40 | 41 | static_assert(empty_test()); 42 | static_assert(empty_test()); 43 | static_assert(empty_test()); 44 | static_assert(empty_test()); 45 | 46 | } 47 | 48 | TEST_CASE("empty") 49 | { 50 | auto e = flux::empty; 51 | auto f = flux::empty; 52 | 53 | // Make sure the assertion fires 54 | REQUIRE_THROWS_AS(e[e.first()], flux::unrecoverable_error); 55 | 56 | REQUIRE(e.first() == f.first()); 57 | REQUIRE(!(e.first() < f.first())); 58 | REQUIRE(e.first() == e.last()); 59 | REQUIRE(e.next(e.first()) == e.last()); 60 | REQUIRE(e.prev(e.last()) == e.first()); 61 | REQUIRE(e.size() == 0); 62 | REQUIRE(e.distance(e.first(), e.last()) == 0); 63 | REQUIRE(e.data() == nullptr); 64 | REQUIRE(e.is_empty()); 65 | REQUIRE(flux::is_empty(e)); 66 | } 67 | -------------------------------------------------------------------------------- /test/test_equal.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "test_utils.hpp" 5 | 6 | namespace { 7 | 8 | struct S { 9 | int i; 10 | }; 11 | 12 | class T { 13 | int i; 14 | public: 15 | constexpr T(int i) : i(i) {} 16 | constexpr int get() const { return i; } 17 | }; 18 | 19 | constexpr bool test_equal() 20 | { 21 | // Basic equal 22 | { 23 | int arr1[] = {1, 2, 3, 4, 5}; 24 | int arr2[] = {1, 2, 3, 4, 5}; 25 | 26 | STATIC_CHECK(flux::equal(arr1, arr2)); 27 | } 28 | 29 | // Basic equal, same size but different elements 30 | { 31 | int arr1[] = {1, 2, 3, 4, 5}; 32 | int arr2[] = {1, 2, 99, 4, 5}; 33 | 34 | STATIC_CHECK(not flux::equal(arr1, arr2)); 35 | } 36 | 37 | // Different but comparable element types 38 | { 39 | int arr1[] = {1, 2, 3, 4, 5}; 40 | float arr2[] = {1.f, 2.f, 3.f, 4.f, 5.f}; 41 | 42 | STATIC_CHECK(flux::equal(arr1, arr2)); 43 | } 44 | 45 | // Differing lengths, both sized sequences 46 | { 47 | int arr1[] = {1, 2, 3, 4, 5}; 48 | int arr2[] = {1}; 49 | 50 | STATIC_CHECK(not flux::equal(arr1, arr2)); 51 | STATIC_CHECK(not flux::equal(arr2, arr1)); 52 | } 53 | 54 | // Differing lengths, not sized 55 | { 56 | auto yes = [](int) { return true; }; 57 | auto seq1 = flux::take_while(std::array{1, 2, 3, 4, 5}, yes); 58 | auto seq2 = flux::take_while(std::array{1}, yes); 59 | 60 | static_assert(not flux::sized_sequence); 61 | 62 | STATIC_CHECK(not flux::equal(seq1, seq2)); 63 | STATIC_CHECK(not flux::equal(seq2, seq1)); 64 | } 65 | 66 | // Custom comparator 67 | { 68 | S arr1[] = {{1}, {2}, {3}, {4}, {5}}; 69 | T arr2[] = {1, 2, 3, 4, 5}; 70 | 71 | STATIC_CHECK(flux::equal(arr1, arr2, [](S const& s, T const& t) { 72 | return s.i ==t.get(); 73 | })); 74 | } 75 | 76 | // Test with projections 77 | { 78 | S arr1[] = {{1}, {2}, {3}, {4}, {5}}; 79 | T arr2[] = {1, 2, 3, 4, 5}; 80 | 81 | STATIC_CHECK(flux::equal(arr1, arr2, flux::proj2(std::equal_to<>{}, &S::i, &T::get))); 82 | } 83 | 84 | // Two empty sequences compare equal if their element types are comparable 85 | { 86 | std::array seq1{}; 87 | auto seq2 = flux::take_while(flux::ref(seq1), [](int) { return true; }); // not sized 88 | 89 | STATIC_CHECK(flux::equal(seq1, seq2)); 90 | 91 | static_assert(flux::equal(flux::empty, flux::empty)); 92 | } 93 | 94 | { 95 | std::array arr; 96 | STATIC_CHECK(flux::equal(arr, arr) == true); 97 | } 98 | 99 | return true; 100 | } 101 | static_assert(test_equal()); 102 | 103 | } 104 | 105 | TEST_CASE("equal") 106 | { 107 | bool result = test_equal(); 108 | REQUIRE(result); 109 | } -------------------------------------------------------------------------------- /test/test_fill.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | 9 | #include "test_utils.hpp" 10 | 11 | namespace { 12 | 13 | constexpr bool test_fill() 14 | { 15 | // Basic fill() 16 | { 17 | std::array arr{}; 18 | 19 | flux::fill(arr, 1); 20 | 21 | STATIC_CHECK(check_equal(arr, {1, 1, 1, 1, 1})); 22 | } 23 | 24 | // fill and adapted sequence 25 | { 26 | std::array arr{}; 27 | 28 | flux::take(flux::mut_ref(arr), 3).fill(1); 29 | 30 | STATIC_CHECK(check_equal(arr, {1, 1, 1, 0, 0})); 31 | } 32 | 33 | // single-pass sequences can be filled 34 | { 35 | std::array arr{}; 36 | 37 | single_pass_only(flux::mut_ref(arr)).fill(1); 38 | 39 | STATIC_CHECK(check_equal(arr, {1, 1, 1, 1, 1})); 40 | } 41 | 42 | // empty sequences can be "filled" 43 | { 44 | auto e = flux::empty; 45 | flux::fill(e, 99); 46 | } 47 | 48 | // single sequences can be filled 49 | { 50 | auto s = flux::single(0); 51 | 52 | flux::fill(s, short{1}); 53 | 54 | STATIC_CHECK(s.value() == 1); 55 | } 56 | 57 | // string fill with char 58 | { 59 | std::array arr{}; 60 | std::uint8_t c = 5; 61 | flux::fill(arr, c); 62 | STATIC_CHECK(check_equal(arr, {5, 5, 5, 5, 5})); 63 | } 64 | 65 | // fill empty sequence with char 66 | { 67 | std::array arr{}; 68 | std::uint8_t c = 5; 69 | flux::fill(arr, c); 70 | } 71 | 72 | return true; 73 | } 74 | 75 | static_assert(test_fill()); 76 | 77 | } 78 | 79 | TEST_CASE("fill") 80 | { 81 | bool result = test_fill(); 82 | REQUIRE(result); 83 | } -------------------------------------------------------------------------------- /test/test_find.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef USE_MODULES 15 | import flux; 16 | #else 17 | #include 18 | #include 19 | #endif 20 | 21 | namespace { 22 | 23 | struct S { 24 | int i_; 25 | }; 26 | 27 | 28 | using find_fn = decltype(flux::find); 29 | 30 | static_assert(std::invocable); 31 | 32 | // Incompatible value type 33 | static_assert(not std::invocable); 34 | 35 | // Not equality comparable 36 | static_assert(not std::invocable); 37 | 38 | constexpr bool test_find() 39 | { 40 | { 41 | int const ints[] = {0, 1, 2, 3, 4, 5}; 42 | 43 | auto cur = flux::find(ints, 3); 44 | if (cur != 3) { 45 | return false; 46 | } 47 | 48 | cur = flux::find(ints, 99); 49 | if (!flux::is_last(ints, cur)) { 50 | return false; 51 | } 52 | 53 | auto lens = flux::ref(ints); 54 | 55 | cur = lens.find(3); 56 | if (cur != 3) { 57 | return false; 58 | } 59 | 60 | cur = lens.find(99); 61 | if (!lens.is_last(cur)) { 62 | return false; 63 | } 64 | } 65 | 66 | return true; 67 | } 68 | static_assert(test_find()); 69 | 70 | } 71 | 72 | TEST_CASE("find") 73 | { 74 | { 75 | std::vector vec{1, 2, 3, 4, 5}; 76 | auto idx = flux::find(vec, 3); 77 | REQUIRE(idx == 2); 78 | } 79 | 80 | { 81 | std::vector vec{1, 2, 3, 4, 5}; 82 | auto idx = flux::ref(vec).find(99); 83 | REQUIRE(idx == std::ssize(vec)); 84 | } 85 | 86 | { 87 | std::string_view str = "abcdefg"; 88 | auto idx = flux::find(str, 'd'); 89 | REQUIRE(idx == 3); 90 | } 91 | 92 | { 93 | std::string str = ""; 94 | auto idx = flux::find(str, 'a'); 95 | REQUIRE(idx == flux::last(str)); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /test/test_for_each.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "test_utils.hpp" 9 | 10 | namespace { 11 | 12 | struct S { 13 | constexpr void p() const { *p_ += i_; } 14 | 15 | int *p_; 16 | int i_; 17 | }; 18 | 19 | constexpr bool test_for_each() 20 | { 21 | { 22 | int sum = 0; 23 | auto fun = [&](int i) { sum += i; }; 24 | auto arr = std::array{0, 2, 4, 6}; 25 | 26 | flux::for_each(arr, fun); 27 | 28 | STATIC_CHECK(sum == 12); 29 | } 30 | 31 | { 32 | int sum = 0; 33 | auto fun = [&](int& i) { sum += i; }; 34 | auto arr = std::array{0, 2, 4, 6}; 35 | 36 | flux::from(arr).for_each(fun); 37 | 38 | STATIC_CHECK(sum == 12); 39 | } 40 | 41 | { 42 | int sum = 0; 43 | auto arr = std::array{ S{&sum, 0}, S{&sum, 2}, S{&sum, 4}, S{&sum, 6}}; 44 | 45 | flux::for_each(arr, &S::p); 46 | 47 | STATIC_CHECK(sum == 12); 48 | } 49 | 50 | { 51 | struct counter { 52 | constexpr void operator()(int i) { sum += i; } 53 | int sum = 0; 54 | }; 55 | 56 | auto ilist = {0, 2, 4, 6}; 57 | auto result = flux::for_each(ilist, counter{}); 58 | 59 | STATIC_CHECK(result.sum == 12); 60 | } 61 | 62 | return true; 63 | } 64 | static_assert(test_for_each()); 65 | 66 | } 67 | 68 | TEST_CASE("for_each") 69 | { 70 | bool result = test_for_each(); 71 | REQUIRE(result); 72 | } -------------------------------------------------------------------------------- /test/test_getlines.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 2 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "test_utils.hpp" 12 | 13 | constexpr const auto& test_str1 = "Line1\nLine2\nLine3"; 14 | 15 | TEST_CASE("getlines") 16 | { 17 | std::istringstream iss(test_str1); 18 | 19 | auto seq = flux::getlines(iss); 20 | 21 | static_assert(flux::sequence); 22 | static_assert(!flux::multipass_sequence); 23 | 24 | auto cur = seq.first(); 25 | REQUIRE(seq[cur] == "Line1"); 26 | seq.inc(cur); 27 | REQUIRE(seq[cur] == "Line2"); 28 | seq.inc(cur); 29 | REQUIRE(seq[cur] == "Line3"); 30 | seq.inc(cur); 31 | REQUIRE(seq.is_last(cur)); 32 | 33 | // Make sure assertion fires 34 | REQUIRE_THROWS_AS(seq.inc(cur), flux::unrecoverable_error); 35 | } 36 | 37 | TEST_CASE("getlines to vector") 38 | { 39 | std::istringstream iss(test_str1); 40 | 41 | auto const vec = flux::getlines(iss).to(); 42 | 43 | static_assert(std::same_as); 44 | 45 | REQUIRE(vec == std::vector{"Line1", "Line2", "Line3"}); 46 | } 47 | 48 | TEST_CASE("getlines with custom delimiter") 49 | { 50 | using namespace std::string_view_literals; 51 | 52 | std::istringstream iss("Lorem ipsum dolor sit amet"); 53 | 54 | auto seq = flux::getlines(iss, ' '); 55 | 56 | REQUIRE(flux::equal(seq, flux::split_string("Lorem ipsum dolor sit amet"sv, ' '))); 57 | } 58 | 59 | constexpr const auto& test_str2 = L"Line1\nLine2\nLine3"; 60 | 61 | TEST_CASE("getlines with wide strings") 62 | { 63 | std::wistringstream iss(test_str2); 64 | 65 | auto const vec = flux::getlines(iss).to(); 66 | 67 | static_assert(std::same_as); 68 | 69 | REQUIRE(vec == std::vector{L"Line1", L"Line2", L"Line3"}); 70 | } 71 | -------------------------------------------------------------------------------- /test/test_iota.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 2 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #include 6 | #include 7 | 8 | #include "test_utils.hpp" 9 | 10 | namespace { 11 | 12 | constexpr bool test_iota_basic() 13 | { 14 | auto f = flux::ints(); 15 | 16 | using F = decltype(f); 17 | 18 | static_assert(sizeof(f) == 1); 19 | static_assert(flux::sequence); 20 | static_assert(flux::bidirectional_sequence); 21 | static_assert(flux::random_access_sequence); 22 | static_assert(flux::infinite_sequence); 23 | static_assert(not flux::bounded_sequence); 24 | static_assert(not flux::sized_sequence); 25 | 26 | STATIC_CHECK(check_equal(flux::take(f, 5), {0, 1, 2, 3, 4})); 27 | 28 | return true; 29 | } 30 | static_assert(test_iota_basic()); 31 | 32 | constexpr bool test_iota_from() 33 | { 34 | auto f = flux::iota(1u); 35 | 36 | using F = decltype(f); 37 | 38 | static_assert(sizeof(f) == sizeof(unsigned)); 39 | static_assert(flux::sequence); 40 | static_assert(flux::bidirectional_sequence); 41 | static_assert(flux::random_access_sequence); 42 | static_assert(flux::infinite_sequence); 43 | static_assert(not flux::bounded_sequence); 44 | static_assert(not flux::sized_sequence); 45 | 46 | STATIC_CHECK(check_equal(flux::take(f, 5), {1u, 2u, 3u, 4u, 5u})); 47 | 48 | return true; 49 | } 50 | static_assert(test_iota_from()); 51 | 52 | constexpr bool test_iota_bounded() 53 | { 54 | auto f = flux::iota(1u, 6u); 55 | 56 | using F = decltype(f); 57 | 58 | static_assert(flux::sequence); 59 | static_assert(flux::bidirectional_sequence); 60 | static_assert(flux::random_access_sequence); 61 | static_assert(not flux::infinite_sequence); 62 | static_assert(flux::bounded_sequence); 63 | static_assert(flux::sized_sequence); 64 | 65 | STATIC_CHECK(f.size() == 5); 66 | STATIC_CHECK(check_equal(f, {1u, 2u, 3u, 4u, 5u})); 67 | 68 | return true; 69 | } 70 | static_assert(test_iota_bounded()); 71 | 72 | constexpr bool test_iota_custom_type() 73 | { 74 | using namespace std::chrono_literals; 75 | 76 | auto f = flux::iota(1s, 6s); 77 | 78 | using F = decltype(f); 79 | 80 | static_assert(flux::sequence); 81 | static_assert(flux::bidirectional_sequence); 82 | static_assert(not flux::random_access_sequence); // no iter_difference_t 83 | static_assert(not flux::infinite_sequence); 84 | static_assert(flux::bounded_sequence); 85 | static_assert(not flux::sized_sequence); // ! 86 | 87 | STATIC_CHECK(f.count() == 5); 88 | STATIC_CHECK(check_equal(f, {1s, 2s, 3s, 4s, 5s})); 89 | 90 | return true; 91 | } 92 | static_assert(test_iota_custom_type()); 93 | 94 | } 95 | 96 | TEST_CASE("iota") 97 | { 98 | REQUIRE(test_iota_basic()); 99 | REQUIRE(test_iota_from()); 100 | REQUIRE(test_iota_bounded()); 101 | REQUIRE(test_iota_custom_type()); 102 | } 103 | -------------------------------------------------------------------------------- /test/test_istream.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "test_utils.hpp" 9 | 10 | TEST_CASE("istream") 11 | { 12 | { 13 | std::istringstream iss{"0 1 2 3 4"}; 14 | 15 | auto seq = flux::from_istream(iss); 16 | 17 | using S = decltype(seq); 18 | 19 | static_assert(flux::sequence); 20 | static_assert(not flux::multipass_sequence); 21 | static_assert(not flux::sized_sequence); 22 | static_assert(not flux::bounded_sequence); 23 | 24 | static_assert(std::same_as, int const&>); 25 | static_assert(std::same_as, int const&&>); 26 | 27 | REQUIRE(check_equal(seq, {0, 1, 2, 3, 4})); 28 | } 29 | 30 | { 31 | std::istringstream iss{"0 1 2 3 4 5 6 7 8 9 10"}; 32 | 33 | auto seq = flux::from_istream(iss) 34 | .filter([](int i) { return i >= 5; }) 35 | .map([](int i) { return i * 2; }) 36 | .take(3); 37 | 38 | static_assert(flux::sequence); 39 | static_assert(not flux::multipass_sequence); 40 | 41 | REQUIRE(check_equal(seq, {10, 12, 14})); 42 | } 43 | 44 | { 45 | std::istringstream iss; 46 | auto seq = flux::from_istream(iss); 47 | REQUIRE(flux::is_last(seq, flux::first(seq))); 48 | } 49 | } -------------------------------------------------------------------------------- /test/test_istreambuf.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | 9 | #include "test_utils.hpp" 10 | 11 | TEST_CASE("istreambuf") 12 | { 13 | { 14 | std::istringstream iss("hello world"); 15 | 16 | auto& seq = *iss.rdbuf(); 17 | 18 | using S = decltype(seq); 19 | 20 | static_assert(flux::sequence); 21 | static_assert(not flux::multipass_sequence); 22 | static_assert(not flux::sized_sequence); 23 | static_assert(not flux::bounded_sequence); 24 | 25 | static_assert(std::same_as, char>); 26 | static_assert(std::same_as, char>); 27 | static_assert(std::same_as, char>); 28 | 29 | std::string str; 30 | FLUX_FOR(char c, seq) { 31 | str.push_back(c); 32 | } 33 | 34 | REQUIRE(str == "hello world"); 35 | } 36 | 37 | { 38 | std::wistringstream iss(L"hello world"); 39 | 40 | auto seq = flux::from_istreambuf(iss.rdbuf()); 41 | 42 | static_assert(flux::sequence); 43 | static_assert(std::same_as, wchar_t>); 44 | 45 | std::wstring str; 46 | FLUX_FOR(auto c, seq) { 47 | str.push_back(c); 48 | } 49 | 50 | REQUIRE(str == L"hello world"); 51 | } 52 | 53 | { 54 | std::basic_istringstream iss(U"hello world"); 55 | 56 | auto seq = flux::from_istreambuf(iss); 57 | 58 | static_assert(flux::sequence); 59 | static_assert(std::same_as, char32_t>); 60 | 61 | std::u32string str; 62 | FLUX_FOR(auto c, seq) { 63 | str.push_back(c); 64 | } 65 | 66 | REQUIRE(str == U"hello world"); 67 | } 68 | 69 | // Make sure assertion fires 70 | { 71 | std::basic_streambuf>* ptr = nullptr; 72 | REQUIRE_THROWS_AS(flux::from_istreambuf(ptr), flux::unrecoverable_error); 73 | } 74 | } -------------------------------------------------------------------------------- /test/test_module_import.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | import flux; 9 | 10 | int main() 11 | { 12 | FLUX_ASSERT(1 != 2); 13 | constexpr int arr[] = {1, 2, 3, 4, 5}; 14 | static_assert(flux::sum(arr) == 15); 15 | } 16 | -------------------------------------------------------------------------------- /test/test_output_to.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "test_utils.hpp" 14 | 15 | namespace { 16 | 17 | constexpr bool test_output_to() 18 | { 19 | { 20 | int arr1[] = {1, 2, 3, 4, 5}; 21 | int arr2[5] = {}; 22 | 23 | int* p = flux::output_to(arr1, arr2); 24 | 25 | STATIC_CHECK(check_equal(arr2, {1, 2, 3, 4, 5})); 26 | STATIC_CHECK(p == arr2 + 5); 27 | } 28 | 29 | return true; 30 | } 31 | static_assert(test_output_to()); 32 | 33 | } 34 | 35 | TEST_CASE("output to") 36 | { 37 | bool result = test_output_to(); 38 | REQUIRE(result); 39 | 40 | SUBCASE("...with contiguous iterators") 41 | { 42 | std::vector const in{1, 2, 3, 4, 5}; 43 | std::vector out(5); 44 | 45 | auto iter = flux::output_to(in, out.begin()); 46 | 47 | REQUIRE(out == in); 48 | REQUIRE(iter == out.cend()); 49 | } 50 | 51 | SUBCASE("...with back_inserter") 52 | { 53 | std::vector const in{1, 2, 3, 4, 5}; 54 | std::list out; 55 | 56 | flux::output_to(in, std::back_inserter(out)); 57 | 58 | REQUIRE(std::ranges::equal(in, out)); 59 | } 60 | 61 | SUBCASE("...with iostreams") 62 | { 63 | std::istringstream iss("1 2 3"); 64 | std::ostringstream oss; 65 | 66 | flux::from_istream(iss).output_to(std::ostream_iterator(oss)); 67 | 68 | REQUIRE(oss.str() == "123"); 69 | } 70 | 71 | SUBCASE("...with streambufs") 72 | { 73 | std::istringstream iss(" hello world!! "); 74 | std::ostringstream oss; 75 | 76 | flux::from_istreambuf(iss).output_to(std::ostreambuf_iterator(oss)); 77 | 78 | REQUIRE(oss.str() == " hello world!! "); 79 | } 80 | 81 | SUBCASE("...with empty input sequences") 82 | { 83 | std::vector const in; 84 | std::vector out; 85 | 86 | auto iter = flux::output_to(in, out.begin()); 87 | 88 | REQUIRE(iter == out.begin()); 89 | REQUIRE(out.empty()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /test/test_single.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | 9 | #include "test_utils.hpp" 10 | 11 | namespace { 12 | 13 | constexpr bool test_single() 14 | { 15 | { 16 | auto s = flux::single(1.0f); 17 | 18 | using S = decltype(s); 19 | 20 | static_assert(flux::contiguous_sequence); 21 | static_assert(flux::sized_sequence); 22 | static_assert(flux::bounded_sequence); 23 | 24 | static_assert(flux::contiguous_sequence); 25 | static_assert(flux::sized_sequence); 26 | static_assert(flux::bounded_sequence); 27 | 28 | static_assert(std::same_as, float&>); 29 | static_assert(std::same_as, float>); 30 | static_assert(std::same_as, float&&>); 31 | 32 | static_assert(std::same_as, float const&>); 33 | static_assert(std::same_as, float>); 34 | static_assert(std::same_as, float const&&>); 35 | 36 | static_assert(flux::size(s) == 1); 37 | static_assert(flux::size(std::as_const(s)) == 1); 38 | 39 | 40 | } 41 | 42 | { 43 | namespace rng = std::ranges; 44 | 45 | auto view = flux::single(1.0f); 46 | 47 | using V = decltype(view); 48 | 49 | static_assert(rng::contiguous_range); 50 | static_assert(rng::common_range); 51 | static_assert(rng::sized_range); 52 | 53 | static_assert(rng::contiguous_range); 54 | static_assert(rng::common_range); 55 | static_assert(rng::sized_range); 56 | 57 | static_assert(rng::size(view) == 1); 58 | 59 | float sum = 0.0f; 60 | for (auto f : view) { 61 | sum += f; 62 | } 63 | 64 | STATIC_CHECK(sum == 1.0f); 65 | 66 | std::ranges::sort(view); 67 | 68 | 69 | } 70 | 71 | return true; 72 | } 73 | static_assert(test_single()); 74 | 75 | } 76 | 77 | 78 | TEST_CASE("single") 79 | { 80 | bool result = test_single(); 81 | REQUIRE(result); 82 | } -------------------------------------------------------------------------------- /test/test_starts_with.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "test_utils.hpp" 9 | 10 | namespace { 11 | 12 | struct S { 13 | int i; 14 | 15 | constexpr int get() const { return i; } 16 | }; 17 | 18 | constexpr bool test_starts_with() 19 | { 20 | // Basic starts_with 21 | { 22 | std::array const arr1{1, 2, 3, 4, 5}; 23 | int arr2[] = {1, 2, 3}; 24 | 25 | STATIC_CHECK(flux::starts_with(arr1, arr2)); 26 | } 27 | 28 | // Basic member starts_with 29 | { 30 | std::array const arr1{1, 2, 3, 4, 5}; 31 | int arr2[] = {1, 2, 3}; 32 | 33 | STATIC_CHECK(flux::ref(arr1).starts_with(arr2)); 34 | } 35 | 36 | // Basic starts_with, failing 37 | { 38 | std::array const arr1{1, 2, 3, 4, 5}; 39 | int arr2[] = {1, 2, 99}; 40 | 41 | STATIC_CHECK(not flux::starts_with(arr1, arr2)); 42 | } 43 | 44 | // Basic member starts_with, failing 45 | { 46 | std::array const arr1{1, 2, 3, 4, 5}; 47 | int arr2[] = {1, 2, 99}; 48 | 49 | STATIC_CHECK(not flux::ref(arr1).starts_with(arr2)); 50 | } 51 | 52 | // A sequence always starts_with itself 53 | { 54 | std::array const arr{1, 2, 3, 4, 5}; 55 | 56 | STATIC_CHECK(flux::starts_with(arr, arr)); 57 | 58 | // ...even if they're both empty 59 | STATIC_CHECK(flux::starts_with(flux::empty, flux::empty)); 60 | } 61 | 62 | // Needle is longer than haystack 63 | { 64 | std::array const arr1{1, 2, 3}; 65 | int arr2[] = {1, 2, 3, 4, 5}; 66 | 67 | STATIC_CHECK(not flux::starts_with(arr1, arr2)); 68 | } 69 | 70 | // Cross-type starts_with using default comparator 71 | { 72 | std::array const arr1{1, 2, 3, 4, 5}; 73 | double arr2[] = {1.0, 2.0, 3.0, 4.0, 99.0}; 74 | 75 | STATIC_CHECK(flux::starts_with(arr1, flux::ref(arr2).take(3))); 76 | STATIC_CHECK(not flux::starts_with(arr1, arr2)); 77 | } 78 | 79 | // starts_with using custom comparator 80 | { 81 | std::array const arr1{1, 2, 3, 4, 5}; 82 | S arr2[] = {{1}, {2}, {3}}; 83 | 84 | auto cmp = [](int i, S s) { return i == s.i; }; 85 | 86 | STATIC_CHECK(flux::starts_with(arr1, arr2, cmp)); 87 | STATIC_CHECK(flux::ref(arr1).starts_with(arr2, cmp)); 88 | } 89 | 90 | // starts_with using projection 91 | { 92 | S arr1[] = {{1}, {2}, {3}, {4}, {5}}; 93 | std::array const arr2{1, 2, 3}; 94 | 95 | STATIC_CHECK(flux::starts_with(arr1, arr2, flux::proj2{std::equal_to{}, &S::i})); 96 | 97 | STATIC_CHECK(flux::ref(arr1).starts_with(arr2, flux::proj2{std::equal_to{}, &S::get})); 98 | 99 | STATIC_CHECK(flux::starts_with(arr1, arr1, flux::proj(std::equal_to{}, &S::i))); 100 | } 101 | 102 | return true; 103 | } 104 | static_assert(test_starts_with()); 105 | 106 | } 107 | 108 | TEST_CASE("starts_with") 109 | { 110 | bool result = test_starts_with(); 111 | REQUIRE(result); 112 | } -------------------------------------------------------------------------------- /test/test_take_while.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "test_utils.hpp" 9 | 10 | namespace { 11 | 12 | struct ints { 13 | int from = 0; 14 | 15 | struct flux_sequence_traits : flux::default_sequence_traits { 16 | static constexpr int first(ints) { return 0; } 17 | static constexpr bool is_last(ints, int) { return false; } 18 | static constexpr int read_at(ints self, int cur){ return self.from + cur; } 19 | static constexpr int& inc(ints, int& cur, int o = 1) { return cur += o; } 20 | static constexpr int& dec(ints, int& cur) { return --cur; } 21 | static constexpr int distance(ints, int from, int to) { return to - from; } 22 | }; 23 | }; 24 | 25 | constexpr bool test_take_while() 26 | { 27 | { 28 | auto seq = flux::take_while(ints{10}, [](int i) { return i != 25; }); 29 | 30 | using S = decltype(seq); 31 | 32 | static_assert(flux::random_access_sequence); 33 | static_assert(not flux::bounded_sequence); 34 | static_assert(not flux::sized_sequence); 35 | 36 | STATIC_CHECK(check_equal( 37 | seq, {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24})); 38 | } 39 | 40 | { 41 | auto seq = flux::from(std::array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) 42 | .take_while([](int i) { return i != 50; }); 43 | 44 | using S = decltype(seq); 45 | 46 | static_assert(flux::random_access_sequence); 47 | static_assert(not flux::bounded_sequence); 48 | static_assert(not flux::sized_sequence); 49 | 50 | STATIC_CHECK(check_equal(seq, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); 51 | } 52 | 53 | { 54 | int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 55 | 56 | auto cur = flux::ref(arr) 57 | .take_while([](int i) { return i < 5; }) 58 | .find(99); 59 | 60 | STATIC_CHECK(cur == flux::next(arr, flux::first(arr), 5)); 61 | } 62 | 63 | { 64 | auto seq = flux::from(ints{}) 65 | .filter([](int i) { return i % 2 == 0; }) 66 | .take_while([](int i) { return i <= 10; }) 67 | .map([](int i) { return i * i; }) 68 | .drop(1); 69 | 70 | STATIC_CHECK(check_equal(seq, {4, 16, 36, 64, 100})); 71 | } 72 | 73 | return true; 74 | } 75 | static_assert(test_take_while()); 76 | 77 | } 78 | 79 | TEST_CASE("take_while") 80 | { 81 | bool result = test_take_while(); 82 | REQUIRE(result); 83 | } -------------------------------------------------------------------------------- /test/test_unchecked.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "test_utils.hpp" 9 | 10 | namespace { 11 | 12 | constexpr bool test_unchecked() 13 | { 14 | using namespace flux; 15 | 16 | { 17 | auto seq = unchecked(std::array{5, 4, 3, 2, 1}); 18 | 19 | using S = decltype(seq); 20 | 21 | static_assert(contiguous_sequence); 22 | static_assert(sized_sequence); 23 | static_assert(bounded_sequence); 24 | 25 | seq.sort(); 26 | 27 | STATIC_CHECK(check_equal(seq, {1, 2, 3, 4, 5})); 28 | } 29 | 30 | { 31 | auto ints = std::array{5, 4, 3, 2, 1}; 32 | auto doubles = std::array{3.0, 2.0, 1.0}; 33 | 34 | auto seq = unchecked(zip(mut_ref(ints), mut_ref(doubles))); 35 | 36 | using S = decltype(seq); 37 | 38 | static_assert(random_access_sequence); 39 | static_assert(bounded_sequence); 40 | static_assert(sized_sequence); 41 | static_assert(std::same_as, std::pair>); 42 | static_assert(std::same_as, std::pair>); 43 | static_assert(std::same_as, std::pair>); 44 | 45 | seq.sort(flux::proj(std::weak_order, [](auto p) { return p.second; })); 46 | 47 | STATIC_CHECK(check_equal(doubles, {1.0, 2.0, 3.0})); 48 | STATIC_CHECK(check_equal(ints, {3, 4, 5, 2, 1})); 49 | 50 | } 51 | 52 | return true; 53 | } 54 | static_assert(test_unchecked()); 55 | 56 | 57 | } 58 | 59 | TEST_CASE("unchecked adaptor") 60 | { 61 | auto res = test_unchecked(); 62 | REQUIRE(res); 63 | } 64 | -------------------------------------------------------------------------------- /test/test_unfold.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | 9 | #include "test_utils.hpp" 10 | 11 | namespace { 12 | 13 | constexpr bool test_unfold() 14 | { 15 | // Basic unfold 16 | { 17 | auto seq = flux::unfold([](int i) { return ++i; }, 0); 18 | 19 | using S = decltype(seq); 20 | static_assert(flux::sequence); 21 | static_assert(not flux::multipass_sequence); 22 | static_assert(flux::infinite_sequence); 23 | static_assert(not flux::sized_sequence); 24 | 25 | STATIC_CHECK(check_equal(flux::take(seq, 10), flux::ints().take(10))); 26 | } 27 | 28 | // unfold -> take is a finite sequence 29 | { 30 | auto seq = flux::unfold([](int i) { return ++i; }, 0).take(10); 31 | 32 | using S = decltype(seq); 33 | static_assert(flux::sequence); 34 | static_assert(not flux::multipass_sequence); 35 | static_assert(not flux::infinite_sequence); 36 | static_assert(flux::sized_sequence); 37 | 38 | STATIC_CHECK(flux::size(seq) == 10); 39 | STATIC_CHECK(check_equal(seq, flux::ints().take(10))); 40 | } 41 | 42 | // unfold can be used to implement repeat() 43 | { 44 | auto repeat = flux::unfold(std::identity{}, std::string_view("hello")); 45 | 46 | STATIC_CHECK(check_equal(flux::take(repeat, 3), {"hello", "hello", "hello"})); 47 | } 48 | 49 | // unfold works with mutable stateful lambdas 50 | { 51 | auto fib = flux::unfold([next = 1](int cur) mutable { 52 | return std::exchange(next, cur + next); 53 | }, 0).take(10); 54 | 55 | STATIC_CHECK(check_equal(fib, {0, 1, 1, 2, 3, 5, 8, 13, 21, 34})); 56 | } 57 | 58 | // internal iteration works as expected 59 | { 60 | auto seq = flux::unfold([](int i) { return ++i; }, 0); 61 | 62 | auto cur = seq.find(5); 63 | 64 | STATIC_CHECK(seq[cur] == 5); 65 | seq.inc(cur); 66 | STATIC_CHECK(seq[cur] == 6); 67 | } 68 | 69 | return true; 70 | } 71 | static_assert(test_unfold()); 72 | 73 | } 74 | 75 | TEST_CASE("unfold") 76 | { 77 | bool res = test_unfold(); 78 | REQUIRE(res); 79 | } -------------------------------------------------------------------------------- /test/test_write_to.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #ifdef USE_MODULES 12 | import flux; 13 | #else 14 | #include 15 | #endif 16 | 17 | TEST_CASE("write to") 18 | { 19 | SUBCASE("Basic write_to") 20 | { 21 | std::vector vec{1, 2, 3, 4, 5}; 22 | 23 | std::ostringstream oss; 24 | 25 | flux::write_to(vec, oss); 26 | 27 | REQUIRE(oss.str() == "[1, 2, 3, 4, 5]"); 28 | } 29 | 30 | SUBCASE("Nested sequences") 31 | { 32 | std::vector>> vec{ 33 | { {1, 2}, {3, 4} }, 34 | { {5, 6}, {7, 8} }, 35 | { {9, 10}, {11, 12} } 36 | }; 37 | 38 | std::ostringstream oss; 39 | 40 | flux::ref(vec).write_to(oss); 41 | 42 | REQUIRE(oss.str() == "[[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]]"); 43 | } 44 | 45 | SUBCASE("Reading and writing streams") 46 | { 47 | std::istringstream iss("1 2 3 4 5"); 48 | std::ostringstream oss; 49 | 50 | flux::from_istream(iss).write_to(oss) << "\n"; 51 | 52 | REQUIRE(oss.str() == "[1, 2, 3, 4, 5]\n"); 53 | } 54 | } -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(make_single_header make_single_header.cpp) 3 | target_compile_features(make_single_header PRIVATE cxx_std_20) 4 | target_link_libraries(make_single_header PRIVATE flux) 5 | 6 | if (CMAKE_COMPILER_IS_GNUCXX) 7 | target_link_libraries(make_single_header PRIVATE stdc++fs) 8 | endif() 9 | --------------------------------------------------------------------------------