├── .clang-format ├── .clang-format-ignore ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ └── codeql.yml ├── .gitignore ├── CITATION.cff ├── CMakeLists.txt ├── CONTRIBUTING.md ├── INSTALL.md ├── LICENSE ├── README.md ├── all └── CMakeLists.txt ├── api_search ├── compile_all.sh ├── compile_all_and_deploy.sh ├── frontend │ ├── ExploreCompile.sh │ ├── TypeSignatureTestCompile.sh │ ├── compile.sh │ ├── elm.json │ └── src │ │ ├── FPlusApiCommon.elm │ │ ├── FPlusApiExplore.elm │ │ ├── FPlusApiSearch.elm │ │ ├── TypeSignature.elm │ │ ├── TypeSignatureTestMain.elm │ │ ├── explore.html │ │ ├── favicon.png │ │ ├── highlight │ │ ├── LICENSE │ │ ├── highlight.pack.js │ │ └── styles │ │ │ ├── default.css │ │ │ └── monokai-sublime.css │ │ ├── htmlmain.js │ │ ├── htmlmain_explore.js │ │ ├── index.html │ │ ├── style.css │ │ └── style_explore.css ├── generate_database.sh └── parse_source_files.cpp ├── cmake ├── FunctionalPlusConfig.cmake ├── install-rules.cmake ├── root-project.cmake └── warnings.cmake ├── conanfile.txt ├── examples ├── 99_problems.cpp ├── CMakeLists.txt ├── performance_range_v3.cpp └── readme_perf_examples.cpp ├── generate ├── .gitignore └── auto_generate.py ├── include └── fplus │ ├── benchmark_session.hpp │ ├── compare.hpp │ ├── composition.hpp │ ├── container_common.hpp │ ├── container_properties.hpp │ ├── container_traits.hpp │ ├── curry.hpp │ ├── curry_instances.autogenerated_defines │ ├── extrapolate.hpp │ ├── filter.hpp │ ├── fplus.hpp │ ├── function_traits.hpp │ ├── fwd.hpp │ ├── fwd_instances.autogenerated_defines │ ├── generate.hpp │ ├── internal │ ├── apply.hpp │ ├── asserts │ │ ├── composition.hpp │ │ ├── functions.hpp │ │ └── pairs.hpp │ ├── compare.hpp │ ├── composition.hpp │ ├── container_common.hpp │ ├── function_traits_asserts.hpp │ ├── invoke.hpp │ ├── meta.hpp │ └── split.hpp │ ├── interpolate.hpp │ ├── maps.hpp │ ├── maybe.hpp │ ├── numeric.hpp │ ├── optimize.hpp │ ├── pairs.hpp │ ├── queue.hpp │ ├── raii.hpp │ ├── read.hpp │ ├── replace.hpp │ ├── result.hpp │ ├── search.hpp │ ├── sets.hpp │ ├── shared_ref.hpp │ ├── show.hpp │ ├── side_effects.hpp │ ├── split.hpp │ ├── stopwatch.hpp │ ├── string_tools.hpp │ ├── timed.hpp │ ├── transform.hpp │ ├── tree.hpp │ └── variant.hpp ├── include_all_in_one └── include │ └── fplus │ └── fplus.hpp ├── logo ├── fplus.png └── fplus.xcf ├── script ├── auto_format.sh ├── ci.sh └── ci_setup_linux.sh └── test ├── CMakeLists.txt ├── benchmark_session_test.cpp ├── compare_test.cpp ├── composition_test.cpp ├── container_common_test.cpp ├── container_properties_test.cpp ├── container_traits_test.cpp ├── curry_test.cpp ├── extrapolate_test.cpp ├── filter_test.cpp ├── function_traits_test.cpp ├── fwd_test.cpp ├── generate_test.cpp ├── interpolate_test.cpp ├── invoke_test.cpp ├── maps_test.cpp ├── maybe_test.cpp ├── numeric_test.cpp ├── optimize_test.cpp ├── pairs_test.cpp ├── queue_test.cpp ├── raii_test.cpp ├── read_test.cpp ├── readme_examples_test.cpp ├── replace_test.cpp ├── result_test.cpp ├── search_test.cpp ├── sets_test.cpp ├── shared_ref_test.cpp ├── show_test.cpp ├── show_versions.cpp ├── side_effects_test.cpp ├── split_test.cpp ├── stopwatch_test.cpp ├── stringtools_test.cpp ├── timed_test.cpp ├── transform_test.cpp ├── tree_test.cpp ├── udemy_course_test.cpp └── variant_test.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | -------------------------------------------------------------------------------- /.clang-format-ignore: -------------------------------------------------------------------------------- 1 | # Ignore generated files 2 | ./include_all_in_one/* 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [ dobiasd ] 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [ workflow_dispatch, push, pull_request ] 4 | 5 | jobs: 6 | build_gcc: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | build_config: 11 | - { version: 15 } 12 | - { version: 14 } 13 | - { version: 13 } 14 | - { version: 12 } 15 | - { version: 11 } 16 | - { version: 10 } 17 | - { version: 9 } 18 | container: 19 | image: gcc:${{ matrix.build_config.version }} 20 | options: -v /usr/local:/host_usr_local 21 | name: "gcc-${{ matrix.build_config.version }}" 22 | steps: 23 | - uses: actions/checkout@main 24 | - name: Setup 25 | run: | 26 | echo "/host_usr_local/bin" >> $GITHUB_PATH 27 | script/ci_setup_linux.sh 28 | - name: Build and Test 29 | run: script/ci.sh run_tests 30 | 31 | 32 | build_clang: 33 | runs-on: ubuntu-latest 34 | strategy: 35 | matrix: 36 | build_config: 37 | - { version: 20 } 38 | - { version: 19 } 39 | - { version: 18 } 40 | - { version: 17 } 41 | - { version: 16 } 42 | - { version: 15 } 43 | - { version: 14 } 44 | - { version: 13 } 45 | - { version: 12 } 46 | - { version: 11 } 47 | container: 48 | image: teeks99/clang-ubuntu:${{ matrix.build_config.version }} 49 | options: -v /usr/local:/host_usr_local 50 | name: "clang-${{ matrix.build_config.version }}" 51 | env: 52 | CC: clang-${{ matrix.build_config.version }}${{ matrix.build_config.suffix }} 53 | CXX: clang++-${{ matrix.build_config.version }}${{ matrix.build_config.suffix }} 54 | steps: 55 | - uses: actions/checkout@main 56 | - name: Setup 57 | run: | 58 | echo "/host_usr_local/bin" >> $GITHUB_PATH 59 | script/ci_setup_linux.sh 60 | - name: Setup libc++ 61 | run: | 62 | if [ "${{ matrix.build_config.version }}" -ge "12" ]; then 63 | apt-get install -y --no-install-recommends libunwind-${{ matrix.build_config.version }}-dev; 64 | fi 65 | echo "CXXFLAGS=-stdlib=libc++" >> $GITHUB_ENV 66 | - name: Build and Test 67 | run: script/ci.sh run_tests 68 | 69 | 70 | build_osx: 71 | runs-on: macos-latest 72 | name: "OS X" 73 | env: 74 | CC: clang 75 | CXX: clang++ 76 | steps: 77 | - uses: actions/checkout@main 78 | - name: Build and Test 79 | run: script/ci.sh run_tests 80 | 81 | 82 | build_windows_msvc: 83 | runs-on: windows-${{ matrix.msvc_version }} 84 | strategy: 85 | matrix: 86 | msvc_version: 87 | - 2019 88 | - 2022 89 | name: "Windows ${{ matrix.msvc_version }} MSVC" 90 | steps: 91 | - uses: actions/checkout@main 92 | - uses: ilammy/msvc-dev-cmd@v1 93 | - name: Build and Test 94 | shell: bash 95 | run: script/ci.sh run_tests 96 | 97 | 98 | formatting-check: 99 | name: "formatting" 100 | runs-on: ubuntu-latest 101 | steps: 102 | - uses: actions/checkout@main 103 | - uses: DoozyX/clang-format-lint-action@master 104 | name: "Verify formatting" 105 | with: 106 | clangFormatVersion: 16 107 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: codeql 2 | on: 3 | push: 4 | pull_request: 5 | schedule: 6 | - cron: '0 5 * * 3' 7 | jobs: 8 | codeql: 9 | runs-on: ubuntu-latest 10 | name: "CodeQL" 11 | env: 12 | CC: gcc 13 | CXX: g++ 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@main 17 | - name: CodeQL Initialization 18 | uses: github/codeql-action/init@v3 19 | with: 20 | languages: cpp 21 | queries: +security-and-quality 22 | - name: Build 23 | shell: bash 24 | run: script/ci.sh run_build 25 | - name: CodeQL Analysis 26 | uses: github/codeql-action/analyze@v3 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-project 2 | *.sublime-workspace 3 | .vscode 4 | test/temp* 5 | build 6 | build64 7 | api_search/frontend/build 8 | api_search/frontend/elm-stuff 9 | api_search/frontend/deploy.sh 10 | api_search/frontend/src/Database.elm 11 | .mypy_cache 12 | .idea 13 | cmake-build-*/ 14 | CMakeUserPresets.json 15 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: "FunctionalPlus" 3 | url: "https://github.com/Dobiasd/FunctionalPlus/" 4 | authors: 5 | - family-names: "Hermann" 6 | given-names: "Tobias" 7 | orcid: "https://orcid.org/0009-0007-4792-4904" 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(FunctionalPlus VERSION 0.2.25) 4 | 5 | # ---- Warning guard ---- 6 | 7 | # Protect dependents from this project's warnings if the guard isn't disabled 8 | set(FunctionalPlus_warning_guard SYSTEM) 9 | if (FunctionalPlus_INCLUDE_WITHOUT_SYSTEM) 10 | set(FunctionalPlus_warning_guard "") 11 | endif () 12 | 13 | # ---- Declare library ---- 14 | 15 | add_library(fplus INTERFACE) 16 | add_library(FunctionalPlus::fplus ALIAS fplus) 17 | target_include_directories( 18 | fplus 19 | ${FunctionalPlus_warning_guard} 20 | INTERFACE 21 | "$" 22 | ) 23 | 24 | target_compile_features(fplus INTERFACE cxx_std_14) 25 | 26 | find_package(Threads REQUIRED) 27 | target_link_libraries(fplus INTERFACE Threads::Threads) 28 | 29 | # ---- Create auto-generated fwd and curried functions, and amalgamated library ---- 30 | 31 | find_package(Python COMPONENTS Interpreter QUIET) 32 | if (Python_FOUND) 33 | add_custom_target( 34 | auto_generate 35 | ALL 36 | COMMAND 37 | "${Python_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/generate/auto_generate.py" 38 | BYPRODUCTS 39 | "${PROJECT_SOURCE_DIR}/include/fplus/fwd_instances.autogenerated_defines" 40 | "${PROJECT_SOURCE_DIR}/include/fplus/curry_instances.autogenerated_defines" 41 | "${PROJECT_SOURCE_DIR}/include_all_in_one/include/fplus/fplus.hpp" 42 | ) 43 | add_dependencies(fplus auto_generate) 44 | else () 45 | message(STATUS "Not adding the 'auto_generate' target (requires Python)") 46 | endif () 47 | 48 | # ---- Install ---- 49 | 50 | include(cmake/install-rules.cmake) 51 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to FunctionalPlus 2 | 3 | The main intention of this library is to provide small composable and [referentially transparent](https://en.wikipedia.org/wiki/Referential_transparency) functions. 4 | 5 | 6 | ### New Issues 7 | 8 | Feel free to open [issues](https://github.com/Dobiasd/FunctionalPlus/issues) for any kind of bugs, problems, feature requests, or questions. 9 | 10 | A good bug report should include: 11 | 12 | - A clear title 13 | - A detailed description of the problem or error 14 | - The expected behavior 15 | - (If possible) a minimal example or steps to reproduce 16 | - Information about used compiler and platform 17 | 18 | If you have problems installing FunctionalPlus please let us know by opening an issue. This will help us optimize the setup experience. 19 | 20 | 21 | ### Open Issues 22 | 23 | If you are looking for a way to contribute, have a look into the [open issues](https://github.com/Dobiasd/FunctionalPlus/issues). Especially the ones tagged with "help wanted" could be interesting to you. 24 | 25 | 26 | ### Pull requests 27 | 28 | A good [PR](https://github.com/Dobiasd/FunctionalPlus/pulls) should include: 29 | 30 | - A clear Description 31 | - Test cases 32 | - Informative commit message 33 | 34 | Before starting to write code, please check the issues to see if there is already work in progress regarding your concern to avoid redundant work. 35 | 36 | ------------------------ 37 | 38 | ### Details of the inner workings 39 | 40 | Let's say you have an idea for a new useful function you would like to [add](https://github.com/Dobiasd/FunctionalPlus/pulls). 41 | The small example of `without` can already show a lot of things. 42 | 43 | ```c++ 44 | // API search type: without : (a, [a]) -> [a] 45 | // fwd bind count: 1 46 | // without(0, [1, 0, 0, 5, 3, 0, 1]) == [1, 5, 3, 1] 47 | template 49 | Container without(T elem, const Container& xs) 50 | { 51 | return drop_if(is_equal_to(elem), xs); 52 | } 53 | ``` 54 | 55 | The function resides in `./include/fplus/filter.hpp`, because, well, it is some kind of filter. ;) 56 | 57 | Every public exposed function (so everything not in `namespace internal`) should have an `API search type`. So the `./api_search/compile_all_and_deploy.sh` can parse the type and show it on the [website](http://www.editgym.com/fplus-api-search/). It will be run by a website admin after merging your pull request 58 | 59 | If it makes sense to have a partially curried version of your function in `namespace fwd` for forward application and composition (data parameter as the last one), you should specify a `fwd bind count`. If your functions type is `foo : (a, b, c) -> d` then `generate/auto_generate.py` will insert a derived function `fwd::foo : (a, b) -> (c -> d)` into `./include/fplus/fwd_instances.autogenerated_defines` 60 | 61 | The `make` step will automatically call `python ./generate/auto_generate.py`, which will update the `fwd_instances.autogenerated_defines` file, as well as the amalgamated library in `include_all_in_one/include/fplus/fplus.hpp`. 62 | 63 | As the maintainer, I will run `cd api_search && ./compile_all_and_deploy.sh && cd ..` once your pull request has been merged, in order to update the doc at http://www.editgym.com/fplus-api-search/. 64 | 65 | 66 | A few unit tests would also be nice. In our example they belong into `./test/filter_test.cpp` 67 | 68 | ```c++ 69 | TEST_CASE("filter_test - without") 70 | { 71 | using namespace fplus; 72 | typedef std::vector Ints; 73 | REQUIRE_EQ(without(1, Ints({1,2,3})), Ints({2,3})); 74 | REQUIRE_EQ(without(5, Ints({1,2,3})), Ints({1,2,3})); 75 | REQUIRE_EQ(without(5, Ints({})), Ints({})); 76 | } 77 | ``` 78 | 79 | Try to also cover corner cases you can think of. 80 | 81 | Please do not hesitate to create a PR even if you are not completely sure if you have followed these guidelines correctly. We will help you perfect your contribution before merging. 82 | 83 | Oh, and for a unified coding style, just run `./script/auto_format.sh`. :-) 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /all/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is the lists file that should be used during development for the best 2 | # discovery in the developer's choice of IDE. 3 | cmake_minimum_required(VERSION 3.14) 4 | 5 | project(FunctionalPlusDevelopment) 6 | 7 | # Test should be added first, because it disables the warning guard 8 | add_subdirectory( 9 | "${PROJECT_SOURCE_DIR}/../test" 10 | "${PROJECT_BINARY_DIR}/test" 11 | ) 12 | 13 | add_subdirectory( 14 | "${PROJECT_SOURCE_DIR}/../examples" 15 | "${PROJECT_BINARY_DIR}/examples" 16 | ) 17 | 18 | # INTERFACE targets can't provide sources, so not all IDEs can properly 19 | # discover files belonging to targets. This is a portable way to do just that. 20 | set( 21 | fplus_headers 22 | benchmark_session.hpp 23 | compare.hpp 24 | composition.hpp 25 | container_common.hpp 26 | container_properties.hpp 27 | container_traits.hpp 28 | curry.hpp 29 | curry_instances.autogenerated_defines 30 | extrapolate.hpp 31 | filter.hpp 32 | fplus.hpp 33 | function_traits.hpp 34 | fwd.hpp 35 | fwd_instances.autogenerated_defines 36 | generate.hpp 37 | internal/apply.hpp 38 | internal/asserts/composition.hpp 39 | internal/asserts/functions.hpp 40 | internal/asserts/pairs.hpp 41 | internal/compare.hpp 42 | internal/composition.hpp 43 | internal/container_common.hpp 44 | internal/function_traits_asserts.hpp 45 | internal/invoke.hpp 46 | internal/meta.hpp 47 | internal/split.hpp 48 | interpolate.hpp 49 | maps.hpp 50 | maybe.hpp 51 | numeric.hpp 52 | optimize.hpp 53 | pairs.hpp 54 | queue.hpp 55 | raii.hpp 56 | read.hpp 57 | replace.hpp 58 | result.hpp 59 | search.hpp 60 | sets.hpp 61 | shared_ref.hpp 62 | show.hpp 63 | side_effects.hpp 64 | split.hpp 65 | stopwatch.hpp 66 | string_tools.hpp 67 | timed.hpp 68 | transform.hpp 69 | tree.hpp 70 | variant.hpp 71 | ) 72 | list( 73 | TRANSFORM 74 | fplus_headers 75 | PREPEND 76 | "${FunctionalPlus_SOURCE_DIR}/include/fplus/" 77 | ) 78 | 79 | file( 80 | WRITE 81 | "${PROJECT_BINARY_DIR}/fplus_sources.generated.cpp" 82 | "// Generated by CMake, DO NOT EDIT\n" 83 | ) 84 | add_executable( 85 | fplus_sources 86 | EXCLUDE_FROM_ALL 87 | "${PROJECT_BINARY_DIR}/fplus_sources.generated.cpp" 88 | ${fplus_headers} 89 | ) 90 | target_link_libraries(fplus_sources PRIVATE FunctionalPlus::fplus) 91 | -------------------------------------------------------------------------------- /api_search/compile_all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./generate_database.sh 4 | cd frontend 5 | ./compile.sh 6 | ./ExploreCompile.sh 7 | -------------------------------------------------------------------------------- /api_search/compile_all_and_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./compile_all.sh 4 | cd frontend 5 | ./deploy.sh 6 | -------------------------------------------------------------------------------- /api_search/frontend/ExploreCompile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | elm make src/FPlusApiExplore.elm --output=build/js/fplus_api_explore.js 4 | 5 | if [ $? -eq 0 ] 6 | then 7 | cp ./src/explore.html ./build/explore.html 8 | cp ./src/htmlmain_explore.js ./build/js/htmlmain_explore.js 9 | cp ./src/style_explore.css ./build/style_explore.css 10 | fi -------------------------------------------------------------------------------- /api_search/frontend/TypeSignatureTestCompile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | elm make src/TypeSignatureTestMain.elm --output=build/typesignaturetest.html -------------------------------------------------------------------------------- /api_search/frontend/compile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -r -f build 4 | 5 | mkdir -p build 6 | mkdir -p build/js 7 | 8 | # todo: Use --optimize 9 | elm make src/FPlusApiSearch.elm --output=build/js/fplus_api_search.js 10 | 11 | if [ $? -eq 0 ] 12 | then 13 | cp ../../logo/fplus.png ./build/ 14 | cp -r ./src/highlight ./build/ 15 | cp ./src/style.css ./build/style.css 16 | cp ./src/favicon.png ./build/favicon.png 17 | cp ./src/htmlmain.js ./build/js/htmlmain.js 18 | cp ./src/index.html ./build/index.html 19 | fi 20 | 21 | ./ExploreCompile.sh 22 | ./TypeSignatureTestCompile.sh 23 | -------------------------------------------------------------------------------- /api_search/frontend/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src" 5 | ], 6 | "elm-version": "0.19.1", 7 | "dependencies": { 8 | "direct": { 9 | "andre-dietrich/parser-combinators": "4.0.0", 10 | "elm/browser": "1.0.2", 11 | "elm/core": "1.0.5", 12 | "elm/html": "1.0.0", 13 | "elm/regex": "1.0.0", 14 | "elm-community/list-extra": "8.2.4", 15 | "elm-explorations/markdown": "1.0.0" 16 | }, 17 | "indirect": { 18 | "elm/json": "1.1.3", 19 | "elm/time": "1.0.0", 20 | "elm/url": "1.0.0", 21 | "elm/virtual-dom": "1.0.2", 22 | "pilatch/flip": "1.0.0" 23 | } 24 | }, 25 | "test-dependencies": { 26 | "direct": {}, 27 | "indirect": {} 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api_search/frontend/src/FPlusApiExplore.elm: -------------------------------------------------------------------------------- 1 | module FPlusApiExplore exposing (..) 2 | 3 | import FPlusApiCommon exposing (..) 4 | import Browser 5 | import Database 6 | import TypeSignature 7 | import Html exposing (..) 8 | import Html.Attributes exposing (..) 9 | import Html.Events exposing (..) 10 | import Markdown 11 | import Regex 12 | import String 13 | 14 | 15 | main = 16 | Browser.element 17 | { init = init 18 | , update = update 19 | , subscriptions = subscriptions 20 | , view = view 21 | } 22 | 23 | 24 | subscriptions : Model -> Sub Msg 25 | subscriptions model = 26 | Sub.none 27 | 28 | 29 | init : () -> ( Model, Cmd Msg ) 30 | init _ = 31 | ( defaultModel, Cmd.none ) 32 | 33 | 34 | type alias Model = 35 | { allFunctions : List Function 36 | } 37 | 38 | 39 | defaultModel : Model 40 | defaultModel = 41 | { allFunctions = functions 42 | } 43 | 44 | 45 | update : Msg -> Model -> ( Model, Cmd Msg ) 46 | update action model = 47 | case action of 48 | NoOp -> 49 | ( model, Cmd.none ) 50 | 51 | UpdateQuery str -> 52 | ( model, Cmd.none ) 53 | 54 | 55 | view : Model -> Html Msg 56 | view model = 57 | div [ class "mainwrapper" ] 58 | [ let 59 | url = 60 | "https://github.com/Dobiasd/FunctionalPlus/" 61 | in 62 | div [ class "main" ] 63 | [ div [ class "githublink" ] 64 | [ a [ href url ] 65 | [ img [ class "logo", src "fplus.png" ] [] 66 | ] 67 | , p [] [ a [ href url ] [ text url ] ] 68 | ] 69 | , hr [] [] 70 | , model.allFunctions |> showFunctions 71 | , hr [] [] 72 | , showFooter 73 | ] 74 | ] 75 | 76 | 77 | showFooter : Html Msg 78 | showFooter = 79 | footer [ class "footer" ] 80 | [ text "Copyright © 2017 Tobias Hermann. All rights reserved." 81 | ] 82 | 83 | 84 | showNonRatedFunction : Function -> Html Msg 85 | showNonRatedFunction function = 86 | div [ class "function" ] ( showFunctionDivs function ) 87 | 88 | 89 | showFunctions : List Function -> Html Msg 90 | showFunctions ratedFunctions = 91 | List.map showNonRatedFunction ratedFunctions 92 | |> List.intersperse (hr [] []) 93 | |> div [ class "functions" ] 94 | -------------------------------------------------------------------------------- /api_search/frontend/src/TypeSignatureTestMain.elm: -------------------------------------------------------------------------------- 1 | module TypeSignatureTestMain exposing (..) 2 | 3 | import Browser 4 | import TypeSignature 5 | import Html exposing (..) 6 | import Html.Attributes exposing (..) 7 | import Html.Events exposing (..) 8 | import Maybe 9 | import Result 10 | import String 11 | 12 | 13 | main = 14 | Browser.sandbox 15 | { init = init 16 | , update = update 17 | , view = view 18 | } 19 | 20 | 21 | type alias Model = 22 | String 23 | 24 | 25 | init : Model 26 | init = 27 | "" 28 | 29 | 30 | type Msg 31 | = UpdateStr String 32 | 33 | 34 | update : Msg -> Model -> Model 35 | update action model = 36 | case action of 37 | UpdateStr str -> 38 | str 39 | 40 | 41 | showMaybeSig : Maybe TypeSignature.Signature -> String 42 | showMaybeSig maybeSig = 43 | case maybeSig of 44 | Maybe.Just s -> 45 | TypeSignature.showSignature True s 46 | 47 | Maybe.Nothing -> 48 | "" 49 | 50 | 51 | view : Model -> Html Msg 52 | view str = 53 | let 54 | maybeSignature = 55 | str |> TypeSignature.parseSignature 56 | 57 | maybeSignatureNormalized = 58 | maybeSignature |> Maybe.map TypeSignature.normalizeSignature 59 | 60 | signatureString = 61 | maybeSignatureNormalized |> showMaybeSig 62 | 63 | maybeParsedAgainSignature = 64 | signatureString 65 | |> TypeSignature.parseSignature 66 | 67 | signatureAgainString = 68 | showMaybeSig maybeParsedAgainSignature 69 | 70 | isIdempotent = 71 | signatureString == signatureAgainString 72 | in 73 | div [] 74 | [ input 75 | [ placeholder "please enter type signature" 76 | , autofocus True 77 | , style "width" "500px" 78 | , onInput UpdateStr 79 | ] 80 | [] 81 | , div [ style "margin" "10px" ] 82 | [ "parse result: " ++ (maybeSignature |> Debug.toString) |> text ] 83 | , div [ style "margin" "10px" ] 84 | [ "parse result: " 85 | ++ (maybeSignatureNormalized |> Debug.toString) 86 | |> text 87 | ] 88 | , div [] [ "as string: " ++ signatureString |> text ] 89 | , if not isIdempotent then 90 | div [] 91 | [ div [] [ "Error: Parsing was not isIdempotent!" |> text ] 92 | , div [] [ signatureAgainString |> text ] 93 | ] 94 | else 95 | div [] [] 96 | ] 97 | -------------------------------------------------------------------------------- /api_search/frontend/src/explore.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FunctionalPlus API explore 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /api_search/frontend/src/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dobiasd/FunctionalPlus/5a951682e45037fa5046fa6b47e9019d0fc55763/api_search/frontend/src/favicon.png -------------------------------------------------------------------------------- /api_search/frontend/src/highlight/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006, Ivan Sagalaev 2 | All rights reserved. 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of highlight.js nor the names of its contributors 12 | may be used to endorse or promote products derived from this software 13 | without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 16 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /api_search/frontend/src/highlight/styles/default.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Original highlight.js style (c) Ivan Sagalaev 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #F0F0F0; 12 | } 13 | 14 | 15 | /* Base color: saturation 0; */ 16 | 17 | .hljs, 18 | .hljs-subst { 19 | color: #444; 20 | } 21 | 22 | .hljs-comment { 23 | color: #888888; 24 | } 25 | 26 | .hljs-keyword, 27 | .hljs-attribute, 28 | .hljs-selector-tag, 29 | .hljs-meta-keyword, 30 | .hljs-doctag, 31 | .hljs-name { 32 | font-weight: bold; 33 | } 34 | 35 | 36 | /* User color: hue: 0 */ 37 | 38 | .hljs-type, 39 | .hljs-string, 40 | .hljs-number, 41 | .hljs-selector-id, 42 | .hljs-selector-class, 43 | .hljs-quote, 44 | .hljs-template-tag, 45 | .hljs-deletion { 46 | color: #880000; 47 | } 48 | 49 | .hljs-title, 50 | .hljs-section { 51 | color: #880000; 52 | font-weight: bold; 53 | } 54 | 55 | .hljs-regexp, 56 | .hljs-symbol, 57 | .hljs-variable, 58 | .hljs-template-variable, 59 | .hljs-link, 60 | .hljs-selector-attr, 61 | .hljs-selector-pseudo { 62 | color: #BC6060; 63 | } 64 | 65 | 66 | /* Language color: hue: 90; */ 67 | 68 | .hljs-literal { 69 | color: #78A960; 70 | } 71 | 72 | .hljs-built_in, 73 | .hljs-bullet, 74 | .hljs-code, 75 | .hljs-addition { 76 | color: #397300; 77 | } 78 | 79 | 80 | /* Meta color: hue: 200 */ 81 | 82 | .hljs-meta { 83 | color: #1f7199; 84 | } 85 | 86 | .hljs-meta-string { 87 | color: #4d99bf; 88 | } 89 | 90 | 91 | /* Misc effects */ 92 | 93 | .hljs-emphasis { 94 | font-style: italic; 95 | } 96 | 97 | .hljs-strong { 98 | font-weight: bold; 99 | } 100 | -------------------------------------------------------------------------------- /api_search/frontend/src/highlight/styles/monokai-sublime.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/ 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #23241f; 12 | } 13 | 14 | .hljs, 15 | .hljs-tag, 16 | .hljs-subst { 17 | color: #f8f8f2; 18 | } 19 | 20 | .hljs-strong, 21 | .hljs-emphasis { 22 | color: #a8a8a2; 23 | } 24 | 25 | .hljs-bullet, 26 | .hljs-quote, 27 | .hljs-number, 28 | .hljs-regexp, 29 | .hljs-literal, 30 | .hljs-link { 31 | color: #ae81ff; 32 | } 33 | 34 | .hljs-code, 35 | .hljs-title, 36 | .hljs-section, 37 | .hljs-selector-class { 38 | color: #a6e22e; 39 | } 40 | 41 | .hljs-strong { 42 | font-weight: bold; 43 | } 44 | 45 | .hljs-emphasis { 46 | font-style: italic; 47 | } 48 | 49 | .hljs-keyword, 50 | .hljs-selector-tag, 51 | .hljs-name, 52 | .hljs-attr { 53 | color: #f92672; 54 | } 55 | 56 | .hljs-symbol, 57 | .hljs-attribute { 58 | color: #66d9ef; 59 | } 60 | 61 | .hljs-params, 62 | .hljs-class .hljs-title { 63 | color: #f8f8f2; 64 | } 65 | 66 | .hljs-string, 67 | .hljs-type, 68 | .hljs-built_in, 69 | .hljs-builtin-name, 70 | .hljs-selector-id, 71 | .hljs-selector-attr, 72 | .hljs-selector-pseudo, 73 | .hljs-addition, 74 | .hljs-variable, 75 | .hljs-template-variable { 76 | color: #e6db74; 77 | } 78 | 79 | .hljs-comment, 80 | .hljs-deletion, 81 | .hljs-meta { 82 | color: #75715e; 83 | } 84 | -------------------------------------------------------------------------------- /api_search/frontend/src/htmlmain.js: -------------------------------------------------------------------------------- 1 | function init() { 2 | var mainDiv = document.getElementById('main'); 3 | elmContent = Elm.FPlusApiSearch.init({ node: mainDiv }); 4 | } -------------------------------------------------------------------------------- /api_search/frontend/src/htmlmain_explore.js: -------------------------------------------------------------------------------- 1 | function init() { 2 | var mainDiv = document.getElementById('main'); 3 | elmContent = Elm.FPlusApiExplore.init({ node: mainDiv }); 4 | } -------------------------------------------------------------------------------- /api_search/frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FunctionalPlus API search 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /api_search/frontend/src/style.css: -------------------------------------------------------------------------------- 1 | body 2 | { 3 | color: #f8f8f2; 4 | background-color: #23241f; 5 | font-size: 110%; 6 | font-family: monospace; 7 | } 8 | 9 | input 10 | { 11 | font-size: 110%; 12 | } 13 | 14 | pre { 15 | padding: 0px; 16 | border: 0px; 17 | margin: 0px; 18 | } 19 | 20 | code { 21 | white-space: pre; 22 | padding: 0px; 23 | border: 0px; 24 | margin: 0px; 25 | } 26 | 27 | .mainwrapper 28 | { 29 | } 30 | 31 | .main 32 | { 33 | margin: 0 auto; 34 | width: 800px; 35 | } 36 | 37 | .logo 38 | { 39 | margin: 4px; 40 | } 41 | 42 | @media only screen and (max-height: 740px) 43 | { 44 | .logo 45 | { 46 | margin: -176px 0 0 0px; 47 | } 48 | } 49 | 50 | .githublink 51 | { 52 | text-align: center; 53 | } 54 | 55 | .githublink a:link { color: #f8f8f2 } 56 | .githublink a:visited { color: #f8f8f2 } 57 | .githublink a:hover { color: #f8f8f2 } 58 | .githublink a:active { color: #f8f8f2 } 59 | 60 | .functionnameandsig 61 | { 62 | } 63 | 64 | .parsedsignature 65 | { 66 | color: #75715e; 67 | margin: 8px; 68 | } 69 | 70 | .queryhelper 71 | { 72 | color: #75715e; 73 | margin: 8px; 74 | } 75 | 76 | .functionnameandsig 77 | { 78 | } 79 | 80 | .functiondoc 81 | { 82 | } 83 | 84 | .functiondecl 85 | { 86 | } 87 | 88 | .functionrating 89 | { 90 | color: #75715e; 91 | } 92 | 93 | .footer 94 | { 95 | } 96 | -------------------------------------------------------------------------------- /api_search/frontend/src/style_explore.css: -------------------------------------------------------------------------------- 1 | body 2 | { 3 | font-family: monospace; 4 | } 5 | 6 | pre { 7 | padding: 0px; 8 | border: 0px; 9 | margin: 0px; 10 | } 11 | 12 | code { 13 | white-space: pre; 14 | padding: 0px; 15 | border: 0px; 16 | margin: 0px; 17 | } 18 | 19 | .mainwrapper 20 | { 21 | } 22 | 23 | .main 24 | { 25 | margin: 0 auto; 26 | width: 800px; 27 | } 28 | 29 | .logo 30 | { 31 | margin: 4px; 32 | } 33 | 34 | @media only screen and (max-height: 740px) 35 | { 36 | .logo 37 | { 38 | margin: -176px 0 0 0px; 39 | } 40 | } 41 | 42 | .githublink 43 | { 44 | text-align: center; 45 | } 46 | 47 | .functionnameandsig 48 | { 49 | } 50 | 51 | .functionnameandsig 52 | { 53 | } 54 | 55 | .functiondoc 56 | { 57 | } 58 | 59 | .function 60 | { 61 | margin-top: 16px; 62 | margin-bottom: 16px; 63 | } 64 | 65 | .footer 66 | { 67 | } 68 | -------------------------------------------------------------------------------- /api_search/generate_database.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | clang++ -O3 -std=c++14 -Wall -Wextra -pedantic -Wshadow -Werror -Weffc++ -Wconversion -Wsign-conversion -Wctor-dtor-privacy -Wreorder -Wold-style-cast -Wparentheses -pthread -o ./temp_FunctionalPlus_api__clang -I../include parse_source_files.cpp 4 | 5 | if [ -f ./temp_FunctionalPlus_api__clang ]; 6 | then 7 | g++ -E -CC -std=c++14 -w -I../include ../include/fplus/fplus.hpp > temp_preprocessor_output.txt 8 | ./temp_FunctionalPlus_api__clang temp_preprocessor_output.txt 9 | rm ./temp_FunctionalPlus_api__clang 10 | rm -f temp_preprocessor_output.txt 11 | fi 12 | -------------------------------------------------------------------------------- /cmake/FunctionalPlusConfig.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | find_dependency(Threads) 3 | 4 | include("${CMAKE_CURRENT_LIST_DIR}/FunctionalPlusTargets.cmake") 5 | -------------------------------------------------------------------------------- /cmake/install-rules.cmake: -------------------------------------------------------------------------------- 1 | include(CMakePackageConfigHelpers) 2 | include(GNUInstallDirs) 3 | 4 | # Create an export set for the CMake package 5 | install( 6 | TARGETS fplus 7 | EXPORT FunctionalPlusTargets 8 | INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 9 | ) 10 | 11 | # Dev component name, useful if the client embeds the library into their build 12 | # tree and want to install only their own files 13 | set(fplus_component FunctionalPlus_Development) 14 | 15 | # Install the include/fplus directory to a location that was added to the 16 | # export set and the include directories of its target above 17 | install( 18 | DIRECTORY "${PROJECT_SOURCE_DIR}/include/fplus" # no trailing slash 19 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 20 | COMPONENT "${fplus_component}" 21 | ) 22 | 23 | # Use the PROJECT_VERSION variable to generate a basic config version file in 24 | # the PROJECT_BINARY_DIR directory 25 | write_basic_package_version_file( 26 | FunctionalPlusConfigVersion.cmake 27 | COMPATIBILITY SameMajorVersion 28 | ARCH_INDEPENDENT 29 | ) 30 | 31 | # Location of the CMake package that find_package() can find 32 | set( 33 | FunctionalPlus_INSTALL_CMAKEDIR 34 | "${CMAKE_INSTALL_LIBDIR}/cmake/FunctionalPlus" 35 | CACHE STRING "CMake package config location relative to the install prefix" 36 | ) 37 | 38 | # This variable is a customization point for package managers like vcpkg 39 | mark_as_advanced(FunctionalPlus_INSTALL_CMAKEDIR) 40 | 41 | install( 42 | FILES 43 | "${PROJECT_SOURCE_DIR}/cmake/FunctionalPlusConfig.cmake" 44 | "${PROJECT_BINARY_DIR}/FunctionalPlusConfigVersion.cmake" 45 | DESTINATION "${FunctionalPlus_INSTALL_CMAKEDIR}" 46 | COMPONENT "${fplus_component}" 47 | ) 48 | 49 | # Generate the FunctionalPlusTargets.cmake file in the 50 | # FunctionalPlus_INSTALL_CMAKEDIR that is included by 51 | # FunctionalPlusConfig.cmake after the dependencies have been found 52 | install( 53 | EXPORT FunctionalPlusTargets 54 | NAMESPACE FunctionalPlus:: 55 | DESTINATION "${FunctionalPlus_INSTALL_CMAKEDIR}" 56 | COMPONENT "${fplus_component}" 57 | ) 58 | 59 | # Conditionally include the CPack module only if this is the top project, so 60 | # appropriate targets and files are generated for packaging the library 61 | if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) 62 | include(CPack) 63 | endif () 64 | -------------------------------------------------------------------------------- /cmake/root-project.cmake: -------------------------------------------------------------------------------- 1 | # This file is to be used by subprojects that make use of the library and uses 2 | # the variable PROJECT_SOURCE_DIR from the scope of include(). 3 | 4 | include(FetchContent) 5 | FetchContent_Declare(fplus SOURCE_DIR "${PROJECT_SOURCE_DIR}/..") 6 | FetchContent_MakeAvailable(fplus) 7 | -------------------------------------------------------------------------------- /cmake/warnings.cmake: -------------------------------------------------------------------------------- 1 | if (MSVC) 2 | set( 3 | project_warnings 4 | /W4 # Set warning level 4 (/Wall is too noisy) 5 | /WX # Warnings as errors 6 | # Disabled warnings 7 | /wd4459 # declaration of 'xs' hides global declaration 8 | /wd4127 # conditional expression is constant 9 | /wd4100 # unreferenced formal parameter 10 | /wd4189 # local variable is initialized but not referenced 11 | /wd4244 # '=': conversion from 'unsigned int' to 'char', possible loss of data 12 | # Additional warnings 13 | /we4263 # member function does not override any base class virtual member function) 14 | /we4264 # no override available for virtual member function; function is hidden 15 | /we4242 # conversion from 'type1' to 'type2', possible loss of data 16 | /we4266 # no override available for virtual member function from base 'type'; function is hidden 17 | /we4302 # truncation from 'type 1' to 'type 2' 18 | /we4826 # conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior 19 | /we4905 # wide string literal cast to 'LPSTR' 20 | /we4906 # string literal cast to 'LPWSTR' 21 | /we4389 # signed/unsigned mismatch 22 | /we4239 # nonstandard extension used: 'argument': conversion from T to T & 23 | /we4928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied 24 | /we4505 # unreferenced local function has been removed 25 | /we4265 # 'class': class has virtual functions, but destructor is not virtual 26 | /we4191 # unsafe conversion from 'type of expression' to 'type required' 27 | /we4456 # declaration of variable hides previous local declaration 28 | /we4458 # declaration of variable hides class member 29 | # /we4711 # The compiler performed inlining on the given function, although it was not marked for inlining 30 | ) 31 | else () 32 | set( 33 | project_warnings 34 | -Wall 35 | -Wextra 36 | -pedantic 37 | -Wshadow 38 | -Werror 39 | -Weffc++ 40 | -Wconversion 41 | -Wsign-conversion 42 | -Wctor-dtor-privacy 43 | -Wreorder 44 | -Wold-style-cast 45 | -Wparentheses 46 | ) 47 | endif () 48 | -------------------------------------------------------------------------------- /conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | doctest/2.4.11 3 | 4 | [generators] 5 | CMakeToolchain 6 | CMakeDeps 7 | -------------------------------------------------------------------------------- /examples/99_problems.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | using namespace fplus; 9 | 10 | typedef std::vector Ints; 11 | typedef std::vector Intss; 12 | typedef std::vector Idxs; 13 | Ints xs = { 0, 2, 2, 2, 1, 3, 3, 4 }; 14 | Intss xss = { { 0, 1, 2 }, { 3, 4 } }; 15 | typedef std::pair size_t_int_pair; 16 | typedef std::vector size_t_int_pairs; 17 | size_t_int_pairs xs_run_length_encoded = { { 1, 0 }, { 3, 2 }, { 1, 1 }, { 2, 3 }, { 1, 4 } }; 18 | 19 | template 20 | void print_result(const T& x) 21 | { 22 | std::cout << show(x) << std::endl; 23 | } 24 | 25 | // P01 (*) Find the last box of a list. 26 | void problem_01() 27 | { 28 | print_result(last(xs)); 29 | } 30 | 31 | // P02 (*) Find the last but one box of a list. 32 | void problem_02() 33 | { 34 | print_result(elem_at_idx(size_of_cont(xs) - 2, xs)); 35 | } 36 | 37 | // P03 (*) Find the K'th element of a list. 38 | void problem_03() 39 | { 40 | print_result(elem_at_idx(2, xs)); 41 | } 42 | 43 | // P04 (*) Find the number of elements of a list. 44 | void problem_04() 45 | { 46 | print_result(size_of_cont(xs)); 47 | } 48 | 49 | // P05 (*) Reverse a list. 50 | void problem_05() 51 | { 52 | print_result(reverse(xs)); 53 | } 54 | 55 | // P06 (*) Find out whether a list is a palindrome. 56 | void problem_06() 57 | { 58 | print_result(xs == reverse(xs)); 59 | } 60 | 61 | // P07 (**) Flatten a nested list structure. 62 | void problem_07() 63 | { 64 | print_result(concat(xss)); 65 | } 66 | 67 | // P08 (**) Eliminate consecutive duplicates of list elements. 68 | void problem_08() 69 | { 70 | print_result(unique(xs)); 71 | } 72 | 73 | // P09 (**) Pack consecutive duplicates of list elements into sublists. 74 | void problem_09() 75 | { 76 | print_result(group(xs)); 77 | } 78 | 79 | // P10 (*) Run-length encoding of a list. 80 | void problem_10() 81 | { 82 | auto group_to_pair = [](const Ints& group) -> size_t_int_pair { 83 | return std::make_pair(size_of_cont(group), group.front()); 84 | }; 85 | print_result(transform(group_to_pair, group(xs))); 86 | } 87 | 88 | // P11 (*) Modified run-length encoding. 89 | void problem_11() 90 | { 91 | const auto modify = [](const auto& p) -> Ints { 92 | return p.first == 1 ? Ints({ p.second }) 93 | : Ints({ static_cast(p.first), p.second }); 94 | }; 95 | 96 | print_result(fwd::apply(xs, 97 | fwd::run_length_encode(), 98 | fwd::transform(modify))); 99 | } 100 | 101 | // P12 (**) Decode a run-length encoded list. 102 | void problem_12() 103 | { 104 | const auto pair_to_vec = [](const size_t_int_pair& p) -> Ints { 105 | return replicate(p.first, p.second); 106 | }; 107 | print_result(concat(transform(pair_to_vec, xs_run_length_encoded))); 108 | } 109 | 110 | // P13 (**) Run-length encoding of a list (direct solution). 111 | void problem_13() 112 | { 113 | const auto f = [](const Intss& acc, int x) -> Intss { 114 | if (is_empty(acc)) { 115 | return singleton_seq(singleton_seq(x)); 116 | } else if (size_of_cont(acc.back()) == 1 && acc.back().back() == x) { 117 | return replace_elem_at_idx(size_of_cont(acc) - 1, { 2, x }, acc); 118 | } else { 119 | if (acc.back().back() == x) { 120 | return replace_elem_at_idx( 121 | size_of_cont(acc) - 1, { acc.back().front() + 1, x }, acc); 122 | } else { 123 | return append_elem(singleton_seq(x), acc); 124 | } 125 | } 126 | }; 127 | print_result(fold_left(f, Intss(), xs)); 128 | } 129 | 130 | // P14 (*) Duplicate the elements of a list. 131 | void problem_14() 132 | { 133 | print_result(replicate_elems(2, xs)); 134 | } 135 | 136 | // P15 (**) Replicate the elements of a list a given number of times. 137 | void problem_15() 138 | { 139 | print_result(replicate_elems(3, xs)); 140 | } 141 | 142 | // P16 (**) Drop every N'th element from a list. 143 | void problem_16() 144 | { 145 | std::size_t n = 3; 146 | print_result( 147 | drop_idxs(numbers_step(std::size_t(0), size_of_cont(xs), n), xs)); 148 | } 149 | 150 | // P17 (*) Split a list into two parts; the length of the first part is given. 151 | void problem_17() 152 | { 153 | print_result(split_at_idx(2, xs)); 154 | } 155 | 156 | // P18 (**) Extract a slice from a list. 157 | void problem_18() 158 | { 159 | print_result(get_segment(2, 4 + 1, xs)); 160 | } 161 | 162 | // P19 (**) Rotate a list N places to the left. 163 | void problem_19() 164 | { 165 | print_result(apply_function_n_times(rotate_left, 3, xs)); 166 | } 167 | 168 | // P20 (*) Remove the K'th element from a list. 169 | void problem_20() 170 | { 171 | print_result(drop_idx(0, xs)); 172 | } 173 | 174 | // P21 (*) Insert an element at a given position into a list. 175 | void problem_21() 176 | { 177 | print_result(insert_at_idx(3, -1, xs)); 178 | } 179 | 180 | // P22 (*) Create a list containing all integers within a given range. 181 | void problem_22() 182 | { 183 | print_result(numbers(3, 8)); 184 | } 185 | 186 | // P23 (**) Extract a given number of randomly selected elements from a list. 187 | void problem_23() 188 | { 189 | print_result(sample(std::random_device()(), 3, xs)); 190 | } 191 | 192 | // P24 (*) Lotto: Draw N different random numbers from the set 1..M. 193 | void problem_24() 194 | { 195 | print_result(sample(std::random_device()(), 3, numbers(1, 100))); 196 | } 197 | 198 | // P25 (*) Generate a random permutation of the elements of a list. 199 | void problem_25() 200 | { 201 | print_result(shuffle(std::random_device()(), xs)); 202 | } 203 | 204 | // P26 (**) Generate the combinations of K distinct objects chosen from the N elements of a list 205 | void problem_26() 206 | { 207 | print_result(combinations(3, xs)); 208 | } 209 | 210 | // P27 (**) Group the elements of a set into disjoint subsets. 211 | void problem_27() 212 | { 213 | // todo ;) 214 | } 215 | 216 | // P28 (**) Sorting a list of lists according to length of sublists 217 | void problem_28() 218 | { 219 | print_result(sort_on(size_of_cont, xss)); 220 | } 221 | 222 | // Let's follow tradition for once and at least do the list-related problems. 223 | // L-99: Ninety-Nine Lisp Problems: http://www.ic.unicamp.br/~meidanis/courses/mc336/2006s2/funcional/L-99_Ninety-Nine_Lisp_Problems.html 224 | // P-99: Ninety-Nine Prolog Problems: https://sites.google.com/site/prologsite/prolog-problems 225 | // H-99: Ninety-Nine Haskell Problems: https://wiki.haskell.org/H-99:_Ninety-Nine_Haskell_Problems 226 | int main() 227 | { 228 | problem_01(); 229 | problem_02(); 230 | problem_03(); 231 | problem_04(); 232 | problem_05(); 233 | problem_06(); 234 | problem_07(); 235 | problem_08(); 236 | problem_09(); 237 | problem_10(); 238 | problem_11(); 239 | problem_12(); 240 | problem_13(); 241 | problem_14(); 242 | problem_15(); 243 | problem_16(); 244 | problem_17(); 245 | problem_18(); 246 | problem_19(); 247 | problem_20(); 248 | problem_21(); 249 | problem_22(); 250 | problem_23(); 251 | problem_24(); 252 | problem_25(); 253 | problem_26(); 254 | problem_27(); 255 | problem_28(); 256 | } 257 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(FunctionalPlusExamples) 4 | 5 | include(../cmake/root-project.cmake) 6 | 7 | function(add_example NAME) 8 | add_executable("${NAME}" "${NAME}.cpp") 9 | target_link_libraries("${NAME}" PRIVATE FunctionalPlus::fplus) 10 | target_compile_features("${NAME}" PRIVATE cxx_std_14) 11 | endfunction() 12 | 13 | add_example(readme_perf_examples) 14 | add_example(99_problems) 15 | -------------------------------------------------------------------------------- /examples/performance_range_v3.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | const auto times_3 = [](int i) { return 3 * i; }; 13 | const auto is_odd_int = [](int i) { return i % 2 != 0; }; 14 | const auto as_string_length = [](int i) { return std::to_string(i).size(); }; 15 | 16 | typedef std::chrono::time_point Time; 17 | 18 | for (int i = 0; i < 3; ++i) { 19 | // FunctionalPlus 20 | Time startTimeFPlus = std::chrono::system_clock::now(); 21 | using namespace fplus; 22 | const auto result_fplus = fwd::apply( 23 | numbers(0, 15000000), fwd::transform(times_3), fwd::drop_if(is_odd_int), fwd::transform(as_string_length), fwd::sum()); 24 | Time endTimeFPlus = std::chrono::system_clock::now(); 25 | std::chrono::duration elapsed_s_fplus = endTimeFPlus - startTimeFPlus; 26 | std::cout << "(check: " << result_fplus << "), elapsed time fplus: " << elapsed_s_fplus.count() << "s\n"; 27 | 28 | // range-v3 29 | Time startTimeRangev3 = std::chrono::system_clock::now(); 30 | using namespace ranges; 31 | const auto result_range_v3 = accumulate( 32 | view::ints(0) 33 | | view::take(15000000) 34 | | view::transform(times_3) 35 | | view::remove_if(is_odd_int) 36 | | view::transform(as_string_length), 37 | 0); 38 | Time endTimeRangev3 = std::chrono::system_clock::now(); 39 | std::chrono::duration elapsed_s_rangev3 = endTimeRangev3 - startTimeRangev3; 40 | std::cout << "(check: " << result_range_v3 << "), elapsed time range-v3: " << elapsed_s_rangev3.count() << "s\n"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/readme_perf_examples.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 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 | bool is_odd_int(int x) { return x % 2 != 0; } 22 | 23 | void Test_example_KeepIf() 24 | { 25 | typedef std::vector Ints; 26 | Ints values = { 24, 11, 65, 44, 80, 18, 73, 90, 69, 18 }; 27 | 28 | { // Version 1: hand written range based for loop 29 | Ints odds; 30 | for (int x : values) 31 | if (is_odd_int(x)) 32 | odds.push_back(x); 33 | } 34 | 35 | { // Version 2: STL 36 | Ints odds; 37 | std::copy_if(std::begin(values), std::end(values), 38 | std::back_inserter(odds), is_odd_int); 39 | } 40 | 41 | { // Version : FunctionalPlus 42 | auto odds = fplus::keep_if(is_odd_int, values); 43 | } 44 | } 45 | 46 | void run_n_times(std::function(std::vector)> f, 47 | std::size_t n, const std::string& name, const std::vector& inList) 48 | { 49 | typedef std::chrono::time_point Time; 50 | Time startTime = std::chrono::system_clock::now(); 51 | std::size_t lengthSum = 0; 52 | for (std::size_t i = 0; i < n; ++i) { 53 | lengthSum += f(inList).size(); 54 | } 55 | Time endTime = std::chrono::system_clock::now(); 56 | std::chrono::duration elapsed_seconds = endTime - startTime; 57 | std::cout << name << "(check: " << lengthSum << "), elapsed time: " << elapsed_seconds.count() << "s\n"; 58 | } 59 | 60 | void Test_example_KeepIf_performance() 61 | { 62 | using namespace fplus; 63 | 64 | typedef std::vector Ints; 65 | auto run_loop = [&](const Ints values) { 66 | Ints odds; 67 | for (int x : values) 68 | if (is_odd_int(x)) 69 | odds.push_back(x); 70 | return odds; 71 | }; 72 | auto run_stl = [&](const Ints values) { 73 | Ints odds; 74 | std::copy_if(std::begin(values), std::end(values), 75 | std::back_inserter(odds), is_odd_int); 76 | return odds; 77 | }; 78 | auto run_FunctionalPlus = [&](const Ints values) { return keep_if(is_odd_int, values); }; 79 | 80 | // make debug runs faster 81 | #if defined NDEBUG || defined _DEBUG 82 | std::size_t numRuns = 10; 83 | #else 84 | std::size_t numRuns = 20000; 85 | #endif 86 | 87 | Ints values = generate(rand, 5000); 88 | run_n_times(run_loop, numRuns, "Hand-written for loop", values); 89 | run_n_times(run_stl, numRuns, "std::copy_if", values); 90 | run_n_times(run_FunctionalPlus, numRuns, "FunctionalPlus::keep_if", values); 91 | } 92 | 93 | int main() 94 | { 95 | Test_example_KeepIf_performance(); 96 | } 97 | -------------------------------------------------------------------------------- /generate/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /include/fplus/curry.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | namespace fplus { 10 | namespace curry { 11 | 12 | // Currying. 13 | // Allow to generically bind parameters one by one. 14 | 15 | #define fplus_curry_define_fn_0(fplus_curry_define_fn_0_name) \ 16 | inline auto fplus_curry_define_fn_0_name() \ 17 | { \ 18 | return [](auto&& fplus_curry_p1) { \ 19 | return fplus::fplus_curry_define_fn_0_name(std::forward(fplus_curry_p1)); \ 20 | }; \ 21 | } 22 | 23 | #define fplus_curry_define_fn_1(fplus_curry_define_fn_1_name) \ 24 | template \ 25 | auto fplus_curry_define_fn_1_name(P1 p1) \ 26 | { \ 27 | return [p1](auto&& fplus_curry_p2) { \ 28 | return fplus::fplus_curry_define_fn_1_name(p1, std::forward(fplus_curry_p2)); \ 29 | }; \ 30 | } 31 | 32 | #define fplus_curry_define_fn_2(fplus_curry_define_fn_2_name) \ 33 | template \ 34 | auto fplus_curry_define_fn_2_name(P1 p1) \ 35 | { \ 36 | return [p1](const auto& fplus_curry_p2) { \ 37 | return [p1, fplus_curry_p2](auto&& fplus_curry_p3) { \ 38 | return fplus::fplus_curry_define_fn_2_name(p1, fplus_curry_p2, std::forward(fplus_curry_p3)); \ 39 | }; \ 40 | }; \ 41 | } 42 | 43 | #define fplus_curry_define_fn_3(fplus_curry_define_fn_3_name) \ 44 | template \ 45 | auto fplus_curry_define_fn_3_name(P1 p1) \ 46 | { \ 47 | return [p1](const auto& fplus_curry_p2) { \ 48 | return [p1, fplus_curry_p2](const auto& fplus_curry_p3) { \ 49 | return [p1, fplus_curry_p2, fplus_curry_p3](auto&& fplus_curry_p4) { \ 50 | return fplus::fplus_curry_define_fn_3_name(p1, fplus_curry_p2, fplus_curry_p3, std::forward(fplus_curry_p4)); \ 51 | }; \ 52 | }; \ 53 | }; \ 54 | } 55 | 56 | #define fplus_curry_define_fn_4(fplus_curry_define_fn_4_name) \ 57 | template \ 58 | auto fplus_curry_define_fn_4_name(P1 p1) \ 59 | { \ 60 | return [p1](const auto& fplus_curry_p2) { \ 61 | return [p1, fplus_curry_p2](const auto& fplus_curry_p3) { \ 62 | return [p1, fplus_curry_p2, fplus_curry_p3](const auto& fplus_curry_p4) { \ 63 | return [p1, fplus_curry_p2, fplus_curry_p3, fplus_curry_p4](auto&& fplus_curry_p5) { \ 64 | return fplus::fplus_curry_define_fn_4_name(p1, fplus_curry_p2, fplus_curry_p3, fplus_curry_p4, std::forward(fplus_curry_p5)); \ 65 | }; \ 66 | }; \ 67 | }; \ 68 | }; \ 69 | } 70 | 71 | #include "curry_instances.autogenerated_defines" 72 | 73 | } // namespace curry 74 | } // namespace fplus 75 | -------------------------------------------------------------------------------- /include/fplus/extrapolate.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace fplus { 13 | 14 | // API search type: elem_at_idx_or_nothing : (Int, [a]) -> Maybe a 15 | // fwd bind count: 1 16 | // Return nth element of a sequence. 17 | // Returns nothing if index is outside of xs. 18 | template 20 | maybe elem_at_idx_or_nothing(signed int idx, const Container& xs) 21 | { 22 | if (idx < 0 || idx >= static_cast(size_of_cont(xs))) { 23 | return {}; 24 | } 25 | auto it = std::begin(xs); 26 | internal::advance_iterator(it, static_cast(idx)); 27 | return *it; 28 | } 29 | 30 | // API search type: elem_at_idx_or_constant : (a, Int, [a]) -> a 31 | // fwd bind count: 2 32 | // Return nth element of a sequence. 33 | // Interpolate outside of sequence with a constant value. 34 | // iiiiii|abcdefgh|iiiiiii 35 | template 37 | T elem_at_idx_or_constant(const T& c, signed int idx, const Container& xs) 38 | { 39 | if (idx < 0 || idx >= static_cast(size_of_cont(xs))) { 40 | return c; 41 | } 42 | auto it = std::begin(xs); 43 | internal::advance_iterator(it, static_cast(idx)); 44 | return *it; 45 | } 46 | 47 | // API search type: elem_at_idx_or_replicate : (Int, [a]) -> a 48 | // fwd bind count: 1 49 | // Return nth element of a sequence. 50 | // Interpolate outside of sequence by replicating the nearest inside value. 51 | // aaaaaa|abcdefgh|hhhhhhh 52 | // xs must be non-empty. 53 | template 55 | T elem_at_idx_or_replicate(signed int idx, const Container& xs) 56 | { 57 | assert(is_not_empty(xs)); 58 | if (idx < 0) { 59 | return xs.front(); 60 | } 61 | if (idx >= static_cast(size_of_cont(xs))) { 62 | return xs.back(); 63 | } 64 | auto it = std::begin(xs); 65 | internal::advance_iterator(it, static_cast(idx)); 66 | return *it; 67 | } 68 | 69 | // API search type: elem_at_idx_or_wrap : (Int, [a]) -> a 70 | // fwd bind count: 1 71 | // Return nth element of a sequence. 72 | // Interpolate outside of sequence by replicating the sequence. 73 | // For cyclic element access. 74 | // cdefgh|abcdefgh|abcdefg 75 | // xs must be non-empty. 76 | template 78 | T elem_at_idx_or_wrap(signed int idx, const Container& xs) 79 | { 80 | assert(is_not_empty(xs)); 81 | const signed int cont_size = static_cast(size_of_cont(xs)); 82 | if (idx < 0) 83 | idx = cont_size - (std::abs(idx) % cont_size); 84 | else 85 | idx = idx % cont_size; 86 | auto it = std::begin(xs); 87 | internal::advance_iterator(it, static_cast(idx)); 88 | return *it; 89 | } 90 | 91 | // API search type: extrapolate_replicate : (Int, Int, [a]) -> [a] 92 | // fwd bind count: 2 93 | // Extrapolate a sequence by replicating the border values. 94 | // count_begin determines the number of elements to be prepended. 95 | // count_end determines the number of elements to be appended. 96 | // aaaaaa|abcdefgh|hhhhhhh 97 | // xs must be non-empty. 98 | template 100 | Container extrapolate_replicate(std::size_t count_begin, std::size_t count_end, 101 | const Container& xs) 102 | { 103 | assert(is_not_empty(xs)); 104 | Container ys; 105 | const auto xs_size = size_of_cont(xs); 106 | internal::prepare_container(ys, xs_size + count_begin + count_end); 107 | auto it = internal::get_back_inserter(ys); 108 | const signed int idx_end = static_cast(xs_size + count_end); 109 | const signed int idx_start = -static_cast(count_begin); 110 | for (signed int idx = idx_start; idx < idx_end; ++idx) { 111 | *it = elem_at_idx_or_replicate(idx, xs); 112 | } 113 | return ys; 114 | } 115 | 116 | // API search type: extrapolate_wrap : (Int, Int, [a]) -> [a] 117 | // fwd bind count: 2 118 | // Extrapolate a sequence by accessing the elements in cyclic fashion. 119 | // count_begin determines the number of elements to be prepended. 120 | // count_end determines the number of elements to be appended. 121 | // cdefgh|abcdefgh|abcdefg 122 | // xs must be non-empty. 123 | template 125 | Container extrapolate_wrap(std::size_t count_begin, std::size_t count_end, 126 | const Container& xs) 127 | { 128 | assert(is_not_empty(xs)); 129 | Container ys; 130 | const auto xs_size = size_of_cont(xs); 131 | internal::prepare_container(ys, xs_size + count_begin + count_end); 132 | auto it = internal::get_back_inserter(ys); 133 | const signed int idx_end = static_cast(xs_size + count_end); 134 | const signed int idx_start = -static_cast(count_begin); 135 | for (signed int idx = idx_start; idx < idx_end; ++idx) { 136 | *it = elem_at_idx_or_wrap(idx, xs); 137 | } 138 | return ys; 139 | } 140 | 141 | } // namespace fplus 142 | -------------------------------------------------------------------------------- /include/fplus/fplus.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 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 | 42 | #include 43 | #include 44 | -------------------------------------------------------------------------------- /include/fplus/fwd.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | namespace fplus { 10 | namespace fwd { 11 | 12 | // Partial currying. 13 | // Allow to generically bind all but parameters except the last one. 14 | // The lambda paramter ist named fplus_fwd_x instead of x 15 | // because gcc can produce unjustified shadow warnings. see: 16 | // http://stackoverflow.com/questions/41208811/parameter-of-returned-generic-lambda-allegedly-shadows-parameter-of-free-functio 17 | #define fplus_fwd_define_fn_0(fplus_fwd_define_fn_0_name) \ 18 | inline auto fplus_fwd_define_fn_0_name() \ 19 | { \ 20 | return [](auto&& fplus_fwd_x) { \ 21 | return fplus::fplus_fwd_define_fn_0_name(std::forward(fplus_fwd_x)); \ 22 | }; \ 23 | } 24 | 25 | #define fplus_fwd_define_fn_1(fplus_fwd_define_fn_1_name) \ 26 | template \ 27 | auto fplus_fwd_define_fn_1_name(P1 p1) \ 28 | { \ 29 | return [p1](auto&& fplus_fwd_x) { \ 30 | return fplus::fplus_fwd_define_fn_1_name(p1, std::forward(fplus_fwd_x)); \ 31 | }; \ 32 | } 33 | 34 | #define fplus_fwd_define_fn_2(fplus_fwd_define_fn_2_name) \ 35 | template \ 36 | auto fplus_fwd_define_fn_2_name(P1 p1, P2 p2) \ 37 | { \ 38 | return [p1, p2](auto&& fplus_fwd_x) { \ 39 | return fplus::fplus_fwd_define_fn_2_name(p1, p2, std::forward(fplus_fwd_x)); \ 40 | }; \ 41 | } 42 | 43 | #define fplus_fwd_define_fn_3(fplus_fwd_define_fn_3_name) \ 44 | template \ 45 | auto fplus_fwd_define_fn_3_name(P1 p1, P2 p2, P3 p3) \ 46 | { \ 47 | return [p1, p2, p3](auto&& fplus_fwd_x) { \ 48 | return fplus::fplus_fwd_define_fn_3_name(p1, p2, p3, std::forward(fplus_fwd_x)); \ 49 | }; \ 50 | } 51 | 52 | #define fplus_fwd_define_fn_4(fplus_fwd_define_fn_4_name) \ 53 | template \ 54 | auto fplus_fwd_define_fn_4_name(P1 p1, P2 p2, P3 p3, P4 p4) \ 55 | { \ 56 | return [p1, p2, p3, p4](auto&& fplus_fwd_x) { \ 57 | return fplus::fplus_fwd_define_fn_4_name(p1, p2, p3, p4, std::forward(fplus_fwd_x)); \ 58 | }; \ 59 | } 60 | 61 | #define fplus_fwd_flip_define_fn_1(fplus_fwd_flip_define_fn_1_name) \ 62 | namespace flip { \ 63 | template \ 64 | auto fplus_fwd_flip_define_fn_1_name(P2 p2) \ 65 | { \ 66 | return [p2](auto&& fplus_fwd_flip_x) { \ 67 | return fplus::fplus_fwd_flip_define_fn_1_name(std::forward(fplus_fwd_flip_x), p2); \ 68 | }; \ 69 | } \ 70 | } // namespace flip 71 | 72 | namespace internal { 73 | template 74 | struct compose_helper { 75 | compose_helper(F f, G g) 76 | : f_(f) 77 | , g_(g) 78 | { 79 | } 80 | template 81 | decltype(auto) operator()(X&& x) const 82 | { 83 | return g_(f_(std::forward(x))); 84 | } 85 | 86 | private: 87 | F f_; 88 | G g_; 89 | }; 90 | } // namespace internal 91 | template 92 | auto compose(F f, G g) 93 | { 94 | return internal::compose_helper { f, g }; 95 | } 96 | template 97 | auto compose(F1 f, Fs... args) 98 | { 99 | return compose(f, compose(args...)); 100 | } 101 | 102 | template 103 | auto apply(X&& x, Fs... args) 104 | { 105 | return compose(args...)(std::forward(x)); 106 | } 107 | template 108 | auto apply(X&& x, F f) 109 | { 110 | return f(std::forward(x)); 111 | } 112 | 113 | #include "fwd_instances.autogenerated_defines" 114 | 115 | } // namespace fwd 116 | } // namespace fplus 117 | -------------------------------------------------------------------------------- /include/fplus/internal/apply.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace fplus { 16 | namespace internal { 17 | // C++17 std::apply (http://en.cppreference.com/w/cpp/utility/apply) 18 | template 19 | constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence) 20 | { 21 | return internal::invoke(std::forward(f), 22 | std::get(std::forward(t))...); 23 | } 24 | 25 | template 26 | constexpr decltype(auto) apply(F&& f, Tuple&& t) 27 | { 28 | return internal::apply_impl( 29 | std::forward(f), 30 | std::forward(t), 31 | std::make_index_sequence< 32 | std::tuple_size>::value> {}); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /include/fplus/internal/asserts/composition.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace fplus { 12 | namespace internal { 13 | 14 | struct bind_1st_of_2_tag { 15 | }; 16 | 17 | struct bind_2nd_of_2_tag { 18 | }; 19 | 20 | struct bind_1st_of_3_tag { 21 | }; 22 | 23 | struct bind_1st_and_2nd_of_3_tag { 24 | }; 25 | 26 | struct bind_2nd_and_3rd_of_3_tag { 27 | }; 28 | 29 | template 30 | struct function_traits_asserts { 31 | static_assert(utils::function_traits::arity == 2, 32 | "Function must take two parameters."); 33 | typedef typename utils::function_traits::template arg<0>::type FIn0; 34 | typedef typename utils::function_traits::template arg<1>::type FIn1; 35 | static_assert(std::is_convertible::value, 36 | "Function can not take bound parameter type"); 37 | static_assert(std::is_convertible::value, 38 | "Function can not take provided parameter type"); 39 | }; 40 | 41 | template 42 | struct function_traits_asserts { 43 | static_assert(utils::function_traits::arity == 2, 44 | "Function must take two parameters."); 45 | typedef typename utils::function_traits::template arg<0>::type FIn0; 46 | typedef typename utils::function_traits::template arg<1>::type FIn1; 47 | static_assert(std::is_convertible::value, 48 | "Function can not take provided parameter type"); 49 | static_assert(std::is_convertible::value, 50 | "Function can not take bound parameter type"); 51 | }; 52 | 53 | template 54 | struct function_traits_asserts { 55 | static_assert(utils::function_traits::arity == 3, 56 | "Function must take three parameters."); 57 | typedef typename utils::function_traits::template arg<0>::type FIn0; 58 | typedef typename utils::function_traits::template arg<1>::type FIn1; 59 | typedef typename utils::function_traits::template arg<2>::type FIn2; 60 | static_assert(std::is_convertible::value, 61 | "Function can not take bound parameter type"); 62 | static_assert(std::is_convertible::value, 63 | "Function can not take provided first parameter type"); 64 | static_assert(std::is_convertible::value, 65 | "Function can not take provided second parameter type"); 66 | }; 67 | 68 | template 69 | struct function_traits_asserts { 70 | static_assert(utils::function_traits::arity == 3, 71 | "Function must take three parameters."); 72 | typedef typename utils::function_traits::template arg<0>::type FIn0; 73 | typedef typename utils::function_traits::template arg<1>::type FIn1; 74 | typedef typename utils::function_traits::template arg<2>::type FIn2; 75 | static_assert(std::is_convertible::value, 76 | "Function can not take first bound parameter type"); 77 | static_assert(std::is_convertible::value, 78 | "Function can not take second bound parameter type"); 79 | static_assert(std::is_convertible::value, 80 | "Function can not take provided parameter type"); 81 | }; 82 | 83 | template 84 | struct function_traits_asserts { 85 | static_assert(utils::function_traits::arity == 3, 86 | "Function must take three parameters."); 87 | typedef typename utils::function_traits::template arg<0>::type FIn0; 88 | typedef typename utils::function_traits::template arg<1>::type FIn1; 89 | typedef typename utils::function_traits::template arg<2>::type FIn2; 90 | static_assert(std::is_convertible::value, 91 | "Function can not take provided parameter type"); 92 | static_assert(std::is_convertible::value, 93 | "Function can not take second bound parameter type"); 94 | static_assert(std::is_convertible::value, 95 | "Function can not take first bound parameter type"); 96 | }; 97 | 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /include/fplus/internal/asserts/functions.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace fplus { 12 | namespace internal { 13 | 14 | struct nullary_function_tag { 15 | }; 16 | 17 | struct unary_function_tag { 18 | }; 19 | 20 | struct binary_function_tag { 21 | }; 22 | 23 | struct binary_predicate_tag { 24 | }; 25 | 26 | struct check_arity_tag { 27 | }; 28 | 29 | template 30 | struct function_traits_asserts { 31 | static_assert(utils::function_traits::arity == 0, 32 | "Function must take no parameters."); 33 | }; 34 | 35 | template 36 | struct function_traits_asserts { 37 | static_assert(utils::function_traits::arity == 1, 38 | "Function must take one parameter."); 39 | typedef typename utils::function_traits::template arg<0>::type FIn0; 40 | static_assert(std::is_convertible::value, 41 | "Invalid argument type for function"); 42 | }; 43 | 44 | template 45 | struct function_traits_asserts { 46 | static_assert(utils::function_traits::arity == 2, 47 | "Function must take two parameters."); 48 | }; 49 | 50 | template 51 | struct function_traits_asserts { 52 | static_assert(utils::function_traits::arity == 2, 53 | "Function must take two parameters."); 54 | typedef typename utils::function_traits::template arg<0>::type FIn0; 55 | static_assert(std::is_convertible::value, 56 | "Invalid first argument type for function"); 57 | typedef typename utils::function_traits::template arg<1>::type FIn1; 58 | static_assert(std::is_convertible::value, 59 | "Invalid second argument type for function"); 60 | }; 61 | 62 | template 63 | struct function_traits_asserts { 64 | static_assert(utils::function_traits::arity == 2, 65 | "Function must take two parameters."); 66 | typedef typename utils::function_traits::template arg<0>::type FIn0; 67 | typedef typename utils::function_traits::template arg<1>::type FIn1; 68 | static_assert(std::is_same::value, 69 | "Both parameters must have the same type."); 70 | static_assert(std::is_same>, bool>::value, 71 | "Predicate must return bool."); 72 | }; 73 | 74 | template 75 | struct function_traits_asserts { 76 | static_assert(utils::function_traits::arity == sizeof...(Args), 77 | "Wrong arity."); 78 | }; 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /include/fplus/internal/asserts/pairs.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace fplus { 12 | namespace internal { 13 | struct apply_to_pair_tag { 14 | }; 15 | 16 | struct zip_with_tag { 17 | }; 18 | 19 | struct zip_with_3_tag { 20 | }; 21 | 22 | struct transform_fst_tag { 23 | }; 24 | 25 | struct transform_snd_tag { 26 | }; 27 | 28 | struct inner_product_with_tag { 29 | }; 30 | 31 | template 32 | struct function_traits_asserts { 33 | static_assert(utils::function_traits::arity == 2, 34 | "Function must take two parameters."); 35 | typedef typename utils::function_traits::template arg<0>::type FIn0; 36 | typedef typename utils::function_traits::template arg<1>::type FIn1; 37 | static_assert(std::is_convertible::value, 38 | "Function does not take pair.first type as first Parameter."); 39 | static_assert(std::is_convertible::value, 40 | "Function does not take pair.second type as second Parameter."); 41 | }; 42 | 43 | template 44 | struct function_traits_asserts { 45 | static_assert(utils::function_traits::arity == 2, 46 | "Function must take two parameters."); 47 | typedef typename utils::function_traits::template arg<0>::type FIn0; 48 | typedef typename utils::function_traits::template arg<1>::type FIn1; 49 | static_assert(std::is_convertible::value, 50 | "Function does not take elements from first Container as first Parameter."); 51 | static_assert(std::is_convertible::value, 52 | "Function does not take elements from second Container as second Parameter."); 53 | }; 54 | 55 | template 56 | struct function_traits_asserts { 57 | static_assert(utils::function_traits::arity == 3, 58 | "Function must take two parameters."); 59 | typedef typename utils::function_traits::template arg<0>::type FIn0; 60 | typedef typename utils::function_traits::template arg<1>::type FIn1; 61 | typedef typename utils::function_traits::template arg<2>::type FIn2; 62 | static_assert(std::is_convertible::value, 63 | "Function does not take elements from first Container as first Parameter."); 64 | static_assert(std::is_convertible::value, 65 | "Function does not take elements from second Container as second Parameter."); 66 | static_assert(std::is_convertible::value, 67 | "Function does not take elements from third Container as third Parameter."); 68 | }; 69 | 70 | template 71 | struct function_traits_asserts { 72 | static_assert(utils::function_traits::arity == 1, 73 | "Function must take one parameter."); 74 | typedef typename utils::function_traits::template arg<0>::type FIn0; 75 | static_assert(std::is_convertible::value, 76 | "Function does not take pair.first type as first Parameter."); 77 | }; 78 | 79 | template 80 | struct function_traits_asserts { 81 | static_assert(utils::function_traits::arity == 1, 82 | "Function must take one parameter."); 83 | typedef typename utils::function_traits::template arg<0>::type FIn0; 84 | static_assert(std::is_convertible::value, 85 | "Function does not take pair.second type as first Parameter."); 86 | }; 87 | 88 | template 89 | struct function_traits_asserts { 90 | static_assert(utils::function_traits::arity == 2, 91 | "Function must take two parameters."); 92 | typedef typename utils::function_traits::template arg<0>::type FIn0; 93 | typedef typename utils::function_traits::template arg<1>::type FIn1; 94 | static_assert(std::is_convertible::value, 95 | "Function does not take elements from first Container as first Parameter."); 96 | static_assert(std::is_convertible::value, 97 | "Function does not take elements from second Container as second Parameter."); 98 | }; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /include/fplus/internal/compare.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace fplus { 15 | namespace internal { 16 | template 17 | auto ord_to_impl(Compare comp) 18 | { 19 | return [comp](auto x, auto y) { 20 | static_assert(std::is_same::value, 21 | "Argument types must be the same"); 22 | using In = decltype(x); 23 | internal::trigger_static_asserts(); 24 | 25 | using CompareOut = std::decay_t>; 26 | static_assert(std::is_same::value, 27 | "Function must return bool."); 28 | return std::make_pair(internal::invoke(comp, x, y), 29 | internal::invoke(comp, y, x)); 30 | }; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /include/fplus/internal/composition.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace fplus { 15 | namespace internal { 16 | // source: https://codereview.stackexchange.com/a/63893 17 | // note: the code in the link above is called with the arguments in reverse order 18 | template 19 | class compose_impl { 20 | static constexpr std::size_t size = sizeof...(Fs); 21 | static_assert(size > 1, 22 | "Invalid number of functions to compose, minimum is two."); 23 | 24 | public: 25 | compose_impl(Fs&&... fs) 26 | : _functionTuple(std::forward(fs)...) 27 | { 28 | } 29 | 30 | template 31 | auto operator()(Ts&&... ts) const 32 | { 33 | return _apply(std::integral_constant {}, 34 | std::forward(ts)...); 35 | } 36 | 37 | private: 38 | template 39 | auto _apply(std::integral_constant, Ts&&... ts) const 40 | { 41 | return _apply(std::integral_constant {}, 42 | std::get(_functionTuple)(std::forward(ts)...)); 43 | } 44 | 45 | template 46 | auto _apply(std::integral_constant, Ts&&... ts) const 47 | { 48 | return internal::invoke(std::get(_functionTuple), 49 | std::forward(ts)...); 50 | } 51 | 52 | std::tuple _functionTuple; 53 | }; 54 | 55 | // Is BinaryLift really correct? 56 | template 57 | auto compose_binary_lift_impl(std::integral_constant, 58 | const Tuple& tup, 59 | const BinaryLift& lifter) 60 | { 61 | return lifter(std::get<0>(tup), std::get<1>(tup)); 62 | } 63 | 64 | template 65 | auto compose_binary_lift_impl(std::integral_constant, 66 | const Tuple& tup, 67 | const BinaryLift& lifter) 68 | { 69 | return lifter( 70 | compose_binary_lift_impl( 71 | std::integral_constant {}, tup, lifter), 72 | std::get(tup)); 73 | } 74 | 75 | template 76 | auto compose_binary_lift(const BinaryLift& lifter, Callables&&... args) 77 | { 78 | static_assert(sizeof...(Callables) > 1, 79 | "Invalid number of functions to compose, minimum is two."); 80 | const auto tup = std::forward_as_tuple(std::forward(args)...); 81 | return compose_binary_lift_impl( 82 | std::integral_constant {}, 83 | tup, 84 | lifter); 85 | } 86 | 87 | // concentrate asserts in this method. Lambda is provided by the library. 88 | template 89 | auto logical_binary_op(Lambda op, F f, G g) 90 | { 91 | // Perfect-forwarding might move twice, if we add a requirement on F and G, 92 | // that might not be an issue. 93 | return [op, f, g](auto x) { 94 | internal::trigger_static_asserts(); 97 | internal::trigger_static_asserts(); 100 | using FRes = std::decay_t>; 101 | using GRes = std::decay_t>; 102 | static_assert(std::is_same::value, "Must return bool."); 103 | static_assert(std::is_same::value, "Must return bool."); 104 | 105 | return op(f, g, x); 106 | }; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /include/fplus/internal/container_common.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace fplus { 15 | namespace internal { 16 | 17 | template 18 | T accumulate(InputIt first, InputIt last, T init) 19 | { 20 | for (; first != last; ++first) { 21 | init = std::move(init) + *first; 22 | } 23 | return init; 24 | } 25 | 26 | template 27 | T accumulate(InputIt first, InputIt last, T init, 28 | BinaryOperation op) 29 | { 30 | for (; first != last; ++first) { 31 | init = op(std::move(init), *first); 32 | } 33 | return init; 34 | } 35 | 36 | template 40 | void scan_impl(F f, 41 | const Acc& init, 42 | OutputIterator itOut, 43 | InputIterator begin, 44 | InputIterator end) 45 | { 46 | *itOut = init; 47 | 48 | auto g = [itOut, f](auto acc, auto x) mutable { 49 | acc = internal::invoke(f, acc, x); 50 | *itOut = acc; 51 | return acc; 52 | }; 53 | 54 | internal::accumulate(begin, end, init, g); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /include/fplus/internal/function_traits_asserts.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace fplus { 13 | namespace internal { 14 | template 15 | struct function_traits_asserts; 16 | 17 | template < 18 | typename, 19 | typename F, 20 | typename... Args, 21 | typename std::enable_if::value, int>::type = 0> 22 | constexpr void trigger_static_asserts() 23 | { 24 | } 25 | 26 | // Marks a variable as unused. Prevents the compiler warning 27 | // for set but unused variables. 28 | template 29 | inline void unused(T&&) { } 30 | 31 | template ::value && !is_invocable::value, 35 | int>::type 36 | = 0> 37 | constexpr void trigger_static_asserts() 38 | { 39 | // don't perform checks if function_traits doesn't exist 40 | unused(function_traits_asserts {}); 41 | } 42 | 43 | template ::value && !is_invocable::value, 47 | int>::type 48 | = 0> 49 | constexpr void trigger_static_asserts() 50 | { 51 | static_assert(sizeof(F) == 0, 52 | "F is not a Callable, or its definition is ill-formed"); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /include/fplus/internal/meta.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace fplus { 12 | namespace internal { 13 | // C++14 compatible void_t (http://en.cppreference.com/w/cpp/types/void_t) 14 | template 15 | struct make_void { 16 | using type = void; 17 | }; 18 | 19 | template 20 | using void_t = typename make_void::type; 21 | 22 | // Sometimes you don't want to use std::decay_t, and the temptation of short 23 | // writing can be huge... 24 | template 25 | using uncvref_t = std::remove_cv_t>; 26 | 27 | // disjunction/conjunction/negation, useful to short circuit SFINAE checks 28 | // Use with parsimony, MSVC 2015 can have ICEs quite easily 29 | template 30 | struct disjunction : std::false_type { 31 | }; 32 | 33 | template 34 | struct disjunction : B1 { 35 | }; 36 | 37 | template 38 | struct disjunction 39 | : std::conditional>::type { 40 | }; 41 | 42 | template 43 | struct conjunction : std::true_type { 44 | }; 45 | 46 | template 47 | struct conjunction : B1 { 48 | }; 49 | 50 | template 51 | struct conjunction 52 | : std::conditional, B1>::type { 53 | }; 54 | 55 | template 56 | struct negation : std::integral_constant { 57 | }; 58 | 59 | // non short-circuiting meta functions 60 | // source: https://stackoverflow.com/a/27221517/4116453 61 | template 62 | struct bool_pack; 63 | 64 | template 65 | struct all_of 66 | : std::is_same, bool_pack> { 67 | }; 68 | 69 | // there seems to be a bug in libc++'s std::is_function 70 | // provide our own (cppreference one) 71 | // (the MSVC implementation seems correct) 72 | #ifndef _MSC_VER 73 | #define PROVIDE_IS_FUNCTION_POLYFILL 74 | #endif 75 | 76 | #ifndef PROVIDE_IS_FUNCTION_POLYFILL 77 | template 78 | using is_function = std::is_function; 79 | #else // PROVIDE_IS_FUNCTION_POLYFILL 80 | // primary template 81 | template 82 | struct is_function : std::false_type { 83 | }; 84 | 85 | // specialization for regular functions 86 | template 87 | struct is_function : std::true_type { 88 | }; 89 | 90 | // specialization for variadic functions such as std::printf 91 | template 92 | struct is_function : std::true_type { 93 | }; 94 | 95 | // specialization for function types that have cv-qualifiers 96 | template 97 | struct is_function : std::true_type { 98 | }; 99 | template 100 | struct is_function : std::true_type { 101 | }; 102 | template 103 | struct is_function : std::true_type { 104 | }; 105 | template 106 | struct is_function : std::true_type { 107 | }; 108 | template 109 | struct is_function : std::true_type { 110 | }; 111 | template 112 | struct is_function : std::true_type { 113 | }; 114 | 115 | // specialization for function types that have ref-qualifiers 116 | template 117 | struct is_function : std::true_type { 118 | }; 119 | template 120 | struct is_function : std::true_type { 121 | }; 122 | template 123 | struct is_function : std::true_type { 124 | }; 125 | template 126 | struct is_function : std::true_type { 127 | }; 128 | template 129 | struct is_function : std::true_type { 130 | }; 131 | template 132 | struct is_function : std::true_type { 133 | }; 134 | template 135 | struct is_function : std::true_type { 136 | }; 137 | template 138 | struct is_function : std::true_type { 139 | }; 140 | template 141 | struct is_function : std::true_type { 142 | }; 143 | template 144 | struct is_function : std::true_type { 145 | }; 146 | template 147 | struct is_function : std::true_type { 148 | }; 149 | template 150 | struct is_function : std::true_type { 151 | }; 152 | template 153 | struct is_function : std::true_type { 154 | }; 155 | template 156 | struct is_function : std::true_type { 157 | }; 158 | template 159 | struct is_function : std::true_type { 160 | }; 161 | template 162 | struct is_function : std::true_type { 163 | }; 164 | #endif // PROVIDE_IS_FUNCTION_POLYFILL 165 | 166 | template 167 | struct reverse_integer_sequence_impl; 168 | 169 | template 170 | struct reverse_integer_sequence_impl> 171 | : std::integer_sequence { 172 | }; 173 | 174 | template 175 | struct reverse_integer_sequence_impl> 176 | : std::integer_sequence { 177 | }; 178 | 179 | template 180 | using reverse_integer_sequence = reverse_integer_sequence_impl; 181 | 182 | template 183 | using make_reverse_integer_sequence = reverse_integer_sequence>; 184 | 185 | template 186 | using reverse_index_sequence = reverse_integer_sequence>; 187 | 188 | template 189 | using make_reverse_index_sequence = make_reverse_integer_sequence; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /include/fplus/internal/split.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace fplus { 18 | namespace internal { 19 | template 20 | auto group_on_labeled_impl(GroupByCallable group, F f, const ContainerIn& xs) 21 | { 22 | const auto grouped = group(is_equal_by(f), xs); 23 | const auto attach_label = [f](const auto& g) { 24 | using std::begin; 25 | return std::make_pair(internal::invoke(f, *begin(g)), g); 26 | }; 27 | return fplus::transform(attach_label, grouped); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /include/fplus/interpolate.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace fplus { 15 | 16 | // API search type: elem_at_float_idx : (Float, [a]) -> a 17 | // fwd bind count: 1 18 | // Interpolates linearly between elements. 19 | // xs must be non-empty. 20 | template 22 | T elem_at_float_idx(double idx, const Container& xs) 23 | { 24 | assert(is_not_empty(xs)); 25 | if (idx <= 0.0) { 26 | return xs.front(); 27 | } 28 | std::size_t idx_floor = static_cast(floor(idx)); 29 | std::size_t idx_ceil = static_cast(ceil(idx)); 30 | if (idx_ceil >= size_of_cont(xs)) { 31 | return xs.back(); 32 | } 33 | double idx_floor_float = static_cast(idx_floor); 34 | double idx_ceil_float = static_cast(idx_ceil); 35 | double weight_floor = idx_ceil_float - idx; 36 | double weight_ceil = idx - idx_floor_float; 37 | return (weight_floor * elem_at_idx(idx_floor, xs) + weight_ceil * elem_at_idx(idx_ceil, xs)); 38 | } 39 | 40 | } // namespace fplus 41 | -------------------------------------------------------------------------------- /include/fplus/optimize.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace fplus { 16 | 17 | // Optimizes the initial position to the nearest local minimum 18 | // in regards to the objective_function 19 | // using numerical gradient descent based on the epsilon neighborhood. 20 | // momentum_conservation should be in [0, 1). A low value means much decay. 21 | // If no fixed step size is provided, each step advances by the length 22 | // of the gradient. 23 | // In both cases the step is scaled with a step factor, starting at 1.0. 24 | // If one iteration results in no further improvement, 25 | // the step factor is reduced by a factor of 0.5. 26 | // The callback is executed with 27 | // iteration, step factor, momentum and current position 28 | // after every iteration. 29 | // A initial step factor other than 1.0 in all dimensions 30 | // can be emulated by scaling ones objective function accordingly. 31 | // Optimization stops if one of the provided criteria is met. 32 | // minimize_downhill<1>(\x -> square(x[0] + 2), 0.0001, 0.01, {123})[0] == -2; 33 | template > 34 | pos_t minimize_downhill( 35 | F objective_function, 36 | double epsilon, 37 | const pos_t& init_pos, 38 | maybe fixed_step_size = nothing(), 39 | double momentum_conservation = 0.5, 40 | double sufficing_value = std::numeric_limits::lowest(), 41 | double min_step_factor = std::numeric_limits::min(), 42 | std::size_t max_iterations = std::numeric_limits::max(), 43 | long int max_milliseconds = std::numeric_limits::max(), 44 | const std::function< 45 | void(std::size_t, double, const pos_t&, const pos_t&)>& 46 | callback 47 | = std::function< 48 | void(std::size_t, double, const pos_t&, const pos_t&)>()) 49 | { 50 | std::size_t iteration = 0; 51 | double step_factor = 1.0; 52 | pos_t position = init_pos; 53 | double value = internal::invoke(objective_function, position); 54 | 55 | const auto start_time = std::chrono::steady_clock::now(); 56 | const auto is_done = [&]() -> bool { 57 | if (max_milliseconds != std::numeric_limits::max()) { 58 | const auto current_time = std::chrono::steady_clock::now(); 59 | const auto elapsed = current_time - start_time; 60 | const auto elapsed_ms = std::chrono::duration_cast(elapsed).count(); 61 | if (elapsed_ms >= max_milliseconds) { 62 | return true; 63 | } 64 | } 65 | return iteration >= max_iterations || step_factor <= min_step_factor || value <= sufficing_value; 66 | }; 67 | 68 | const auto calc_gradient = 69 | [&](const pos_t& pos) -> pos_t { 70 | pos_t result; 71 | for (std::size_t dim = 0; dim < N; ++dim) { 72 | auto test_pos_1 = pos; 73 | auto test_pos_2 = pos; 74 | test_pos_1[dim] -= epsilon / 2.0; 75 | test_pos_2[dim] += epsilon / 2.0; 76 | const auto val_1 = internal::invoke(objective_function, test_pos_1); 77 | const auto val_2 = internal::invoke(objective_function, test_pos_2); 78 | result[dim] = (val_2 - val_1) / epsilon; 79 | } 80 | return result; 81 | }; 82 | 83 | const auto add = [](const pos_t& p1, const pos_t& p2) -> pos_t { 84 | pos_t result; 85 | for (std::size_t dim = 0; dim < N; ++dim) { 86 | result[dim] = p1[dim] + p2[dim]; 87 | } 88 | return result; 89 | }; 90 | 91 | const auto multiply = [](const pos_t& p, double f) -> pos_t { 92 | pos_t result; 93 | for (std::size_t dim = 0; dim < N; ++dim) { 94 | result[dim] = p[dim] * f; 95 | } 96 | return result; 97 | }; 98 | 99 | const auto dist_to_origin = [](const pos_t& p) -> double { 100 | double acc = 0; 101 | for (std::size_t dim = 0; dim < N; ++dim) { 102 | acc += square(p[dim]); 103 | } 104 | return sqrt(acc); 105 | }; 106 | 107 | const auto normalize = [&](const pos_t& p) -> pos_t { 108 | return multiply(p, 1.0 / dist_to_origin(p)); 109 | }; 110 | 111 | const auto null_vector = []() -> pos_t { 112 | pos_t result; 113 | for (std::size_t dim = 0; dim < N; ++dim) { 114 | result[dim] = 0; 115 | } 116 | return result; 117 | }; 118 | 119 | pos_t momentum = null_vector(); 120 | while (!is_done()) { 121 | auto new_momentum = multiply(momentum, momentum_conservation); 122 | pos_t gradient = calc_gradient(add(position, new_momentum)); 123 | const auto inverse_gradient = multiply(gradient, -1.0); 124 | 125 | auto new_momentum_add = is_nothing(fixed_step_size) ? inverse_gradient : multiply(normalize(inverse_gradient), fixed_step_size.unsafe_get_just()); 126 | 127 | new_momentum = multiply( 128 | add(new_momentum, new_momentum_add), 129 | step_factor); 130 | if (dist_to_origin(momentum) <= std::numeric_limits::min() && dist_to_origin(new_momentum) <= std::numeric_limits::min()) { 131 | break; 132 | } 133 | const auto new_position = add(position, new_momentum); 134 | const auto new_value = internal::invoke(objective_function, new_position); 135 | if (new_value >= value) { 136 | step_factor /= 2.0; 137 | } else { 138 | value = new_value; 139 | position = new_position; 140 | momentum = new_momentum; 141 | } 142 | ++iteration; 143 | if (callback) { 144 | callback(iteration, step_factor, momentum, position); 145 | } 146 | } 147 | return position; 148 | } 149 | 150 | } // namespace fplus 151 | -------------------------------------------------------------------------------- /include/fplus/queue.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace fplus { 18 | 19 | // A thread-safe queue. 20 | template 21 | class queue { 22 | public: 23 | queue() 24 | : queue_() 25 | , mutex_() 26 | , cond_() 27 | { 28 | } 29 | fplus::maybe pop() 30 | { 31 | std::unique_lock lock(mutex_); 32 | if (queue_.empty()) { 33 | return {}; 34 | } 35 | auto item = queue_.front(); 36 | queue_.pop_front(); 37 | return item; 38 | } 39 | 40 | void push(const T& item) 41 | { 42 | { 43 | std::unique_lock lock(mutex_); 44 | queue_.push_back(item); 45 | } 46 | cond_.notify_one(); 47 | } 48 | 49 | std::vector pop_all() 50 | { 51 | std::unique_lock mlock(mutex_); 52 | const auto result = fplus::convert_container>(queue_); 53 | queue_.clear(); 54 | return result; 55 | } 56 | 57 | std::vector wait_and_pop_all() 58 | { 59 | std::unique_lock mlock(mutex_); 60 | cond_.wait(mlock, [&]() -> bool { return !queue_.empty(); }); 61 | const auto result = fplus::convert_container>(queue_); 62 | queue_.clear(); 63 | return result; 64 | } 65 | 66 | std::vector wait_for_and_pop_all(std::int64_t max_wait_time_us) 67 | { 68 | std::unique_lock mlock(mutex_); 69 | const auto t = std::chrono::microseconds { max_wait_time_us }; 70 | cond_.wait_for(mlock, t, [&]() -> bool { return !queue_.empty(); }); 71 | const auto result = fplus::convert_container>(queue_); 72 | queue_.clear(); 73 | return result; 74 | } 75 | 76 | private: 77 | std::deque queue_; 78 | std::mutex mutex_; 79 | std::condition_variable cond_; 80 | }; 81 | 82 | } // namespace fplus 83 | -------------------------------------------------------------------------------- /include/fplus/raii.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace fplus { 12 | 13 | // A generic RAII class. 14 | // It is recommended to use make_raii for constructing an instance. 15 | template 16 | class raii { 17 | public: 18 | raii(INIT init, QUIT quit) 19 | : quit_(quit) 20 | { 21 | init(); 22 | } 23 | ~raii() 24 | { 25 | quit_(); 26 | } 27 | raii(const raii&) = delete; 28 | raii(raii&&) = default; 29 | raii& operator=(const raii&) = delete; 30 | raii& operator=(raii&&) = default; 31 | 32 | private: 33 | QUIT quit_; 34 | }; 35 | 36 | template 37 | shared_ref> make_raii(INIT init, QUIT quit) 38 | { 39 | return make_shared_ref>(init, quit); 40 | } 41 | 42 | } // namespace fplus 43 | -------------------------------------------------------------------------------- /include/fplus/read.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | namespace fplus { 16 | 17 | namespace internal { 18 | template 19 | struct helper_read_value_struct { 20 | }; 21 | 22 | template <> 23 | struct helper_read_value_struct { 24 | static void read(const std::string& str, 25 | int& result, std::size_t& num_chars_used) 26 | { 27 | result = std::stoi(str, &num_chars_used); 28 | } 29 | }; 30 | 31 | template <> 32 | struct helper_read_value_struct { 33 | static void read(const std::string& str, 34 | long& result, std::size_t& num_chars_used) 35 | { 36 | result = std::stol(str, &num_chars_used); 37 | } 38 | }; 39 | 40 | template <> 41 | struct helper_read_value_struct { 42 | static void read(const std::string& str, 43 | long long& result, std::size_t& num_chars_used) 44 | { 45 | result = std::stoll(str, &num_chars_used); 46 | } 47 | }; 48 | 49 | template <> 50 | struct helper_read_value_struct { 51 | static void read(const std::string& str, 52 | unsigned int& result, std::size_t& num_chars_used) 53 | { 54 | unsigned long result_u_l = std::stoul(str, &num_chars_used); 55 | result = static_cast(result_u_l); 56 | } 57 | }; 58 | 59 | template <> 60 | struct helper_read_value_struct { 61 | static void read(const std::string& str, 62 | unsigned long& result, std::size_t& num_chars_used) 63 | { 64 | result = std::stoul(str, &num_chars_used); 65 | } 66 | }; 67 | 68 | template <> 69 | struct helper_read_value_struct { 70 | static void read(const std::string& str, 71 | unsigned long long& result, std::size_t& num_chars_used) 72 | { 73 | result = std::stoull(str, &num_chars_used); 74 | } 75 | }; 76 | 77 | template <> 78 | struct helper_read_value_struct { 79 | static void read(const std::string& str, 80 | float& result, std::size_t& num_chars_used) 81 | { 82 | result = std::stof(str, &num_chars_used); 83 | } 84 | }; 85 | 86 | template <> 87 | struct helper_read_value_struct { 88 | static void read(const std::string& str, 89 | double& result, std::size_t& num_chars_used) 90 | { 91 | result = std::stod(str, &num_chars_used); 92 | } 93 | }; 94 | 95 | template <> 96 | struct helper_read_value_struct { 97 | static void read(const std::string& str, 98 | long double& result, std::size_t& num_chars_used) 99 | { 100 | result = std::stold(str, &num_chars_used); 101 | } 102 | }; 103 | 104 | template <> 105 | struct helper_read_value_struct { 106 | static void read(const std::string& str, 107 | std::string& result, std::size_t& num_chars_used) 108 | { 109 | num_chars_used = str.size(); 110 | result = str; 111 | } 112 | }; 113 | } 114 | 115 | // API search type: read_value_result : String -> Result a 116 | // Try to deserialize a value. 117 | template 118 | result read_value_result(const std::string& str) 119 | { 120 | try { 121 | T result; 122 | std::size_t num_chars_used = 0; 123 | internal::helper_read_value_struct::read(str, 124 | result, num_chars_used); 125 | if (num_chars_used != str.size()) { 126 | return error(std::string("String not fully parsable.")); 127 | } 128 | return ok(result); 129 | } catch (const std::invalid_argument& e) { 130 | return error(e.what()); 131 | } catch (const std::out_of_range& e) { 132 | return error(e.what()); 133 | } 134 | } 135 | 136 | // API search type: read_value : String -> Maybe a 137 | // Try to deserialize/parse a value, e.g.: 138 | // String to Int 139 | // String to Float 140 | // String to Double 141 | // read_value("42") == 42 142 | // etc. 143 | template 144 | maybe read_value(const std::string& str) 145 | { 146 | return to_maybe(read_value_result(str)); 147 | } 148 | 149 | // API search type: read_value_with_default : (a, String) -> a 150 | // fwd bind count: 1 151 | // Try to deserialize a value, return given default on failure, e.g.: 152 | // String to Int 153 | // String to Float 154 | // String to Double 155 | // read_value_with_default(3, "42") == 42 156 | // read_value_with_default(3, "") == 3 157 | // read_value_with_default(3, "foo") == 3 158 | // etc. 159 | template 160 | T read_value_with_default(const T& def, const std::string& str) 161 | { 162 | return just_with_default(def, to_maybe(read_value_result(str))); 163 | } 164 | 165 | // API search type: read_value_unsafe : String -> a 166 | // Try to deserialize a value, crash on failure, e.g.: 167 | // String to Int 168 | // String to Float 169 | // String to Double 170 | // read_value_unsafe("42") == 42 171 | // read_value_unsafe("") == crash 172 | // read_value_unsafe("foo") == crash 173 | // See read_value and read_value_with_default for safe versions. 174 | // etc. 175 | template 176 | T read_value_unsafe(const std::string& str) 177 | { 178 | return unsafe_get_just(to_maybe(read_value_result(str))); 179 | } 180 | 181 | } // namespace fplus 182 | -------------------------------------------------------------------------------- /include/fplus/replace.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace fplus { 14 | 15 | namespace internal { 16 | 17 | template 18 | Container replace_if(internal::reuse_container_t, 19 | UnaryPredicate p, const T& dest, Container&& xs) 20 | { 21 | std::replace_if(std::begin(xs), std::end(xs), p, dest); 22 | return std::forward(xs); 23 | } 24 | 25 | template 26 | Container replace_if(internal::create_new_container_t, 27 | UnaryPredicate p, const T& dest, const Container& xs) 28 | { 29 | Container ys = xs; 30 | return replace_if(internal::reuse_container_t(), 31 | p, dest, std::move(ys)); 32 | } 33 | 34 | } // namespace internal 35 | 36 | // API search type: replace_if : ((a -> Bool), a, [a]) -> [a] 37 | // fwd bind count: 2 38 | // Replace every element fulfilling a predicate with a specific value. 39 | // replace_if(is_even, 0, [1, 3, 4, 6, 7]) == [1, 3, 0, 0, 7] 40 | template > 42 | ContainerOut replace_if(UnaryPredicate p, 43 | const typename ContainerOut::value_type& dest, Container&& xs) 44 | { 45 | return internal::replace_if(internal::can_reuse_v {}, 46 | p, dest, std::forward(xs)); 47 | } 48 | 49 | namespace internal { 50 | 51 | template 53 | Container replace_elem_at_idx(internal::reuse_container_t, 54 | std::size_t idx, const T& dest, Container&& xs) 55 | { 56 | assert(idx < xs.size()); 57 | auto it = std::begin(xs); 58 | advance_iterator(it, idx); 59 | *it = dest; 60 | return std::forward(xs); 61 | } 62 | 63 | template 65 | Container replace_elem_at_idx(internal::create_new_container_t, 66 | std::size_t idx, const T& dest, const Container& xs) 67 | { 68 | Container ys = xs; 69 | return replace_elem_at_idx(internal::reuse_container_t(), 70 | idx, dest, std::move(ys)); 71 | } 72 | 73 | } // namespace internal 74 | 75 | // API search type: replace_elem_at_idx : (Int, a, [a]) -> [a] 76 | // fwd bind count: 2 77 | // Replace the element at a specific index. 78 | // replace_elem_at_idx(2, 0, [1, 3, 4, 4, 7]) == [1, 3, 0, 4, 7] 79 | template , 81 | typename T = typename ContainerOut::value_type> 82 | ContainerOut replace_elem_at_idx(std::size_t idx, const T& dest, 83 | Container&& xs) 84 | { 85 | return internal::replace_elem_at_idx(internal::can_reuse_v {}, 86 | idx, dest, std::forward(xs)); 87 | } 88 | 89 | // API search type: replace_elems : (a, a, [a]) -> [a] 90 | // fwd bind count: 2 91 | // Replace all elements matching source with dest. 92 | // replace_elems(4, 0, [1, 3, 4, 4, 7]) == [1, 3, 0, 0, 7] 93 | template , 95 | typename T = typename ContainerOut::value_type> 96 | ContainerOut replace_elems(const T& source, const T& dest, Container&& xs) 97 | { 98 | return replace_if(bind_1st_of_2(is_equal, source), dest, xs); 99 | } 100 | 101 | // API search type: replace_tokens : ([a], [a], [a]) -> [a] 102 | // fwd bind count: 2 103 | // Replace all segments matching source with dest. 104 | // replace_tokens("haha", "hihi", "oh, hahaha!") == "oh, hihiha!" 105 | // replace_tokens("haha", "o", "oh, hahaha!") == "oh, oha!" 106 | template 107 | Container replace_tokens(const Container& source, const Container& dest, const Container& xs) 108 | { 109 | auto splitted = split_by_token(source, true, xs); 110 | return join(dest, splitted); 111 | } 112 | 113 | } // namespace fplus 114 | -------------------------------------------------------------------------------- /include/fplus/shared_ref.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace fplus { 12 | 13 | // A std::shared_ptr expresses 14 | // optionality of the contained value (can be nullptr) 15 | // and shared ownership that can be transferred. 16 | // A std::optional expresses optionality only. 17 | // The standard does not provide a class to 18 | // express only shared ownership without optionality. 19 | // shared_ref fills this gap. 20 | // It is recommended to use make_shared_ref for constructing an instance. 21 | template 22 | class shared_ref { 23 | public: 24 | shared_ref(const shared_ref&) = default; 25 | shared_ref(shared_ref&&) = default; 26 | shared_ref& operator=(const shared_ref&) = default; 27 | shared_ref& operator=(shared_ref&&) = default; 28 | ~shared_ref() = default; 29 | 30 | T* operator->() { return m_ptr.get(); } 31 | const T* operator->() const { return m_ptr.get(); } 32 | 33 | T& operator*() { return *m_ptr.get(); } 34 | const T& operator*() const { return *m_ptr.get(); } 35 | 36 | template 37 | friend shared_ref make_shared_ref(XTypes&&... args); 38 | 39 | private: 40 | std::shared_ptr m_ptr; 41 | shared_ref(T* value) 42 | : m_ptr(value) 43 | { 44 | assert(value != nullptr); 45 | } 46 | }; 47 | 48 | // http://stackoverflow.com/a/41976419/1866775 49 | template 50 | shared_ref make_shared_ref(Types&&... args) 51 | { 52 | return shared_ref(new T(std::forward(args)...)); 53 | } 54 | 55 | } // namespace fplus 56 | -------------------------------------------------------------------------------- /include/fplus/stopwatch.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace fplus { 12 | 13 | class stopwatch { 14 | public: 15 | stopwatch() 16 | : beg_(clock::now()) 17 | { 18 | } 19 | void reset() { beg_ = clock::now(); } 20 | 21 | // time since creation or last reset in seconds 22 | double elapsed() const 23 | { 24 | return std::chrono::duration_cast(clock::now() - beg_).count(); 25 | } 26 | 27 | private: 28 | typedef std::chrono::high_resolution_clock clock; 29 | typedef std::chrono::duration> second; 30 | std::chrono::time_point beg_; 31 | }; 32 | 33 | } // namespace fplus 34 | -------------------------------------------------------------------------------- /include/fplus/string_tools.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace fplus { 18 | 19 | // API search type: is_letter_or_digit : Char -> Bool 20 | // fwd bind count: 0 21 | // Is character alphanumerical? 22 | template 23 | bool is_letter_or_digit(const typename String::value_type& c) 24 | { 25 | return std::isdigit(static_cast(c)) || std::isalpha(static_cast(c)); 26 | } 27 | 28 | // API search type: is_whitespace : Char -> Bool 29 | // fwd bind count: 0 30 | // Is character a whitespace. 31 | template 32 | bool is_whitespace(const typename String::value_type& c) 33 | { 34 | return (c == 32 || is_in_interval(9, 14, static_cast(c))); 35 | } 36 | 37 | // API search type: is_line_break : Char -> Bool 38 | // fwd bind count: 0 39 | // Newline character ('\n')? 40 | template 41 | bool is_line_break(const typename String::value_type& c) 42 | { 43 | return c == '\n'; 44 | } 45 | 46 | // API search type: clean_newlines : String -> String 47 | // fwd bind count: 0 48 | // Replaces windows and mac newlines with linux newlines. 49 | template 50 | String clean_newlines(const String& str) 51 | { 52 | return replace_elems('\r', '\n', 53 | replace_tokens(String("\r\n"), String("\n"), str)); 54 | } 55 | 56 | // API search type: split_words : (Bool, String) -> [String] 57 | // fwd bind count: 1 58 | // Splits a string by non-letter and non-digit characters. 59 | // split_words(false, "How are you?") == ["How", "are", "you"] 60 | template > 61 | ContainerOut split_words(const bool allowEmpty, const String& str) 62 | { 63 | return split_by(logical_not(is_letter_or_digit), allowEmpty, str); 64 | } 65 | 66 | // API search type: split_lines : (Bool, String) -> [String] 67 | // fwd bind count: 1 68 | // Splits a string by the found newlines. 69 | // split_lines(false, "Hi,\nhow are you?") == ["Hi,", "How are you"] 70 | template > 71 | ContainerOut split_lines(bool allowEmpty, const String& str) 72 | { 73 | return split_by(is_line_break, allowEmpty, clean_newlines(str)); 74 | } 75 | 76 | // API search type: trim_whitespace_left : String -> String 77 | // fwd bind count: 0 78 | // trim_whitespace_left(" text ") == "text " 79 | template 80 | String trim_whitespace_left(const String& str) 81 | { 82 | return drop_while(is_whitespace, str); 83 | } 84 | 85 | // API search type: trim_whitespace_right : String -> String 86 | // fwd bind count: 0 87 | // Remove whitespace characters from the end of a string. 88 | // trim_whitespace_right(" text ") == " text" 89 | template 90 | String trim_whitespace_right(const String& str) 91 | { 92 | return trim_right_by(is_whitespace, str); 93 | } 94 | 95 | // API search type: trim_whitespace : String -> String 96 | // fwd bind count: 0 97 | // Remove whitespace characters from the beginning and the end of a string. 98 | // trim_whitespace(" text ") == "text" 99 | template 100 | String trim_whitespace(const String& str) 101 | { 102 | return trim_by(is_whitespace, str); 103 | } 104 | 105 | // API search type: to_lower_case : String -> String 106 | // fwd bind count: 0 107 | // Convert a string to lowercase characters. 108 | // to_lower_case("ChaRacTer&WorDs23") == "character&words23" 109 | template 110 | String to_lower_case(const String& str) 111 | { 112 | typedef typename String::value_type Char; 113 | return transform([](Char c) -> Char { 114 | return static_cast( 115 | std::tolower(static_cast(c))); 116 | }, 117 | str); 118 | } 119 | 120 | // API search type: to_lower_case_loc : (Locale, String) -> String 121 | // fwd bind count: 1 122 | // Convert a string to lowercase characters using specified locale. 123 | // to_upper_case_loc(locale("ru_RU.utf8"), "cYrIlLiC КиРиЛлИцА") == "cyrillic кириллица" 124 | template 125 | String to_lower_case_loc(const std::locale& lcl, const String& str) 126 | { 127 | typedef typename String::value_type Char; 128 | return transform([&lcl](Char c) -> Char { 129 | return static_cast( 130 | std::tolower(c, lcl)); 131 | }, 132 | str); 133 | } 134 | 135 | // API search type: to_upper_case : String -> String 136 | // fwd bind count: 0 137 | // Convert a string to uppercase characters. 138 | // to_upper_case("ChaRacTer&WorDs34") == "CHARACTER&WORDS34" 139 | template 140 | String to_upper_case(const String& str) 141 | { 142 | typedef typename String::value_type Char; 143 | return transform([](Char c) -> Char { 144 | return static_cast( 145 | std::toupper(static_cast(c))); 146 | }, 147 | str); 148 | } 149 | 150 | // API search type: to_upper_case_loc : (Locale, String) -> String 151 | // fwd bind count: 1 152 | // Convert a string to uppercase characters using specified locale. 153 | // to_upper_case_loc(locale("ru_RU.utf8"), "cYrIlLiC КиРиЛлИцА") == "CYRILLIC КИРИЛЛИЦА" 154 | template 155 | String to_upper_case_loc(const std::locale& lcl, const String& str) 156 | { 157 | typedef typename String::value_type Char; 158 | return transform([&lcl](Char c) -> Char { 159 | return static_cast( 160 | std::toupper(c, lcl)); 161 | }, 162 | str); 163 | } 164 | 165 | // API search type: to_string_fill_left : (Char, Int, a) -> String 166 | // fwd bind count: 2 167 | // Convert a type right-aligned string using a fill character. 168 | // to_string_fill_left('0', 5, 42) == "00042" 169 | // to_string_fill_left(' ', 5, 42) == " 42" 170 | template 171 | std::string to_string_fill_left(const std::string::value_type& filler, 172 | std::size_t min_size, const T& x) 173 | { 174 | return fill_left(filler, min_size, std::to_string(x)); 175 | } 176 | 177 | // API search type: to_string_fill_right : (Char, Int, a) -> String 178 | // fwd bind count: 2 179 | // Convert a type left-aligned string using a fill character. 180 | // to_string_fill_right(' ', 5, 42) == "42 " 181 | template 182 | std::string to_string_fill_right(const std::string::value_type& filler, 183 | std::size_t min_size, const T& x) 184 | { 185 | return fill_right(filler, min_size, std::to_string(x)); 186 | } 187 | 188 | } // namespace fplus 189 | -------------------------------------------------------------------------------- /include/fplus/timed.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace fplus { 18 | using ExecutionTime = double; // in seconds 19 | 20 | // Holds a value of type T plus an execution time 21 | template 22 | class timed : public std::pair { 23 | using base_pair = std::pair; 24 | 25 | public: 26 | timed() 27 | : base_pair() 28 | { 29 | } 30 | timed(const T& val, ExecutionTime t = 0.) 31 | : base_pair(val, t) 32 | { 33 | } 34 | 35 | // Execution time in seconds (returns a double) 36 | ExecutionTime time_in_s() const { return base_pair::second; } 37 | 38 | // Execution time as a std::chrono::duration 39 | std::chrono::duration> duration_in_s() const 40 | { 41 | return std::chrono::duration>(time_in_s()); 42 | } 43 | 44 | // Inner value 45 | const T& get() const { return base_pair::first; } 46 | T& get() { return base_pair::first; } 47 | }; 48 | 49 | // API search type: show_timed : Timed a -> String 50 | // fwd bind count: 0 51 | // show_timed((42,1)) -> "42 (1000ms)" 52 | template 53 | std::string show_timed(const fplus::timed& v) 54 | { 55 | std::string result = fplus::show(v.get()) + " (" + fplus::show(v.time_in_s() * 1000.) + "ms)"; 56 | return result; 57 | } 58 | 59 | namespace internal { 60 | template 61 | class timed_function_impl { 62 | public: 63 | explicit timed_function_impl(Fn fn) 64 | : _fn(fn) {}; 65 | template 66 | auto operator()(Args&&... args) 67 | { 68 | return _timed_result(std::forward(args)...); 69 | } 70 | 71 | private: 72 | template 73 | auto _timed_result(Args&&... args) 74 | { 75 | fplus::stopwatch timer; 76 | auto r = _fn(std::forward(args)...); 77 | auto r_t = fplus::timed(r, timer.elapsed()); 78 | return r_t; 79 | } 80 | 81 | Fn _fn; 82 | }; 83 | } 84 | 85 | // API search type: make_timed_function : ((a -> b)) -> (a -> Timed b) 86 | // fwd bind count: 0 87 | // Transforms a function into a timed / benchmarked version of the same function. 88 | // - 89 | // Example: 90 | // - 91 | // using Ints = std::vector; 92 | // Ints ascending_numbers = fplus::numbers(0, 1000); 93 | // Ints shuffled_numbers = fplus::shuffle(std::mt19937::default_seed, ascending_numbers); 94 | // auto sort_func = [](const Ints& values) { return fplus::sort(values); }; 95 | // auto sort_bench = fplus::make_timed_function(sort_func); 96 | // auto sorted_numbers = sort_bench(shuffled_numbers); 97 | // assert(sorted_numbers.get() == ascending_numbers); // sorted_numbers.get() <=> actual output 98 | // assert(sorted_numbers.time_in_s() < 0.1); // // sorted_numbers.time_in_s() <=> execution time 99 | template 100 | auto make_timed_function(Fn f) 101 | { 102 | return internal::timed_function_impl(f); 103 | } 104 | 105 | namespace internal { 106 | template 107 | class timed_void_function_impl { 108 | public: 109 | explicit timed_void_function_impl(Fn fn) 110 | : _fn(fn) {}; 111 | template 112 | auto operator()(Args&&... args) 113 | { 114 | return _timed_result(std::forward(args)...); 115 | } 116 | 117 | private: 118 | template 119 | auto _timed_result(Args&&... args) 120 | { 121 | fplus::stopwatch timer; 122 | _fn(std::forward(args)...); 123 | return timer.elapsed(); 124 | } 125 | 126 | Fn _fn; 127 | }; 128 | 129 | } 130 | 131 | // API search type: make_timed_void_function : ((a -> Void)) -> (a -> Double) 132 | // fwd bind count: 0 133 | // Transforms a void function into a timed / benchmarked version of the same function. 134 | // - 135 | // Example: 136 | // - 137 | // void foo() { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } 138 | // ... 139 | // auto foo_bench = make_timed_void_function(foo); 140 | // auto r = foo_bench(); 141 | // double run_time = foo_bench(); // in seconds 142 | template 143 | auto make_timed_void_function(Fn f) 144 | { 145 | return internal::timed_void_function_impl(f); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /include/fplus/tree.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | namespace fplus { 16 | 17 | template 18 | struct tree { 19 | tree(const T& value, const std::vector>& children) 20 | : value_(value) 21 | , children_(children) 22 | { 23 | } 24 | T value_; 25 | std::vector> children_; 26 | }; 27 | 28 | namespace internal { 29 | template 30 | tree make_singleton_tree(const T& x) 31 | { 32 | return { x, {} }; 33 | } 34 | } // namespace internal 35 | 36 | namespace internal { 37 | 38 | template 39 | std::vector> presort_trees(BinaryPredicate tree_is_child_of, 40 | std::vector> xs_orig) 41 | { 42 | auto xs = fplus::convert_container>>(xs_orig); 43 | std::vector> result; 44 | while (!xs.empty()) { 45 | for (auto it = std::begin(xs); it != std::end(xs);) { 46 | bool has_children = false; 47 | for (auto it_rest = std::begin(xs); it_rest != std::end(xs); ++it_rest) { 48 | if (it_rest != it && tree_is_child_of(*it_rest, *it)) { 49 | has_children = true; 50 | } 51 | } 52 | if (!has_children) { 53 | result.push_back(*it); 54 | it = xs.erase(it); 55 | } else { 56 | ++it; 57 | } 58 | } 59 | } 60 | return result; 61 | } 62 | 63 | template // todo: name? 64 | TreeCont trees_from_sequence_helper( 65 | BinaryPredicate tree_is_child_of, TreeCont xs_unsorted) 66 | { 67 | TreeCont result; 68 | auto xs = presort_trees(tree_is_child_of, xs_unsorted); 69 | for (auto it = std::begin(xs); it != std::end(xs); ++it) { 70 | const auto find_pred = bind_1st_of_2(tree_is_child_of, *it); 71 | auto it_find_begin = it; 72 | internal::advance_iterator(it_find_begin, 1); 73 | auto parent_it = std::find_if(it_find_begin, std::end(xs), find_pred); 74 | if (parent_it != std::end(xs)) { 75 | parent_it->children_.push_back(*it); 76 | } else { 77 | result.push_back(*it); 78 | } 79 | } 80 | return result; 81 | } 82 | 83 | } // namespace internal 84 | 85 | // API search type: trees_from_sequence : (((a, a) -> Bool), [a]) -> [Tree a] 86 | // fwd bind count: 1 87 | // Converts the sequence into a tree considering the given binary predicate. 88 | template // todo: name? 89 | std::vector> trees_from_sequence( 90 | BinaryPredicate is_child_of, const Container& xs) 91 | { 92 | internal::check_binary_predicate_for_container(); 93 | typedef typename Container::value_type T; 94 | typedef tree Tree; 95 | const auto singletons = transform_convert>( 96 | internal::make_singleton_tree, xs); 97 | const auto tree_is_child_of = 98 | [is_child_of](const tree& a, const tree& b) -> bool { 99 | return is_child_of(a.value_, b.value_); 100 | }; 101 | return internal::trees_from_sequence_helper( 102 | tree_is_child_of, std::move(singletons)); 103 | } 104 | 105 | namespace internal { 106 | 107 | // -1 = a < b 108 | // 0 = a == b 109 | // 1 = b < a 110 | template 111 | int tree_cmp(const tree& a, const tree& b) 112 | { 113 | if (a.value_ < b.value_) { 114 | return -1; 115 | } else if (b.value_ < a.value_) { 116 | return 1; 117 | } else { 118 | const auto results = zip_with(tree_cmp, 119 | sort_by(tree_cmp, a.children_), 120 | sort_by(tree_cmp, b.children_)); 121 | return just_with_default(0, find_first_by(bind_1st_of_2(is_not_equal, 0), results)); 122 | } 123 | } 124 | 125 | template 126 | bool tree_less(const tree& a, const tree& b) 127 | { 128 | return tree_cmp(a, b) < 0; 129 | } 130 | 131 | } // namespace internal 132 | 133 | namespace internal { 134 | 135 | template 136 | bool are_normalized_trees_equal(const tree& a, const tree& b) 137 | { 138 | if (a.value_ != b.value_ || a.children_.size() != b.children_.size()) { 139 | return false; 140 | } else { 141 | return all(zip_with(are_normalized_trees_equal, 142 | a.children_, b.children_)); 143 | } 144 | } 145 | 146 | template 147 | tree normalize_tree(tree x) 148 | { 149 | x.children_ = sort_by( 150 | internal::tree_less, 151 | transform(normalize_tree, x.children_)); 152 | return x; 153 | } 154 | 155 | } // namespace internal 156 | 157 | // API search type: are_trees_equal : (Tree a, Tree a) -> Bool 158 | // fwd bind count: 1 159 | template 160 | bool are_trees_equal(const tree& a, const tree& b) 161 | { 162 | return internal::are_normalized_trees_equal( 163 | internal::normalize_tree(a), internal::normalize_tree(b)); 164 | } 165 | 166 | // API search type: tree_size : Tree a -> Int 167 | // fwd bind count: 0 168 | // A tree with only one element (root) has size 1. 169 | template 170 | std::size_t tree_size(const tree& x) 171 | { 172 | return 1 + sum(transform(tree_size, x.children_)); 173 | } 174 | 175 | // API search type: tree_depth : Tree a -> Int 176 | // fwd bind count: 0 177 | // A tree with only one element (root) has depth 1. 178 | template 179 | std::size_t tree_depth(const tree& x) 180 | { 181 | return 1 + just_with_default(0, maximum_maybe(transform(tree_depth, x.children_))); 182 | } 183 | 184 | // API search type: flatten_tree_depth_first : Tree a -> [a] 185 | // fwd bind count: 0 186 | // Like in DFS. 187 | template 188 | std::vector flatten_tree_depth_first(const tree& x) 189 | { 190 | return prepend_elem(x.value_, 191 | transform_and_concat(flatten_tree_depth_first, x.children_)); 192 | } 193 | 194 | // API search type: flatten_tree_breadth_first : Tree a -> [a] 195 | // fwd bind count: 0 196 | // Like in BFS. 197 | template 198 | std::vector flatten_tree_breadth_first(const tree& x) 199 | { 200 | std::vector result; 201 | result.reserve(tree_size(x)); 202 | std::queue*> q; 203 | q.push(&x); 204 | while (!q.empty()) { 205 | const auto current = q.front(); 206 | q.pop(); 207 | result.push_back(current->value_); 208 | for (const auto& c : current->children_) { 209 | q.push(&c); 210 | } 211 | } 212 | return result; 213 | } 214 | 215 | } // namespace fplus 216 | -------------------------------------------------------------------------------- /logo/fplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dobiasd/FunctionalPlus/5a951682e45037fa5046fa6b47e9019d0fc55763/logo/fplus.png -------------------------------------------------------------------------------- /logo/fplus.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dobiasd/FunctionalPlus/5a951682e45037fa5046fa6b47e9019d0fc55763/logo/fplus.xcf -------------------------------------------------------------------------------- /script/auto_format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | (find api_search -name "*.cpp" && find include -name "*.hpp" && find examples -name "*.cpp" && find test -name "*.cpp") | xargs clang-format -i {} 3 | -------------------------------------------------------------------------------- /script/ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Central script called by the CI 4 | # Usage: 5 | # ci.sh {run_build|run_tests} 6 | 7 | # 8 | # Private Impl 9 | # 10 | 11 | # Get the directory of the current script (this is bash's notion of poetry) 12 | THIS_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 13 | REPO_DIR="$THIS_DIR"/.. 14 | 15 | # Determine the install prefix based on the OS 16 | if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then 17 | # Windows path handling is a never ending source of ... 18 | INSTALL_PREFIX="$USERPROFILE\\.local" 19 | INSTALL_PREFIX=$(cygpath -u "$INSTALL_PREFIX") # Make CMake happy when using git bash under windows 20 | export CMAKE_PREFIX_PATH=$INSTALL_PREFIX;$CMAKE_PREFIX_PATH # Notice the ";" instead of ":" 21 | else 22 | INSTALL_PREFIX="$HOME/.local" 23 | export CMAKE_PREFIX_PATH=$INSTALL_PREFIX:$CMAKE_PREFIX_PATH 24 | fi 25 | 26 | # Function to install doctest 27 | _install_doctest() { 28 | cd "$REPO_DIR" 29 | git clone --depth=1 --branch=v2.4.11 https://github.com/doctest/doctest 30 | cd doctest && mkdir -p build && cd build 31 | cmake .. -DDOCTEST_WITH_TESTS=OFF -DDOCTEST_WITH_MAIN_IN_STATIC_LIB=OFF -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX 32 | cmake --build . --config Release --target install 33 | } 34 | 35 | # Function to build the project 36 | _build_impl() { 37 | cd "$REPO_DIR" 38 | JOBS=4 39 | BUILD_TYPE=Release 40 | cmake -S test -B build -D CMAKE_BUILD_TYPE=${BUILD_TYPE} 41 | cmake --build build --config ${BUILD_TYPE} -j ${JOBS} 42 | } 43 | 44 | # Function to run tests 45 | _tests_impl() { 46 | cd "$REPO_DIR/build" 47 | JOBS=4 48 | BUILD_TYPE=Release 49 | ctest -C ${BUILD_TYPE} -j ${JOBS} --output-on-failure 50 | } 51 | 52 | # 53 | # API 54 | # 55 | 56 | # API function to run tests 57 | run_tests() { 58 | _install_doctest 59 | _build_impl 60 | _tests_impl 61 | } 62 | 63 | # API function to build the project 64 | run_build() { 65 | _install_doctest 66 | _build_impl 67 | } 68 | 69 | # Main script logic 70 | case "$1" in 71 | run_build) 72 | run_build 73 | ;; 74 | run_tests) 75 | run_tests 76 | ;; 77 | *) 78 | echo "Usage: $0 {run_build|run_tests}" 79 | exit 1 80 | ;; 81 | esac 82 | -------------------------------------------------------------------------------- /script/ci_setup_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | apt-get update 4 | apt-get install -y git 5 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(FunctionalPlusTests) 4 | 5 | set( 6 | FunctionalPlus_INCLUDE_WITHOUT_SYSTEM 7 | YES 8 | CACHE 9 | INTERNAL 10 | "Turn the warning guard off to have errors appear in test builds" 11 | ) 12 | include(../cmake/root-project.cmake) 13 | include(../cmake/warnings.cmake) 14 | 15 | find_package(doctest 2.4.11 CONFIG REQUIRED) 16 | # for doctest_discover_tests 17 | include(doctest) 18 | 19 | enable_testing() 20 | 21 | set( 22 | tests 23 | show_versions 24 | benchmark_session_test 25 | compare_test 26 | composition_test 27 | container_common_test 28 | container_properties_test 29 | container_traits_test 30 | curry_test 31 | extrapolate_test 32 | filter_test 33 | function_traits_test 34 | fwd_test 35 | generate_test 36 | interpolate_test 37 | invoke_test 38 | maps_test 39 | maybe_test 40 | numeric_test 41 | optimize_test 42 | pairs_test 43 | queue_test 44 | raii_test 45 | read_test 46 | readme_examples_test 47 | result_test 48 | replace_test 49 | search_test 50 | sets_test 51 | shared_ref_test 52 | show_test 53 | side_effects_test 54 | split_test 55 | stopwatch_test 56 | stringtools_test 57 | transform_test 58 | timed_test 59 | tree_test 60 | udemy_course_test 61 | variant_test 62 | ) 63 | 64 | foreach (name IN LISTS tests) 65 | add_executable("${name}" "${name}.cpp") 66 | target_compile_options("${name}" PRIVATE ${project_warnings}) 67 | target_compile_definitions( 68 | "${name}" 69 | PRIVATE 70 | DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 71 | ) 72 | target_link_libraries( 73 | "${name}" 74 | PRIVATE 75 | FunctionalPlus::fplus 76 | doctest::doctest 77 | ) 78 | target_compile_features("${name}" PRIVATE cxx_std_14) 79 | doctest_discover_tests("${name}") 80 | endforeach () 81 | -------------------------------------------------------------------------------- /test/compare_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace { 12 | 13 | bool int_less(int x, int y) 14 | { 15 | return x < y; 16 | } 17 | 18 | bool int_less_eq(int x, int y) 19 | { 20 | return x <= y; 21 | } 22 | 23 | auto generic_less = [](auto x, auto y) { 24 | return x < y; 25 | }; 26 | 27 | auto generic_less_eq = [](auto x, auto y) { 28 | return x <= y; 29 | }; 30 | 31 | auto squareGeneric = [](auto x) { return x * x; }; 32 | } 33 | 34 | TEST_CASE("compare_test - is_equal_to") 35 | { 36 | using namespace fplus; 37 | REQUIRE(is_equal_to(2)(2)); 38 | REQUIRE_FALSE(is_equal_to(2)(3)); 39 | } 40 | 41 | TEST_CASE("compare_test - is_not_equal_to") 42 | { 43 | using namespace fplus; 44 | REQUIRE_FALSE(is_not_equal_to(2)(2)); 45 | REQUIRE(is_not_equal_to(2)(3)); 46 | } 47 | 48 | TEST_CASE("compare_test - is_less") 49 | { 50 | using namespace fplus; 51 | REQUIRE_FALSE(is_less(2, 2)); 52 | REQUIRE(is_less(2, 3)); 53 | REQUIRE_FALSE(is_less(3, 2)); 54 | REQUIRE(is_less_than(3)(2)); 55 | } 56 | 57 | TEST_CASE("compare_test - is_less_or_equal") 58 | { 59 | using namespace fplus; 60 | REQUIRE(is_less_or_equal(2, 2)); 61 | REQUIRE(is_less_or_equal(2, 3)); 62 | REQUIRE_FALSE(is_less_or_equal(3, 2)); 63 | REQUIRE(is_less_or_equal_than(3)(2)); 64 | REQUIRE(is_less_or_equal_by_and_by(squareGeneric, squareGeneric)(2, 2)); 65 | REQUIRE(is_less_or_equal_by_than(squareGeneric, 5)(2)); 66 | } 67 | 68 | TEST_CASE("compare_test - is_less_by") 69 | { 70 | using namespace fplus; 71 | auto square = [](int x) { return x * x; }; 72 | REQUIRE(is_less_by_and_by(squareGeneric, square)(2, -3)); 73 | REQUIRE(is_less_by(squareGeneric)(2, -3)); 74 | } 75 | 76 | TEST_CASE("compare_test - is_less_by_than") 77 | { 78 | using namespace fplus; 79 | auto square = [](int x) { return x * x; }; 80 | REQUIRE(is_less_by_than(square, 5)(2)); 81 | REQUIRE(is_less_by_than(squareGeneric, 5)(2)); 82 | } 83 | 84 | TEST_CASE("compare_test - is_greater") 85 | { 86 | using namespace fplus; 87 | REQUIRE_FALSE(is_greater(2, 2)); 88 | REQUIRE_FALSE(is_greater(2, 3)); 89 | REQUIRE(is_greater(3, 2)); 90 | REQUIRE_FALSE(is_greater_than(3)(2)); 91 | REQUIRE(is_greater_by_and_by(squareGeneric, squareGeneric)(3, -2)); 92 | } 93 | 94 | TEST_CASE("compare_test - is_greater_or_equal") 95 | { 96 | using namespace fplus; 97 | REQUIRE(is_greater_or_equal(2, 2)); 98 | REQUIRE_FALSE(is_greater_or_equal(2, 3)); 99 | REQUIRE(is_greater_or_equal(3, 2)); 100 | REQUIRE_FALSE(is_greater_or_equal_than(3)(2)); 101 | REQUIRE(is_greater_or_equal_by_and_by(squareGeneric, squareGeneric)(3, -3)); 102 | REQUIRE(is_greater_or_equal_by(squareGeneric)(3, -3)); 103 | REQUIRE(is_greater_or_equal_by_than(squareGeneric, 3)(-3)); 104 | } 105 | 106 | TEST_CASE("compare_test - is_equal_by") 107 | { 108 | using namespace fplus; 109 | auto square = [](int x) { return x * x; }; 110 | REQUIRE(is_equal_by_and_by(square, square)(2, -2)); 111 | REQUIRE(is_equal_by_and_by(squareGeneric, square)(2, -2)); 112 | REQUIRE(is_equal_by(square)(2, -2)); 113 | REQUIRE(is_not_equal_by_and_by(square, squareGeneric)(2, 3)); 114 | REQUIRE(is_equal_by(squareGeneric)(2, -2)); 115 | REQUIRE(is_not_equal_by(square)(2, 3)); 116 | REQUIRE(is_not_equal_by(squareGeneric)(2, 3)); 117 | REQUIRE(is_equal_by_to(squareGeneric, 4)(2)); 118 | REQUIRE(is_not_equal_by_to(squareGeneric, 5)(2)); 119 | } 120 | 121 | TEST_CASE("compare_test - always") 122 | { 123 | using namespace fplus; 124 | REQUIRE_EQ(identity(2), 2); 125 | REQUIRE_EQ(always(2)(5), 2); 126 | REQUIRE_EQ(always_arg_1_of_2(2, 5), 2); 127 | REQUIRE_EQ(always_arg_2_of_2(2, 5), 5); 128 | } 129 | 130 | TEST_CASE("compare_test - xor_bools") 131 | { 132 | using namespace fplus; 133 | REQUIRE(xor_bools(false, false) == false); 134 | REQUIRE(xor_bools(true, false) == true); 135 | REQUIRE(xor_bools(false, true) == true); 136 | REQUIRE(xor_bools(true, true) == false); 137 | } 138 | 139 | TEST_CASE("compare_test - ord_to_eq") 140 | { 141 | using namespace fplus; 142 | REQUIRE(ord_to_eq(int_less)(1, 2) == false); 143 | REQUIRE(ord_to_eq(int_less)(2, 2) == true); 144 | REQUIRE(ord_to_eq(int_less)(2, 1) == false); 145 | REQUIRE(ord_to_eq(generic_less)(2, 1) == false); 146 | } 147 | 148 | TEST_CASE("compare_test - ord_to_not_eq") 149 | { 150 | using namespace fplus; 151 | REQUIRE(ord_to_not_eq(int_less)(1, 2) == true); 152 | REQUIRE(ord_to_not_eq(int_less)(2, 2) == false); 153 | REQUIRE(ord_to_not_eq(int_less)(2, 1) == true); 154 | REQUIRE(ord_to_not_eq(generic_less)(2, 1) == true); 155 | } 156 | 157 | TEST_CASE("compare_test - ord_eq_to_eq") 158 | { 159 | using namespace fplus; 160 | REQUIRE(ord_eq_to_eq(int_less_eq)(1, 2) == false); 161 | REQUIRE(ord_eq_to_eq(int_less_eq)(2, 2) == true); 162 | REQUIRE(ord_eq_to_eq(int_less_eq)(2, 1) == false); 163 | REQUIRE(ord_eq_to_eq(generic_less_eq)(2, 1) == false); 164 | } 165 | 166 | TEST_CASE("compare_test - ord_eq_to_not_eq") 167 | { 168 | using namespace fplus; 169 | REQUIRE(ord_eq_to_not_eq(int_less_eq)(1, 2) == true); 170 | REQUIRE(ord_eq_to_not_eq(int_less_eq)(2, 2) == false); 171 | REQUIRE(ord_eq_to_not_eq(int_less_eq)(2, 1) == true); 172 | } 173 | 174 | TEST_CASE("compare_test - lexicographical_less") 175 | { 176 | using namespace fplus; 177 | REQUIRE(lexicographical_less(std::vector { 0, 1, 2, 2, 4, 5 }, std::vector { 0, 1, 2, 3, 4, 5 })); 178 | REQUIRE(lexicographical_less(std::vector {}, std::vector { 1 })); 179 | REQUIRE_FALSE(lexicographical_less(std::vector { 1 }, std::vector {})); 180 | REQUIRE(lexicographical_less(std::string("012245"), std::string("012345"))); 181 | REQUIRE(lexicographical_less(std::string("012245"), std::string("01234"))); 182 | REQUIRE(lexicographical_less(std::string("01234"), std::string("012345"))); 183 | REQUIRE_FALSE(lexicographical_less(std::string("012345"), std::string("012245"))); 184 | REQUIRE_FALSE(lexicographical_less(std::string("01234"), std::string("012245"))); 185 | REQUIRE_FALSE(lexicographical_less(std::string("012345"), std::string("01234"))); 186 | } 187 | 188 | TEST_CASE("compare_test - lexicographical_sort") 189 | { 190 | using namespace fplus; 191 | std::vector xs = { "012245", "012345", "01234" }; 192 | std::vector xs_lex_sorted = { "012245", "01234", "012345" }; 193 | REQUIRE_EQ(lexicographical_sort(xs), xs_lex_sorted); 194 | } 195 | -------------------------------------------------------------------------------- /test/container_traits_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | TEST_CASE("container_traits_test - static_asserts") 11 | { 12 | using namespace fplus; 13 | 14 | const auto unary_f = [](int x) -> double { 15 | return static_cast(x); 16 | }; 17 | 18 | const auto binary_f = [](int x, int y) -> double { 19 | return static_cast(x + y); 20 | }; 21 | 22 | static_assert(std::is_same< 23 | internal::same_cont_new_t< 24 | std::vector, 25 | double>::type, 26 | std::vector>::value, 27 | "No."); 28 | 29 | static_assert(std::is_same< 30 | internal::same_cont_new_t_from_unary_f< 31 | std::vector, 32 | decltype(unary_f)>::type, 33 | std::vector>::value, 34 | "No."); 35 | 36 | static_assert(std::is_same< 37 | internal::same_cont_new_t_from_binary_f< 38 | std::vector, 39 | decltype(binary_f), 40 | int, 41 | int>::type, 42 | std::vector>::value, 43 | "No."); 44 | } 45 | -------------------------------------------------------------------------------- /test/curry_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | TEST_CASE("curry_test - show_fill_left") 11 | { 12 | using namespace fplus; 13 | REQUIRE_EQ(show_fill_left(' ', 4, 3), " 3"); 14 | const auto result_old_style = show_fill_left(' ', 4, 3); 15 | const auto result_new_style = curry::show_fill_left(' ')(std::size_t(4))(3); 16 | REQUIRE_EQ(result_old_style, result_new_style); 17 | } 18 | -------------------------------------------------------------------------------- /test/extrapolate_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace { 12 | typedef std::vector IntVector; 13 | IntVector xs = { 1, 2, 2, 3, 2 }; 14 | } 15 | 16 | TEST_CASE("extrapolate_test - elem_at_idx_or_nothing") 17 | { 18 | using namespace fplus; 19 | REQUIRE_EQ(elem_at_idx_or_nothing(-4, xs), nothing()); 20 | REQUIRE_EQ(elem_at_idx_or_nothing(14, xs), nothing()); 21 | REQUIRE_EQ(elem_at_idx_or_nothing(3, xs), maybe(3)); 22 | } 23 | 24 | TEST_CASE("extrapolate_test - elem_at_idx_or_constant") 25 | { 26 | using namespace fplus; 27 | REQUIRE_EQ(elem_at_idx_or_constant(4, -4, xs), 4); 28 | REQUIRE_EQ(elem_at_idx_or_constant(4, 14, xs), 4); 29 | REQUIRE_EQ(elem_at_idx_or_constant(4, 3, xs), 3); 30 | } 31 | 32 | TEST_CASE("extrapolate_test - elem_at_idx_or_replicate") 33 | { 34 | using namespace fplus; 35 | REQUIRE_EQ(elem_at_idx_or_replicate(-2, xs), 1); 36 | REQUIRE_EQ(elem_at_idx_or_replicate(-1, xs), 1); 37 | REQUIRE_EQ(elem_at_idx_or_replicate(0, xs), 1); 38 | REQUIRE_EQ(elem_at_idx_or_replicate(4, xs), 2); 39 | REQUIRE_EQ(elem_at_idx_or_replicate(5, xs), 2); 40 | REQUIRE_EQ(elem_at_idx_or_replicate(6, xs), 2); 41 | } 42 | 43 | TEST_CASE("extrapolate_test - elem_at_idx_or_wrap") 44 | { 45 | using namespace fplus; 46 | REQUIRE_EQ(elem_at_idx_or_wrap(-2, xs), 3); 47 | REQUIRE_EQ(elem_at_idx_or_wrap(-1, xs), 2); 48 | REQUIRE_EQ(elem_at_idx_or_wrap(0, xs), 1); 49 | REQUIRE_EQ(elem_at_idx_or_wrap(4, xs), 2); 50 | REQUIRE_EQ(elem_at_idx_or_wrap(5, xs), 1); 51 | REQUIRE_EQ(elem_at_idx_or_wrap(6, xs), 2); 52 | } 53 | 54 | TEST_CASE("extrapolate_test - elem_at_idx_or_wrap") 55 | { 56 | using namespace fplus; 57 | REQUIRE_EQ(elem_at_idx_or_wrap(-2, xs), 3); 58 | REQUIRE_EQ(elem_at_idx_or_wrap(-1, xs), 2); 59 | REQUIRE_EQ(elem_at_idx_or_wrap(0, xs), 1); 60 | REQUIRE_EQ(elem_at_idx_or_wrap(4, xs), 2); 61 | REQUIRE_EQ(elem_at_idx_or_wrap(5, xs), 1); 62 | REQUIRE_EQ(elem_at_idx_or_wrap(6, xs), 2); 63 | } 64 | 65 | TEST_CASE("extrapolate_test - extrapolate_replicate") 66 | { 67 | using namespace fplus; 68 | REQUIRE_EQ(extrapolate_replicate(2, 2, xs), IntVector({ 1, 1, 1, 2, 2, 3, 2, 2, 2 })); 69 | } 70 | 71 | TEST_CASE("extrapolate_test - extrapolate_wrap") 72 | { 73 | using namespace fplus; 74 | REQUIRE_EQ(extrapolate_wrap(2, 2, xs), IntVector({ 3, 2, 1, 2, 2, 3, 2, 1, 2 })); 75 | } 76 | -------------------------------------------------------------------------------- /test/interpolate_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace { 12 | typedef std::vector FloatVector; 13 | FloatVector xs = { 1.0, 2.0, 6.0 }; 14 | } 15 | 16 | TEST_CASE("interpolate_test - interpolate_linear") 17 | { 18 | using namespace fplus; 19 | REQUIRE(is_in_interval_around(0.0001, 1.0, elem_at_float_idx(-1.7, xs))); 20 | REQUIRE(is_in_interval_around(0.0001, 1.0, elem_at_float_idx(-0.2, xs))); 21 | REQUIRE(is_in_interval_around(0.0001, 1.0, elem_at_float_idx(0.0, xs))); 22 | REQUIRE(is_in_interval_around(0.0001, 1.2, elem_at_float_idx(0.2, xs))); 23 | REQUIRE(is_in_interval_around(0.0001, 1.7, elem_at_float_idx(0.7, xs))); 24 | REQUIRE(is_in_interval_around(0.0001, 2.0, elem_at_float_idx(1.0, xs))); 25 | REQUIRE(is_in_interval_around(0.0001, 4.0, elem_at_float_idx(1.5, xs))); 26 | REQUIRE(is_in_interval_around(0.0001, 5.6, elem_at_float_idx(1.9, xs))); 27 | REQUIRE(is_in_interval_around(0.0001, 6.0, elem_at_float_idx(2.0, xs))); 28 | REQUIRE(is_in_interval_around(0.0001, 6.0, elem_at_float_idx(2.1, xs))); 29 | REQUIRE(is_in_interval_around(0.0001, 6.0, elem_at_float_idx(2.8, xs))); 30 | } 31 | -------------------------------------------------------------------------------- /test/optimize_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | TEST_CASE("optimize_test - minimize_downhill") 12 | { 13 | using namespace fplus; 14 | const auto objective = [](const std::array& x) { 15 | return square(x[0] + 2) + 3; 16 | }; 17 | { 18 | const auto result = minimize_downhill<1>( 19 | objective, 0.0001, { { 123 } }); 20 | REQUIRE(is_in_closed_interval_around(0.0001, -2.0, result[0])); 21 | } 22 | { 23 | const auto result = minimize_downhill<1>( 24 | objective, 0.0001, { { -42 } }, maybe(0.01)); 25 | REQUIRE(is_in_closed_interval_around(0.0001, -2.0, result[0])); 26 | } 27 | { 28 | const auto result = minimize_downhill<1>( 29 | objective, 0.0001, { { -2.000001 } }); 30 | REQUIRE(is_in_closed_interval_around(0.0001, -2.0, result[0])); 31 | } 32 | 33 | const auto objective_2d = [](const std::array& p) { 34 | const auto x = p[0]; 35 | const auto y = p[1]; 36 | return abs(2 * cube(x - 3)) + 4 * square(x + 1) + 2 * x + abs(1 * cube(y - 7)) + 7 * square(y - 4) + 6 * y; 37 | }; 38 | { 39 | const auto result1 = minimize_downhill<2>( 40 | objective_2d, 0.0001, { { 0, 0 } }); 41 | REQUIRE(is_in_closed_interval_around(0.001, 1.1946, result1[0])); 42 | REQUIRE(is_in_closed_interval_around(0.001, 4.7025, result1[1])); 43 | 44 | const auto result2 = minimize_downhill<2>( 45 | objective_2d, 0.0001, { { 0, 0 } }, 0.123, 0.234); 46 | REQUIRE(is_in_closed_interval_around(0.001, 1.1946, result2[0])); 47 | REQUIRE(is_in_closed_interval_around(0.001, 4.7025, result2[1])); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/queue_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | TEST_CASE("queue_test - full") 11 | { 12 | using namespace fplus; 13 | using namespace std::chrono_literals; 14 | 15 | queue q; 16 | 17 | std::thread producer([&q] { 18 | q.push(1); 19 | q.push(2); 20 | std::this_thread::sleep_for(400ms); 21 | q.push(3); 22 | q.push(4); 23 | std::this_thread::sleep_for(400ms); 24 | q.push(5); 25 | std::this_thread::sleep_for(400ms); 26 | q.push(6); 27 | }); 28 | 29 | std::thread consumer([&q] { 30 | std::this_thread::sleep_for(200ms); 31 | REQUIRE_EQ(q.pop(), fplus::just(1)); 32 | REQUIRE_EQ(q.pop(), fplus::just(2)); 33 | REQUIRE_EQ(q.pop(), fplus::nothing()); 34 | std::this_thread::sleep_for(400ms); 35 | REQUIRE_EQ(q.pop_all(), std::vector({ 3, 4 })); 36 | REQUIRE_EQ(q.pop_all(), std::vector({})); 37 | REQUIRE_EQ(q.wait_and_pop_all(), std::vector({ 5 })); 38 | REQUIRE_EQ(q.wait_for_and_pop_all(200000), std::vector({})); 39 | REQUIRE_EQ(q.wait_for_and_pop_all(400000), std::vector({ 6 })); 40 | REQUIRE_EQ(q.wait_for_and_pop_all(200000), std::vector({})); 41 | }); 42 | 43 | producer.join(); 44 | consumer.join(); 45 | } 46 | -------------------------------------------------------------------------------- /test/raii_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | TEST_CASE("raii_test - make_raii") 11 | { 12 | std::string log = "nothing"; 13 | const auto init = [&log]() { log = "init"; }; 14 | const auto quit = [&log]() { log = "quit"; }; 15 | 16 | REQUIRE_EQ(log, "nothing"); 17 | { 18 | REQUIRE_EQ(log, "nothing"); 19 | const auto ressource = fplus::make_raii(init, quit); 20 | REQUIRE_EQ(log, "init"); 21 | } 22 | REQUIRE_EQ(log, "quit"); 23 | } 24 | -------------------------------------------------------------------------------- /test/read_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | TEST_CASE("read_test - read_value") 12 | { 13 | using namespace fplus; 14 | REQUIRE_EQ(read_value("42"), just(42)); 15 | REQUIRE_EQ(read_value("foo"), just("foo")); 16 | REQUIRE_EQ(read_value("42"), just(42)); 17 | REQUIRE_EQ(read_value("42"), just(42)); 18 | REQUIRE_EQ(read_value("42"), just(42)); 19 | REQUIRE_EQ(read_value("42"), just(42)); 20 | REQUIRE_EQ(read_value("42"), just(42)); 21 | REQUIRE_EQ(read_value("-3"), just(-3)); 22 | REQUIRE_EQ(read_value("twenty"), nothing()); 23 | REQUIRE_EQ(read_value("3 thousand"), nothing()); 24 | REQUIRE_EQ(read_value_with_default(3, "42"), 42); 25 | REQUIRE_EQ(read_value_with_default(3, "foo"), 3); 26 | REQUIRE_EQ(read_value_with_default(3, ""), 3); 27 | REQUIRE_EQ(read_value_unsafe("42"), 42); 28 | 29 | REQUIRE(is_in_interval(-42.4f, -42.2f, unsafe_get_just(read_value("-42.3")))); 30 | REQUIRE(is_in_interval(-42.4, -42.2, unsafe_get_just(read_value("-42.3")))); 31 | REQUIRE(is_in_interval(-42.4L, -42.2L, unsafe_get_just(read_value("-42.3")))); 32 | } 33 | 34 | TEST_CASE("read_test - read_value_result") 35 | { 36 | using namespace fplus; 37 | REQUIRE_EQ(read_value_result("42"), (ok(42))); 38 | REQUIRE_EQ(read_value_result("-3"), (ok(-3))); 39 | REQUIRE(is_error(read_value_result("twenty"))); 40 | REQUIRE(is_error(read_value_result("3 thousand"))); 41 | } 42 | -------------------------------------------------------------------------------- /test/replace_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | TEST_CASE("replace_test - replace_if") 13 | { 14 | auto is_even = [](int value) { return value % 2 == 0; }; 15 | std::vector v = { 1, 3, 4, 6, 7 }; 16 | auto result = fplus::replace_if(is_even, 0, v); 17 | REQUIRE_EQ(result, std::vector({ 1, 3, 0, 0, 7 })); 18 | 19 | auto result_rvalue = fplus::replace_if(is_even, 0, std::vector({ 1, 3, 4, 6, 7 })); 20 | REQUIRE_EQ(result_rvalue, std::vector({ 1, 3, 0, 0, 7 })); 21 | } 22 | 23 | TEST_CASE("replace_test - replace_elem_at_idx") 24 | { 25 | std::vector v = { 1, 3, 4, 4, 7 }; 26 | auto result = fplus::replace_elem_at_idx(2, 0, v); 27 | REQUIRE_EQ(result, std::vector({ 1, 3, 0, 4, 7 })); 28 | 29 | auto result_rvalue = fplus::replace_elem_at_idx(2, 0, std::vector({ 1, 3, 4, 4, 7 })); 30 | REQUIRE_EQ(result_rvalue, std::vector({ 1, 3, 0, 4, 7 })); 31 | } 32 | 33 | TEST_CASE("replace_test - replace_elems") 34 | { 35 | std::vector v = { 1, 3, 4, 4, 7 }; 36 | auto result = fplus::replace_elems(4, 0, v); 37 | REQUIRE_EQ(result, std::vector({ 1, 3, 0, 0, 7 })); 38 | 39 | auto result_rvalue = fplus::replace_elems(4, 0, std::vector({ 1, 3, 0, 0, 7 })); 40 | REQUIRE_EQ(result_rvalue, std::vector({ 1, 3, 0, 0, 7 })); 41 | } 42 | 43 | TEST_CASE("replace_test - replace_tokens") 44 | { 45 | const std::string source = "haha"; 46 | const std::string dest = "hihi"; 47 | const std::string input = "oh, hahaha!"; 48 | auto result = fplus::replace_tokens(source, dest, input); 49 | REQUIRE_EQ(result, std::string("oh, hihiha!")); 50 | } 51 | -------------------------------------------------------------------------------- /test/search_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace { 12 | bool is_even(int value) 13 | { 14 | return value % 2 == 0; 15 | } 16 | } 17 | 18 | TEST_CASE("search_test - find_first_by") 19 | { 20 | std::vector v = { 1, 3, 4, 6, 9 }; 21 | auto result = fplus::find_first_by(is_even, v); 22 | REQUIRE_EQ(result, fplus::just(4)); 23 | } 24 | 25 | TEST_CASE("search_test - find_first_by_nothing_found") 26 | { 27 | std::vector v = { 1, 3, 5, 7, 9 }; 28 | auto result = fplus::find_first_by(is_even, v); 29 | REQUIRE_EQ(result, fplus::nothing()); 30 | } 31 | 32 | TEST_CASE("search_test - find_last_by") 33 | { 34 | std::vector v = { 1, 3, 4, 6, 9 }; 35 | auto result = fplus::find_last_by(is_even, v); 36 | REQUIRE_EQ(result, fplus::just(6)); 37 | } 38 | 39 | TEST_CASE("search_test - find_last_by_nothing_found") 40 | { 41 | std::vector v = { 1, 3, 5, 7, 9 }; 42 | auto result = fplus::find_first_by(is_even, v); 43 | REQUIRE_EQ(result, fplus::nothing()); 44 | } 45 | 46 | TEST_CASE("search_test - find_first_idx_by") 47 | { 48 | std::vector v = { 1, 3, 4, 6, 9 }; 49 | auto result = fplus::find_first_idx_by(is_even, v); 50 | REQUIRE_EQ(result, fplus::just(2)); 51 | } 52 | 53 | TEST_CASE("search_test - find_first_idx_by_nothing_found") 54 | { 55 | std::vector v = { 1, 3, 5, 7, 9 }; 56 | auto result = fplus::find_first_idx_by(is_even, v); 57 | REQUIRE_EQ(result, fplus::nothing()); 58 | } 59 | 60 | TEST_CASE("search_test - find_last_idx_by") 61 | { 62 | std::vector v = { 1, 3, 4, 6, 9 }; 63 | auto result = fplus::find_last_idx_by(is_even, v); 64 | REQUIRE_EQ(result, fplus::just(3)); 65 | } 66 | 67 | TEST_CASE("search_test - find_last_idx_by_nothing_found") 68 | { 69 | std::vector v = { 1, 3, 5, 7, 9 }; 70 | auto result = fplus::find_last_idx_by(is_even, v); 71 | REQUIRE_EQ(result, fplus::nothing()); 72 | } 73 | 74 | TEST_CASE("search_test - find_first_idx") 75 | { 76 | std::vector v = { 1, 3, 4, 4, 9 }; 77 | auto result = fplus::find_first_idx(4, v); 78 | REQUIRE_EQ(result, fplus::just(2)); 79 | } 80 | 81 | TEST_CASE("search_test - find_first_idx_nothing_found") 82 | { 83 | std::vector v = { 1, 3, 5, 7, 9 }; 84 | auto result = fplus::find_first_idx(4, v); 85 | REQUIRE_EQ(result, fplus::nothing()); 86 | } 87 | 88 | TEST_CASE("search_test - find_last_idx") 89 | { 90 | std::vector v = { 1, 3, 4, 4, 9 }; 91 | auto result = fplus::find_last_idx(4, v); 92 | REQUIRE_EQ(result, fplus::just(3)); 93 | } 94 | 95 | TEST_CASE("search_test - find_last_idx_nothing_found") 96 | { 97 | std::vector v = { 1, 3, 5, 7, 9 }; 98 | auto result = fplus::find_last_idx(4, v); 99 | REQUIRE_EQ(result, fplus::nothing()); 100 | } 101 | 102 | TEST_CASE("search_test - find_all_idxs_by") 103 | { 104 | std::vector v = { 1, 3, 4, 6, 9 }; 105 | auto result = fplus::find_all_idxs_by(is_even, v); 106 | REQUIRE_EQ(result, std::vector({ 2, 3 })); 107 | } 108 | 109 | TEST_CASE("search_test - find_last_idxs_by_nothing_found") 110 | { 111 | std::vector v = { 1, 3, 5, 7, 9 }; 112 | auto result = fplus::find_all_idxs_by(is_even, v); 113 | REQUIRE(result.empty()); 114 | } 115 | 116 | TEST_CASE("search_test - find_all_idxs_of") 117 | { 118 | std::vector v = { 1, 3, 4, 4, 9 }; 119 | auto result = fplus::find_all_idxs_of(4, v); 120 | REQUIRE_EQ(result, std::vector({ 2, 3 })); 121 | } 122 | 123 | TEST_CASE("search_test - find_all_instances_of_token") 124 | { 125 | const std::string token = "haha"; 126 | const std::string input = "oh, hahaha!"; 127 | auto result = fplus::find_all_instances_of_token(token, input); 128 | REQUIRE_EQ(result, std::vector({ 4, 6 })); 129 | } 130 | 131 | TEST_CASE("search_test - find_last_idxs_of_nothing_found") 132 | { 133 | std::vector v = { 1, 3, 5, 7, 9 }; 134 | auto result = fplus::find_all_idxs_of(4, v); 135 | REQUIRE(result.empty()); 136 | } 137 | 138 | TEST_CASE("search_test - find_all_instances_of_token_oversized_token") 139 | { 140 | const std::string token = "hahahahaha"; 141 | const std::string input = "oh, hahaha!"; 142 | auto result = fplus::find_all_instances_of_token(token, input); 143 | REQUIRE(result.empty()); 144 | } 145 | 146 | TEST_CASE("search_test - find_all_instances_of_token_non_overlapping") 147 | { 148 | const std::string token = "haha"; 149 | const std::string input = "oh, hahaha!"; 150 | auto result = fplus::find_all_instances_of_token_non_overlapping(token, input); 151 | REQUIRE_EQ(result, std::vector({ 4 })); 152 | } 153 | 154 | TEST_CASE("search_test - find_first_instance_of_token") 155 | { 156 | const std::string token = "haha"; 157 | const std::string input = "oh, hahaha!"; 158 | auto result = fplus::find_first_instance_of_token(token, input); 159 | REQUIRE_EQ(result, fplus::just(4)); 160 | } 161 | 162 | TEST_CASE("search_test - find_first_instance_of_token_at_end") 163 | { 164 | const std::string token = "haha"; 165 | const std::string input = "oh, haha"; 166 | auto result = fplus::find_first_instance_of_token(token, input); 167 | REQUIRE_EQ(result, fplus::just(4)); 168 | } 169 | 170 | TEST_CASE("search_test - find_first_instance_of_token_nothing") 171 | { 172 | const std::string token = "hihi"; 173 | const std::string input = "oh, haha"; 174 | auto result = fplus::find_first_instance_of_token(token, input); 175 | REQUIRE_EQ(result, fplus::nothing()); 176 | } 177 | 178 | TEST_CASE("search_test - find_first_instance_of_token_oversized_token") 179 | { 180 | const std::string token = "hahahahaha"; 181 | const std::string input = "oh, hahaha!"; 182 | auto result = fplus::find_first_instance_of_token(token, input); 183 | REQUIRE_EQ(result, fplus::nothing()); 184 | } 185 | -------------------------------------------------------------------------------- /test/sets_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | TEST_CASE("set_test - set functions") 11 | { 12 | using namespace fplus; 13 | using IntSet = std::set; 14 | using setVector = std::vector; 15 | using IntUnordSet = std::unordered_set; 16 | using unordSetVector = std::vector; 17 | using StringUnordSet = std::unordered_set; 18 | 19 | // std::set tests 20 | REQUIRE(set_includes(IntSet({ 0, 1, 2, 3 }), IntSet({ 0, 2 }))); 21 | REQUIRE_FALSE(set_includes(IntSet({ 0, 2 }), IntSet({ 0, 1, 2, 3 }))); 22 | REQUIRE_FALSE(set_includes(IntSet({ 0, 1, 2, 3 }), IntSet({ 2, 3, 4, 5 }))); 23 | REQUIRE_EQ(set_merge(IntSet({ 0, 1, 2, 3 }), IntSet({ 2, 3, 4, 5 })), IntSet({ 0, 1, 2, 3, 4, 5 })); 24 | REQUIRE_EQ(set_merge(IntSet({ 0, 1, 2, 3 }), IntSet({ 0, 2 })), IntSet({ 0, 1, 2, 3 })); 25 | REQUIRE_EQ(set_intersection(IntSet({ 0, 1, 2, 3 }), IntSet({ 2, 3, 4, 5 })), IntSet({ 2, 3 })); 26 | REQUIRE_EQ(set_difference(IntSet({ 0, 1, 2, 3 }), IntSet({ 2, 3, 4, 5 })), IntSet({ 0, 1 })); 27 | REQUIRE_EQ(set_symmetric_difference(IntSet({ 0, 1, 2, 3 }), IntSet({ 2, 3, 4, 5 })), IntSet({ 0, 1, 4, 5 })); 28 | REQUIRE_EQ(set_intersection(IntSet({ 0, 1, 2, 3 }), IntSet({ 2, 3, 4, 5 })), IntSet({ 2, 3 })); 29 | REQUIRE_EQ(sets_intersection(setVector({ IntSet({ 0, 1, 2, 3 }), IntSet({ 2, 3, 4, 5 }), IntSet({ 0, 2 }) })), IntSet({ 2 })); 30 | 31 | // set::unordered_set tests 32 | REQUIRE(unordered_set_includes(IntUnordSet({ 0, 1, 2, 3 }), IntUnordSet({ 0, 2 }))); 33 | REQUIRE_FALSE(unordered_set_includes(IntUnordSet({ 0, 2 }), IntUnordSet({ 0, 1, 2, 3 }))); 34 | REQUIRE_FALSE(unordered_set_includes(IntUnordSet({ 0, 1, 2, 3 }), IntUnordSet({ 2, 3, 4, 5 }))); 35 | REQUIRE_EQ(unordered_set_merge(IntUnordSet({ 0, 1, 2, 3 }), IntUnordSet({ 2, 3, 4, 5 })), IntUnordSet({ 0, 1, 2, 3, 4, 5 })); 36 | REQUIRE_EQ(unordered_set_merge(IntUnordSet({ 0, 1, 2, 3 }), IntUnordSet({ 0, 2 })), IntUnordSet({ 0, 1, 2, 3 })); 37 | REQUIRE_EQ(unordered_set_intersection(IntUnordSet({ 0, 1, 2, 3 }), IntUnordSet({ 2, 3, 4, 5 })), IntUnordSet({ 2, 3 })); 38 | REQUIRE_EQ(unordered_set_difference(IntUnordSet({ 0, 1, 2, 3 }), IntUnordSet({ 2, 3, 4, 5 })), IntUnordSet({ 0, 1 })); 39 | REQUIRE_EQ(unordered_set_symmetric_difference(IntUnordSet({ 0, 1, 2, 3 }), IntUnordSet({ 2, 3, 4, 5 })), IntUnordSet({ 0, 1, 4, 5 })); 40 | REQUIRE_EQ(unordered_set_intersection(IntUnordSet({ 0, 1, 2, 3 }), IntUnordSet({ 2, 3, 4, 5 })), IntUnordSet({ 2, 3 })); 41 | REQUIRE_EQ(unordered_sets_intersection(unordSetVector({ IntUnordSet({ 0, 1, 2, 3 }), IntUnordSet({ 2, 3, 4, 5 }), IntUnordSet({ 0, 2 }) })), IntUnordSet({ 2 })); 42 | 43 | REQUIRE_EQ(unordered_set_merge(StringUnordSet({ "yes", "no", "hello" }), StringUnordSet({ "hello", "what" })), StringUnordSet({ "yes", "no", "what", "hello" })); 44 | 45 | REQUIRE(set_is_disjoint(IntSet({ 0, 1, 3 }), IntSet({ 2, 4 }))); 46 | REQUIRE_FALSE(set_is_disjoint(IntSet({ 0, 1, 3 }), IntSet({ 2, 1 }))); 47 | 48 | REQUIRE(unordered_set_is_disjoint(IntUnordSet({ 0, 1, 3 }), IntUnordSet({ 2, 4 }))); 49 | REQUIRE_FALSE(unordered_set_is_disjoint(IntUnordSet({ 0, 1, 3 }), IntUnordSet({ 2, 1 }))); 50 | } 51 | -------------------------------------------------------------------------------- /test/shared_ref_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | namespace { 11 | std::vector logs; 12 | 13 | void log(const std::string& str) 14 | { 15 | logs.push_back(str); 16 | } 17 | 18 | struct test { 19 | int m_x; 20 | 21 | test(int x) 22 | : m_x(x) 23 | { 24 | log("test(" + fplus::show(m_x) + ")"); 25 | } 26 | test(const test& t) 27 | : m_x(t.m_x) 28 | { 29 | log("test(const test& " + fplus::show(m_x) + ")"); 30 | } 31 | test(test&& t) 32 | : m_x(std::move(t.m_x)) 33 | { 34 | log("test(test&& " + fplus::show(m_x) + ")"); 35 | } 36 | 37 | test& operator=(int x) 38 | { 39 | m_x = x; 40 | log("test::operator=(" + fplus::show(m_x) + ")"); 41 | return *this; 42 | } 43 | test& operator=(const test& t) 44 | { 45 | m_x = t.m_x; 46 | log("test::operator=(const test& " + fplus::show(m_x) + ")"); 47 | return *this; 48 | } 49 | test& operator=(test&& t) 50 | { 51 | m_x = std::move(t.m_x); 52 | log("test::operator=(test&& " + fplus::show(m_x) + ")"); 53 | return *this; 54 | } 55 | 56 | ~test() { log("~test(" + fplus::show(m_x) + ")"); } 57 | }; 58 | } 59 | 60 | TEST_CASE("shared_ref_test - full") 61 | { 62 | using namespace fplus; 63 | 64 | { 65 | auto ref = make_shared_ref(1); 66 | auto ref2 = ref; 67 | 68 | *ref2 = test(5); 69 | } 70 | { 71 | test o(2); 72 | auto ref = make_shared_ref(std::move(o)); 73 | } 74 | 75 | const std::vector logs_dest = { 76 | "test(1)", 77 | "test(5)", 78 | "test::operator=(test&& 5)", 79 | "~test(5)", 80 | "~test(5)", 81 | "test(2)", 82 | "test(test&& 2)", 83 | "~test(2)", 84 | "~test(2)" 85 | }; 86 | 87 | REQUIRE_EQ(logs, logs_dest); 88 | } 89 | -------------------------------------------------------------------------------- /test/show_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | namespace { 11 | typedef std::vector IntVector; 12 | IntVector xs = { 1, 2, 2, 3, 2 }; 13 | std::string xsShown("[1, 2, 2, 3, 2]"); 14 | 15 | typedef std::vector StringVector; 16 | StringVector stringVec = { "foo", "bar" }; 17 | std::string stringVecShown("[foo, bar]"); 18 | 19 | typedef std::set IntSet; 20 | IntSet xsSet = { 1, 2, 3 }; 21 | std::string xsSetShown("[1, 2, 3]"); 22 | 23 | typedef std::set StringSet; 24 | StringSet stringSet = { "foo", "bar" }; 25 | std::string stringSetShown("[bar, foo]"); 26 | } 27 | 28 | TEST_CASE("show_test - show") 29 | { 30 | using namespace fplus; 31 | std::map mapToShow = { { 1, "one" }, { 2, "two" } }; 32 | REQUIRE_EQ(show_cont(mapToShow), "[(1, one), (2, two)]"); 33 | 34 | REQUIRE_EQ(show_cont(xs), xsShown); 35 | REQUIRE_EQ(show(xs), xsShown); 36 | 37 | REQUIRE_EQ(show_cont(xsSet), xsSetShown); 38 | REQUIRE_EQ(show(xsSet), xsSetShown); 39 | 40 | REQUIRE_EQ(show_cont(stringVec), stringVecShown); 41 | REQUIRE_EQ(show(stringVec), stringVecShown); 42 | 43 | REQUIRE_EQ(show_cont(stringSet), stringSetShown); 44 | REQUIRE_EQ(show(stringSet), stringSetShown); 45 | 46 | REQUIRE_EQ(show_cont_with(", ", xs), xsShown); 47 | std::string xsShownNLs = "(1,2,\n" 48 | " 2,3,\n" 49 | " 2)"; 50 | REQUIRE_EQ(show_cont_with_frame_and_newlines(",", "(", ")", xs, 2), xsShownNLs); 51 | REQUIRE_EQ(show(1), "1"); 52 | 53 | REQUIRE_EQ(show(std::vector>({ { 1, 2, 3 }, { 4, 5, 6 } })), "[[1, 2, 3], [4, 5, 6]]"); 54 | } 55 | 56 | TEST_CASE("show_test - show_float") 57 | { 58 | using namespace fplus; 59 | const double pi = 3.14159; 60 | REQUIRE_EQ(show_float(0, 3, pi), "3.142"); 61 | REQUIRE_EQ(show_float(1, 3, pi), "3.142"); 62 | REQUIRE_EQ(show_float(2, 3, pi), "03.142"); 63 | REQUIRE_EQ(show_float(3, 3, pi), "003.142"); 64 | REQUIRE_EQ(show_float(1, 2, pi), "3.14"); 65 | REQUIRE_EQ(show_float(1, 4, pi), "3.1416"); 66 | REQUIRE_EQ(show_float(1, 7, pi), "3.1415900"); 67 | REQUIRE_EQ(show_float(0, 3, -pi), "-3.142"); 68 | REQUIRE_EQ(show_float(1, 3, -pi), "-3.142"); 69 | REQUIRE_EQ(show_float(2, 3, -pi), "-3.142"); 70 | REQUIRE_EQ(show_float(3, 3, -pi), "-03.142"); 71 | REQUIRE_EQ(show_float(4, 3, -pi), "-003.142"); 72 | REQUIRE_EQ(show_float(0, 3, 0.142), "0.142"); 73 | REQUIRE_EQ(show_float(1, 3, 0.142), "0.142"); 74 | REQUIRE_EQ(show_float(2, 3, 0.142), "00.142"); 75 | REQUIRE_EQ(fill_left(' ', 8, show_float(0, 3, -pi)), " -3.142"); 76 | 77 | REQUIRE_EQ(show_float_fill_left(' ', 8, 3, pi), " 3.142"); 78 | REQUIRE_EQ(show_float_fill_left(' ', 8, 6, pi), "3.141590"); 79 | REQUIRE_EQ(show_float_fill_left(' ', 8, 3, -pi), " -3.142"); 80 | REQUIRE_EQ(show_float_fill_left(' ', 2, 3, -pi), "-3.142"); 81 | 82 | REQUIRE_EQ(show_fill_left(' ', 4, 3), " 3"); 83 | REQUIRE_EQ(show_fill_left('0', 4, 3), "0003"); 84 | REQUIRE_EQ(show_fill_left(' ', 4, 12345), "12345"); 85 | 86 | REQUIRE_EQ(show_fill_right(' ', 4, 3), "3 "); 87 | REQUIRE_EQ(show_fill_right(' ', 4, 12345), "12345"); 88 | 89 | std::tuple t1(10, "Test", 3.14); 90 | std::list lt1 = stream(t1); 91 | REQUIRE_EQ(show_cont(lt1), "[10, Test, 3.14]"); 92 | } 93 | -------------------------------------------------------------------------------- /test/show_versions.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | TEST_CASE("show_versions - print_defines") 11 | { 12 | #ifdef __cplusplus 13 | std::cout << "__cplusplus: " << __cplusplus << std::endl; 14 | #endif 15 | 16 | #ifdef _LIBCPP_VERSION 17 | std::cout << "_LIBCPP_VERSION: " << _LIBCPP_VERSION << std::endl; 18 | #endif 19 | 20 | #ifdef _LIBCPP_ABI_VERSION 21 | std::cout << "_LIBCPP_ABI_VERSION: " << _LIBCPP_ABI_VERSION << std::endl; 22 | #endif 23 | 24 | #ifdef _MSC_VER 25 | std::cout << "_MSC_VER: " << _MSC_VER << std::endl; 26 | #endif 27 | 28 | #ifdef __GLIBCPP__ 29 | std::cout << "__GLIBCPP__: " << __GLIBCPP__ << std::endl; 30 | #endif 31 | 32 | #ifdef __GLIBCXX__ 33 | std::cout << "__GLIBCXX__: " << __GLIBCXX__ << std::endl; 34 | #endif 35 | } -------------------------------------------------------------------------------- /test/side_effects_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | TEST_CASE("side_effects_test - execute") 12 | { 13 | using namespace fplus; 14 | std::vector buffer; 15 | auto push_one_return_true = [&]() -> bool { buffer.push_back(1); return true; }; 16 | execute_effect(push_one_return_true); 17 | REQUIRE_EQ(execute_effect(push_one_return_true), true); 18 | REQUIRE_EQ(buffer, std::vector({ 1, 1 })); 19 | } 20 | 21 | TEST_CASE("side_effects_test - execute_serially") 22 | { 23 | using namespace fplus; 24 | std::vector buffer; 25 | auto push_one_return_true = [&]() -> bool { buffer.push_back(1); return true; }; 26 | auto push_3_ones = execute_serially(replicate(3, push_one_return_true))(); 27 | REQUIRE_EQ(buffer, std::vector({ 1, 1, 1 })); 28 | } 29 | 30 | TEST_CASE("side_effects_test - execute_parallelly") 31 | { 32 | using namespace fplus; 33 | auto return_one = [&]() { return 1; }; 34 | REQUIRE_EQ(execute_parallelly(replicate(4, return_one))(), std::vector({ 1, 1, 1, 1 })); 35 | } 36 | 37 | TEST_CASE("side_effects_test - execute_max_n_times_until_success") 38 | { 39 | using namespace fplus; 40 | std::vector buffer; 41 | auto push_one_return_false = [&]() -> bool { buffer.push_back(1); return false; }; 42 | execute_max_n_times_until_success(5, push_one_return_false)(); 43 | execute_max_n_times_until_success(3, push_one_return_false, 1)(); 44 | REQUIRE_EQ(buffer, std::vector({ 1, 1, 1, 1, 1, 1, 1, 1 })); 45 | } 46 | 47 | TEST_CASE("side_effects_test - execute_serially_until_success") 48 | { 49 | using namespace fplus; 50 | std::vector buffer; 51 | auto push_one_return_true = [&]() -> bool { buffer.push_back(1); return true; }; 52 | auto push_one_return_false = [&]() -> bool { buffer.push_back(1); return false; }; 53 | typedef std::function BoolReturningFunction; 54 | typedef std::vector BoolReturningFunctions; 55 | execute_serially_until_success(BoolReturningFunctions({ push_one_return_false, push_one_return_false, push_one_return_true, push_one_return_true }))(); 56 | REQUIRE_EQ(buffer, std::vector({ 1, 1, 1 })); 57 | } 58 | 59 | TEST_CASE("side_effects_test - execute_serially_until_failure") 60 | { 61 | using namespace fplus; 62 | std::vector buffer; 63 | auto push_one_return_true = [&]() -> bool { buffer.push_back(1); return true; }; 64 | auto push_one_return_false = [&]() -> bool { buffer.push_back(1); return false; }; 65 | typedef std::function BoolReturningFunction; 66 | typedef std::vector BoolReturningFunctions; 67 | execute_serially_until_failure(BoolReturningFunctions({ push_one_return_true, push_one_return_false, push_one_return_false }))(); 68 | REQUIRE_EQ(buffer, std::vector({ 1, 1 })); 69 | } 70 | 71 | TEST_CASE("side_effects_test - execute_parallelly_atomic") 72 | { 73 | using namespace fplus; 74 | std::atomic atomic_int(0); 75 | auto inc_atomic_int_return_true = [&]() -> bool { ++atomic_int; return true; }; 76 | execute_parallelly(replicate(4, inc_atomic_int_return_true))(); 77 | REQUIRE_EQ(atomic_int.load(), 4); 78 | } 79 | 80 | TEST_CASE("side_effects_test - for_each (serial and parallel") 81 | { 82 | using namespace fplus; 83 | constexpr size_t nb_elems = 100; 84 | 85 | for (auto i : fplus::numbers(0, 3)) { 86 | std::vector xs(nb_elems, 0); 87 | auto idxs = fplus::numbers(0, nb_elems); 88 | auto fill_one_cell = [&](size_t idx) { xs[idx] = 1; }; 89 | if (i == 0) 90 | fplus::for_each(fill_one_cell, idxs); 91 | else if (i == 1) 92 | fplus::parallel_for_each(fill_one_cell, idxs); 93 | else if (i == 2) 94 | fplus::parallel_for_each_n_threads(size_t(4), fill_one_cell, idxs); 95 | size_t nb_filled = fplus::count_if([](int v) { return v == 1; }, xs); 96 | REQUIRE_EQ(nb_filled, nb_elems); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /test/stopwatch_test.cpp: -------------------------------------------------------------------------------- 1 | // (See accompanying file LICENSE_1_0.txt or copy at 2 | // http://www.boost.org/LICENSE_1_0.txt) 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std::chrono_literals; 9 | 10 | template 11 | auto invoke_n_times(int n, Function f) 12 | { 13 | for (int i = 0; i < n; i++) { 14 | f(); 15 | } 16 | } 17 | 18 | TEST_CASE("Timer - test_accuracy") 19 | { 20 | #ifdef NDEBUG // only on release builds 21 | using namespace fplus; 22 | using namespace std::chrono_literals; 23 | 24 | auto measure_delta = []() { 25 | fplus::stopwatch t; 26 | std::this_thread::sleep_for(0.05s); 27 | auto duration = t.elapsed(); 28 | auto delta = duration - 0.05; 29 | return fabs(delta); 30 | }; 31 | 32 | // we ask for a sleep of 0.05s, but we will have a duration that can be higher 33 | // (up to 15 ms higher, since the cpu scheduler might switch to another process during this sleep) 34 | // to account for the cpu/task scheduler 35 | double max_acceptable_delta__task_scheduler = 0.2; 36 | 37 | // One run 38 | { 39 | auto delta = measure_delta(); 40 | REQUIRE_LT(delta, max_acceptable_delta__task_scheduler); 41 | } 42 | 43 | // 10 consecutive runs (total duration = 0.5 seconds) 44 | { 45 | std::vector deltas; 46 | invoke_n_times(10, [&]() { 47 | deltas.push_back(measure_delta()); 48 | }); 49 | auto mean_dev = fplus::mean_stddev(deltas); 50 | REQUIRE_LT(mean_dev.first, 0.15); 51 | REQUIRE_LT(mean_dev.second, 0.07); 52 | } 53 | #endif 54 | } 55 | -------------------------------------------------------------------------------- /test/stringtools_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | TEST_CASE("stringtools_test - trim") 12 | { 13 | using namespace fplus; 14 | std::string untrimmed = " \n \t foo "; 15 | REQUIRE_EQ(trim_whitespace_left(untrimmed), "foo "); 16 | REQUIRE_EQ(trim_whitespace_right(untrimmed), " \n \t foo"); 17 | REQUIRE_EQ(trim_whitespace(untrimmed), "foo"); 18 | } 19 | 20 | TEST_CASE("stringtools_test - split") 21 | { 22 | using namespace fplus; 23 | std::string text = "Hi,\nI am a\r\n***strange***\n\rstring."; 24 | std::vector textAsLinesWithEmty = { 25 | std::string("Hi,"), 26 | std::string("I am a"), 27 | std::string("***strange***"), 28 | std::string(""), 29 | std::string("string.") 30 | }; 31 | std::vector textAsLinesWithoutEmpty = { 32 | std::string("Hi,"), 33 | std::string("I am a"), 34 | std::string("***strange***"), 35 | std::string("string.") 36 | }; 37 | std::vector textAsWords = { 38 | std::string("Hi"), 39 | std::string("I"), 40 | std::string("am"), 41 | std::string("a"), 42 | std::string("strange"), 43 | std::string("string") 44 | }; 45 | std::vector textSplitBySpaceOnly = { 46 | std::string("Hi,\nI"), 47 | std::string("am"), 48 | std::string("a\r\n***strange***\n\rstring.") 49 | }; 50 | std::vector textSplitBySpaceAndCommaAndLine = { 51 | std::string("Hi"), 52 | std::string("I"), 53 | std::string("am"), 54 | std::string("a"), 55 | std::string("***strange***"), 56 | std::string("string.") 57 | }; 58 | 59 | REQUIRE_EQ(split_lines(false, std::string("")).size(), 0); 60 | REQUIRE_EQ(split_lines(true, std::string("")).size(), 1); 61 | REQUIRE_EQ(split_lines(true, text), textAsLinesWithEmty); 62 | REQUIRE_EQ(split_lines(false, text), textAsLinesWithoutEmpty); 63 | REQUIRE_EQ(split_words(false, text), textAsWords); 64 | REQUIRE_EQ(split(' ', false, text), textSplitBySpaceOnly); 65 | REQUIRE_EQ(split_one_of(std::string { " ,\r\n" }, false, text), textSplitBySpaceAndCommaAndLine); 66 | } 67 | 68 | TEST_CASE("stringtools_test - to_string_filled") 69 | { 70 | using namespace fplus; 71 | REQUIRE_EQ(to_string_fill_left('0', 5, 42), std::string("00042")); 72 | REQUIRE_EQ(to_string_fill_right(' ', 5, 42), std::string("42 ")); 73 | } 74 | 75 | TEST_CASE("stringtools_test - to_lower_case") 76 | { 77 | using namespace fplus; 78 | REQUIRE_EQ(to_lower_case(std::string("ChaRacTer&WorDs23")), std::string("character&words23")); 79 | } 80 | 81 | TEST_CASE("stringtools_test - to_upper_case") 82 | { 83 | using namespace fplus; 84 | REQUIRE_EQ(to_upper_case(std::string("ChaRacTer&WorDs34")), std::string("CHARACTER&WORDS34")); 85 | } 86 | -------------------------------------------------------------------------------- /test/timed_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // Utility functions 15 | namespace { 16 | void require_are_execution_times_close(double t1, double t2) 17 | { 18 | // up to 30 ms difference, since the cpu scheduler might switch to another process during a sleep 19 | double max_acceptable_delta__task_scheduler = 0.03; 20 | #if defined(__APPLE__) 21 | max_acceptable_delta__task_scheduler = 0.2; 22 | #endif 23 | REQUIRE(fabs(t1 - t2) < max_acceptable_delta__task_scheduler); 24 | } 25 | 26 | template 27 | void require_are_timed_equal(const fplus::timed& a, const fplus::timed& b) 28 | { 29 | REQUIRE(a.get() == b.get()); 30 | require_are_execution_times_close(a.time_in_s(), b.time_in_s()); 31 | } 32 | 33 | void sleep_seconds(double sleep_seconds) 34 | { 35 | long long sleep_ns = static_cast(sleep_seconds * 1E9); 36 | std::this_thread::sleep_for(std::chrono::nanoseconds(sleep_ns)); 37 | } 38 | 39 | int add(int a, int b) // a simple function that will be decorated 40 | { 41 | sleep_seconds(0.002); 42 | return a + b; 43 | } 44 | 45 | void void_function() 46 | { 47 | sleep_seconds(0.002); 48 | } 49 | } 50 | 51 | // Test timed class 52 | TEST_CASE("timed - ctor") 53 | { 54 | using namespace fplus; 55 | { 56 | // default constructor 57 | fplus::timed v; 58 | REQUIRE(v.get() == doctest::Approx(0.)); 59 | REQUIRE(v.time_in_s() == doctest::Approx(0.)); 60 | } 61 | { 62 | // T + time constructor 63 | fplus::timed v(1., 3.); 64 | REQUIRE(v.get() == doctest::Approx(1.)); 65 | REQUIRE(v.time_in_s() == doctest::Approx(3.)); 66 | } 67 | { 68 | // timed constructor 69 | fplus::timed v1(2.); 70 | auto v2(v1); 71 | REQUIRE(v1.time_in_s() == doctest::Approx(v2.time_in_s())); 72 | REQUIRE(v1.get() == doctest::Approx(v2.get())); 73 | } 74 | } 75 | 76 | TEST_CASE("timed - operator=") 77 | { 78 | { 79 | auto v1 = fplus::timed(1.); 80 | auto v2 = fplus::timed(2.); 81 | REQUIRE(v1.get() != doctest::Approx(v2.get())); 82 | v2 = v1; 83 | REQUIRE(v1.get() == doctest::Approx(v2.get())); 84 | } 85 | } 86 | 87 | TEST_CASE("timed - show_timed") 88 | { 89 | { 90 | fplus::timed v(42, 1); 91 | auto s = show_timed(v); 92 | REQUIRE_EQ(s, "42 (1000ms)"); 93 | } 94 | } 95 | 96 | TEST_CASE("timed - duration_in_s") 97 | { 98 | { 99 | fplus::timed v(42, 1.2345); 100 | auto d = v.duration_in_s(); 101 | double seconds = d.count(); 102 | REQUIRE(seconds == doctest::Approx(1.2345)); 103 | } 104 | } 105 | 106 | // Test make_timed_function 107 | TEST_CASE("make_timed_function") 108 | { 109 | using namespace fplus; 110 | 111 | { 112 | // Test decorated function 113 | auto add_timed = make_timed_function(add); 114 | auto result = add_timed(39, 3); 115 | auto expected = timed(42, 0.02); 116 | require_are_timed_equal(result, expected); 117 | } 118 | 119 | { 120 | // Test void function 121 | auto void_function_timed = fplus::make_timed_void_function(void_function); 122 | auto execution_time = void_function_timed(); 123 | require_are_execution_times_close(execution_time, 0.02); 124 | } 125 | 126 | { 127 | // Test decorated lambda 128 | auto sub = [](int a, int b) -> int { 129 | sleep_seconds(0.03); 130 | return a - b; 131 | }; 132 | auto sub_timed = make_timed_function(sub); 133 | auto result = sub_timed(45, 3); 134 | auto expected = timed(42, 0.03); 135 | require_are_timed_equal(result, expected); 136 | } 137 | 138 | { 139 | // Test decorated void lambda 140 | auto fn = []() { 141 | sleep_seconds(0.03); 142 | }; 143 | auto fn_timed = make_timed_void_function(fn); 144 | auto execution_time = fn_timed(); 145 | require_are_execution_times_close(execution_time, 0.03); 146 | } 147 | 148 | { 149 | // Test std::function 150 | auto sub_lambda = [](int a, int b) -> int { 151 | sleep_seconds(0.03); 152 | return a - b; 153 | }; 154 | std::function sub = sub_lambda; 155 | auto sub_timed = make_timed_function(sub); 156 | auto result = sub_timed(45, 3); 157 | auto expected = timed(42, 0.03); 158 | require_are_timed_equal(result, expected); 159 | } 160 | 161 | { 162 | using Ints = std::vector; 163 | 164 | Ints ascending_numbers = fplus::numbers(0, 1000); 165 | Ints shuffled_numbers = fplus::shuffle(std::mt19937::default_seed, ascending_numbers); 166 | 167 | auto sort_func = [](const Ints& values) { return fplus::sort(values); }; 168 | auto sort_bench = fplus::make_timed_function(sort_func); 169 | 170 | auto sorted_numbers = sort_bench(shuffled_numbers); 171 | REQUIRE_EQ(sorted_numbers.get(), ascending_numbers); 172 | // sorting 1000 numbers should require less than 0.1 seconds (in practice it requires about 0.2ms) 173 | REQUIRE_LT(sorted_numbers.time_in_s(), 0.1); 174 | } 175 | } 176 | 177 | // 178 | // The test below asserts that variadic arguments containing modifiable references are correctly forwarded 179 | // 180 | bool function_with_input_output_params(int input1, int& output2) 181 | { 182 | output2 = input1 + 1; 183 | return true; 184 | } 185 | void void_function_with_input_output_params(int input1, int& output2) 186 | { 187 | output2 = input1 + 1; 188 | } 189 | TEST_CASE("timed_with_input_output_args") 190 | { 191 | // With non void function 192 | { 193 | auto timed_function_with_input_output_params = fplus::make_timed_function(function_with_input_output_params); 194 | 195 | int input1 = 1; 196 | int output2 = 42; 197 | 198 | fplus::timed r = timed_function_with_input_output_params(input1, output2); 199 | REQUIRE_EQ(output2, 2); 200 | REQUIRE(r.get()); 201 | } 202 | // With void function 203 | { 204 | auto timed_void_function_with_input_output_params = fplus::make_timed_void_function(void_function_with_input_output_params); 205 | 206 | int input1 = 1; 207 | int output2 = 42; 208 | 209 | timed_void_function_with_input_output_params(input1, output2); 210 | REQUIRE_EQ(output2, 2); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /test/tree_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | namespace { 11 | typedef std::pair IntPair; 12 | typedef std::vector IntVector; 13 | typedef std::vector IntPairVector; 14 | typedef fplus::tree IntPairTree; 15 | typedef fplus::tree IntTree; 16 | typedef std::vector IntPairTreeVector; 17 | } 18 | 19 | TEST_CASE("tree_test - are_trees_equal") 20 | { 21 | using namespace fplus; 22 | 23 | const IntPairTree a = { { 0, 8 }, { { { 0, 4 }, {} }, { { 5, 7 }, {} } } }; 24 | 25 | const IntPairTree b = { { 0, 8 }, { { { 5, 7 }, {} }, { { 0, 4 }, {} } } }; 26 | 27 | REQUIRE(are_trees_equal(a, b)); 28 | } 29 | 30 | TEST_CASE("tree_test - sequence_to_tree small") 31 | { 32 | using namespace fplus; 33 | IntPairVector elems = { 34 | { 0, 4 }, 35 | { 0, 8 }, 36 | { 5, 7 } 37 | }; 38 | 39 | const auto is_child_of = [](const IntPair& a, const IntPair& b) -> bool { 40 | return a.first >= b.first && a.second <= b.second; 41 | }; 42 | 43 | const auto result_1 = trees_from_sequence(is_child_of, elems); 44 | const auto result_2 = trees_from_sequence(is_child_of, fplus::shuffle(0, elems)); 45 | 46 | const IntPairTreeVector result = { 47 | { { 0, 8 }, { { { 0, 4 }, {} }, { { 5, 7 }, {} } } } 48 | }; 49 | 50 | REQUIRE_EQ(result_1.size(), result_2.size()); 51 | REQUIRE(all(zip_with(are_trees_equal, result_1, result_2))); 52 | 53 | REQUIRE_EQ(result_1.size(), result.size()); 54 | REQUIRE(all(zip_with(are_trees_equal, result_1, result))); 55 | } 56 | 57 | TEST_CASE("tree_test - sequence_to_tree medium") 58 | { 59 | using namespace fplus; 60 | IntPairVector elems = { 61 | { 0, 4 }, 62 | { 0, 8 }, 63 | { 5, 7 }, 64 | { 9, 10 }, 65 | { 12, 13 }, 66 | { 9, 17 }, 67 | { 11, 15 }, 68 | { 13, 14 }, 69 | { 8, 20 } 70 | }; 71 | 72 | const auto is_child_of = [](const IntPair& a, const IntPair& b) -> bool { 73 | return a.first >= b.first && a.second <= b.second; 74 | }; 75 | 76 | const auto result_1 = trees_from_sequence(is_child_of, elems); 77 | const auto result_2 = trees_from_sequence(is_child_of, fplus::shuffle(0, elems)); 78 | 79 | const IntPairTreeVector result = { 80 | { { 0, 8 }, { { { 0, 4 }, {} }, { { 5, 7 }, {} } } }, 81 | { { 8, 20 }, { 82 | { { 9, 17 }, { { { 9, 10 }, {} }, { { 11, 15 }, { { { 12, 13 }, {} }, { { 13, 14 }, {} } } } } }, 83 | } }, 84 | }; 85 | 86 | REQUIRE_EQ(result_1.size(), result_2.size()); 87 | REQUIRE(all(zip_with(are_trees_equal, result_1, result_2))); 88 | 89 | REQUIRE_EQ(result_1.size(), result.size()); 90 | REQUIRE(all(zip_with(are_trees_equal, result_1, result))); 91 | } 92 | 93 | TEST_CASE("tree_test - tree_depth") 94 | { 95 | const IntTree t = { { 0 }, { { { 1 }, { { { 2 }, {} }, { { 3 }, { { { 4 }, {} }, { { 5 }, {} } } }, { { 6 }, {} } } }, { { 7 }, {} }, { { 8 }, {} } } }; 96 | REQUIRE_EQ(tree_depth(t), 4); 97 | } 98 | 99 | TEST_CASE("tree_test - flatten_tree_depth_first") 100 | { 101 | const IntTree t = { { 0 }, { { { 1 }, { { { 2 }, {} }, { { 3 }, { { { 4 }, {} }, { { 5 }, {} } } }, { { 6 }, {} } } }, { { 7 }, {} }, { { 8 }, {} } } }; 102 | REQUIRE_EQ(flatten_tree_depth_first(t), IntVector({ 0, 1, 2, 3, 4, 5, 6, 7, 8 })); 103 | } 104 | 105 | TEST_CASE("tree_test - flatten_tree_breadth_first") 106 | { 107 | const IntTree t = { { 0 }, { { { 1 }, { { { 4 }, {} }, { { 5 }, { { { 7 }, {} }, { { 8 }, {} } } }, { { 6 }, {} } } }, { { 2 }, {} }, { { 3 }, {} } } }; 108 | REQUIRE_EQ(flatten_tree_breadth_first(t), IntVector({ 0, 1, 2, 3, 4, 5, 6, 7, 8 })); 109 | } 110 | -------------------------------------------------------------------------------- /test/variant_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 | // https://github.com/Dobiasd/FunctionalPlus 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | namespace { 11 | std::string print_output; 12 | 13 | bool print_int(int x) 14 | { 15 | print_output = "int " + std::to_string(x); 16 | return true; 17 | } 18 | 19 | bool print_double(double x) 20 | { 21 | print_output = "double " + std::to_string(fplus::round(x)); 22 | return true; 23 | } 24 | 25 | bool print_string(const std::string& str) 26 | { 27 | print_output = "string " + str; 28 | return true; 29 | } 30 | 31 | void print_int_effect(int x) 32 | { 33 | print_output = "int " + std::to_string(x); 34 | } 35 | 36 | void print_string_effect(const std::string& str) 37 | { 38 | print_output = "string " + str; 39 | } 40 | 41 | std::string show_int(int x) 42 | { 43 | return fplus::show(x); 44 | } 45 | 46 | std::string show_string(const std::string& str) 47 | { 48 | return fplus::show(str); 49 | } 50 | } 51 | 52 | TEST_CASE("variant_test - visit_one") 53 | { 54 | using namespace fplus; 55 | 56 | fplus::variant int_or_double(3); 57 | 58 | int_or_double.visit_one(print_int); 59 | REQUIRE_EQ(print_output, "int 3"); 60 | print_output.clear(); 61 | 62 | int_or_double.visit_one(print_double); 63 | REQUIRE_EQ(print_output, ""); 64 | print_output.clear(); 65 | 66 | int_or_double = 23.0; 67 | 68 | int_or_double.visit_one(print_int); 69 | REQUIRE_EQ(print_output, ""); 70 | print_output.clear(); 71 | 72 | int_or_double.visit_one(print_double); 73 | REQUIRE_EQ(print_output, "double 23"); 74 | print_output.clear(); 75 | 76 | using float_or_double = fplus::variant; 77 | using nested = fplus::variant; 78 | nested int_or_float_double_variant(fplus::variant(3.0)); 79 | 80 | const auto print_nested_double = [&](const float_or_double& f_o_d) -> int { 81 | f_o_d.visit_one(print_double); 82 | return 0; 83 | }; 84 | 85 | int_or_float_double_variant.visit_one(print_nested_double); 86 | REQUIRE_EQ(print_output, "double 3"); 87 | print_output.clear(); 88 | } 89 | 90 | TEST_CASE("variant_test - equality_test") 91 | { 92 | using namespace fplus; 93 | fplus::variant int_or_string_i(3); 94 | fplus::variant int_or_string_s(std::string("hi")); 95 | REQUIRE(int_or_string_i == int_or_string_i); 96 | REQUIRE_FALSE(int_or_string_i == int_or_string_s); 97 | } 98 | 99 | TEST_CASE("variant_test - inequality_test") 100 | { 101 | using namespace fplus; 102 | fplus::variant int_or_string_i(3); 103 | fplus::variant int_or_string_s(std::string("hi")); 104 | REQUIRE_FALSE(int_or_string_i != int_or_string_i); 105 | REQUIRE(int_or_string_i != int_or_string_s); 106 | } 107 | 108 | TEST_CASE("variant_test - visit") 109 | { 110 | using namespace fplus; 111 | 112 | // should not compile 113 | // int_or_double.visit_one(print_string); 114 | 115 | fplus::variant int_or_string(3); 116 | 117 | REQUIRE(int_or_string.is()); 118 | REQUIRE_FALSE(int_or_string.is()); 119 | 120 | int_or_string.visit(print_int, print_string); 121 | REQUIRE_EQ(print_output, "int 3"); 122 | print_output.clear(); 123 | 124 | const auto transform_result = int_or_string.transform(show_int, show_string); 125 | transform_result.visit_one(print_string); 126 | REQUIRE_EQ(print_output, "string 3"); 127 | print_output.clear(); 128 | 129 | // One can use fplus::identity to use visit_one as a getter: 130 | const fplus::maybe y = transform_result.visit_one(fplus::identity); 131 | std::cout << fplus::show_maybe(y) << std::endl; 132 | 133 | // should not compile 134 | // std::cout << int_or_string.visit(show_int, show_float) << std::endl; 135 | 136 | // should not compile 137 | // std::cout << int_or_string.visit(show_int, show_int) << std::endl; 138 | 139 | // should not compile 140 | // std::cout << int_or_string.visit(show_int, show_string, show_double) << std::endl; 141 | 142 | // should not compile 143 | // std::cout << int_or_string.visit(show_int) << std::endl; 144 | } 145 | 146 | TEST_CASE("variant_test - effect") 147 | { 148 | using namespace fplus; 149 | 150 | // should not compile 151 | // int_or_double.effect_one(print_string); 152 | 153 | fplus::variant int_or_string(3); 154 | // 155 | REQUIRE(int_or_string.is()); 156 | REQUIRE_FALSE(int_or_string.is()); 157 | // 158 | int_or_string.effect(print_int_effect, print_string_effect); 159 | REQUIRE_EQ(print_output, "int 3"); 160 | print_output.clear(); 161 | 162 | const auto transform_result = int_or_string.transform(show_int, show_string); 163 | transform_result.effect_one(print_string_effect); 164 | REQUIRE_EQ(print_output, "string 3"); 165 | print_output.clear(); 166 | } 167 | 168 | TEST_CASE("variant_test - get") 169 | { 170 | using namespace fplus; 171 | fplus::variant int_or_string_i(3); 172 | fplus::variant int_or_string_s(std::string("hi")); 173 | 174 | REQUIRE_EQ(int_or_string_i.get(), just(3)); 175 | REQUIRE_EQ(int_or_string_i.get(), nothing()); 176 | 177 | REQUIRE_EQ(int_or_string_s.get(), just("hi")); 178 | REQUIRE_EQ(int_or_string_s.get(), nothing()); 179 | 180 | // should not compile (type not in variant) 181 | // REQUIRE_EQ(int_or_string_i.get(), nothing()); 182 | // REQUIRE_EQ(int_or_string_s.get(), nothing()); 183 | } --------------------------------------------------------------------------------