pax_global_header00006660000000000000000000000064137632224540014522gustar00rootroot0000000000000052 comment=916abce0787da6c6c373d06c453a2cd684594dc2 libfplus-0.2.13/000077500000000000000000000000001376322245400134255ustar00rootroot00000000000000libfplus-0.2.13/.github/000077500000000000000000000000001376322245400147655ustar00rootroot00000000000000libfplus-0.2.13/.github/FUNDING.yml000066400000000000000000000000221376322245400165740ustar00rootroot00000000000000github: [dobiasd] libfplus-0.2.13/.gitignore000077500000000000000000000003161376322245400154200ustar00rootroot00000000000000*.sublime-project *.sublime-workspace .vscode test/temp* build build64 api_search/frontend/build api_search/frontend/elm-stuff api_search/frontend/deploy.sh api_search/frontend/src/Database.elm .mypy_cache libfplus-0.2.13/.travis.yml000066400000000000000000000124151376322245400155410ustar00rootroot00000000000000language: generic dist: bionic jobs: include: - os: linux language: python python: 3.6 compiler: gcc env: GCC_VERSION=10 - CC=gcc-10 - CXX=g++-10 addons: apt: packages: ['g++-10'] sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - os: linux language: python python: 3.6 compiler: gcc env: GCC_VERSION=9 - CC=gcc-9 - CXX=g++-9 addons: apt: packages: ['g++-9'] sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - os: linux language: python python: 3.6 compiler: gcc env: GCC_VERSION=8 - CC=gcc-8 - CXX=g++-8 addons: apt: packages: ['g++-8'] - os: linux language: python python: 3.6 compiler: gcc env: GCC_VERSION=7 - CC=gcc-7 - CXX=g++-7 addons: apt: packages: ['g++-7'] - os: linux language: python python: 3.6 compiler: gcc env: GCC_VERSION=6 - CC=gcc-6 - CXX=g++-6 addons: apt: packages: ['g++-6'] - os: linux language: python python: 3.6 compiler: gcc env: GCC_VERSION=5 - CC=gcc-5 - CXX=g++-5 addons: apt: packages: ['g++-5'] - os: linux language: python python: 3.6 compiler: clang env: CLANG_VERSION=11 - CC=clang-11 - CXX=clang++-11 addons: apt: sources: - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' packages: ['clang-11', 'libc++-11-dev', 'libc++abi-11-dev'] - os: linux language: python python: 3.6 compiler: clang env: CLANG_VERSION=10 - CC=clang-10 - CXX=clang++-10 addons: apt: sources: - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' packages: ['clang-10', 'libc++-10-dev', 'libc++abi-10-dev'] - os: linux language: python python: 3.6 compiler: clang env: CLANG_VERSION=9 - CC=clang-9 - CXX=clang++-9 addons: apt: sources: - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' packages: ['clang-9', 'libc++-9-dev', 'libc++abi-9-dev'] - os: linux language: python python: 3.6 compiler: clang env: CLANG_VERSION=8 - CC=clang-8 - CXX=clang++-8 addons: apt: packages: ['clang-8', 'libc++-8-dev', 'libc++abi-8-dev'] - os: linux language: python python: 3.6 compiler: clang env: CLANG_VERSION=7 - CC=clang-7 - CXX=clang++-7 addons: apt: packages: ['clang-7', 'libc++-7-dev', 'libc++abi-7-dev'] - os: linux language: python python: 3.6 compiler: clang env: CLANG_VERSION=6.0 LIBCXX=On - CC=clang-6.0 - CXX=clang++-6.0 addons: apt: packages: ['clang-6.0', 'g++-6'] - os: linux language: python python: 3.6 compiler: clang env: CLANG_VERSION=5.0 LIBCXX=On - CC=clang-5.0 - CXX=clang++-5.0 addons: apt: packages: ['clang-5.0', 'g++-6'] - os: linux language: python python: 3.6 compiler: clang env: CLANG_VERSION=4.0 LIBCXX=On - CC=clang-4.0 - CXX=clang++-4.0 addons: apt: packages: ['clang-4.0', 'libc++-dev', 'g++-6'] - os: osx osx_image: xcode10 env: OSX="On" - CC=clang - CXX=clang++ - os: osx osx_image: xcode9.4 env: OSX="On" - CC=clang - CXX=clang++ before_install: - | if [ "$OSX" == "On" ]; then # force python 3 on OSX sudo rm /usr/local/bin/python sudo ln -s /usr/local/bin/python3 /usr/local/bin/python sudo rm /usr/local/bin/pip sudo ln -s /usr/local/bin/pip3 /usr/local/bin/pip fi - git clone https://github.com/onqtam/doctest - (cd doctest && mkdir -p build && cd build && cmake .. -DDOCTEST_WITH_TESTS=off -DDOCTEST_WITH_MAIN_IN_STATIC_LIB=OFF && make && sudo make install) - | if [ "$LIBCXX" == "On" ]; then ./cmake/install_libcxx.sh fi if [ -n "${CLANG_VERSION}" ]; then export CXXFLAGS="-stdlib=libc++" fi if [ ! "$OSX" == "On" ]; then if [ ! `locale -a | grep ru_RU.cp1251` ]; then sudo localedef -c -i ru_RU -f CP1251 ru_RU.CP1251 fi if [ ! `locale -a | grep el_GR.cp1253` ]; then sudo localedef -c -i el_GR -f CP1253 el_GR.CP1253 fi fi - | python --version pip --version pip install conan --upgrade pip install conan_package_tools conan user install: - mkdir -p build && cd build - cmake .. -DFPLUS_BUILD_UNITTEST=ON script: - which $CXX - $CXX --version - cmake --version - cmake --build . --target unittest --config Release -- -j4 - cd .. && python conan_build.py libfplus-0.2.13/CMakeLists.txt000066400000000000000000000033461376322245400161730ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.2) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") project(FunctionalPlus VERSION 0.2.13) message(STATUS "===( ${PROJECT_NAME} ${PROJECT_VERSION} )===") option(FPLUS_USE_TOOLCHAIN "Use compiler flags from an external toolchain" OFF) option(FPLUS_BUILD_EXAMPLES "Build examples" ON) option(FPLUS_BUILD_UNITTEST "Build unit tests" OFF) option(FPLUS_UNITTEST_USE_CONAN "Use conan to get doctest dependency (for unit tests only)" OFF) message(STATUS "Building Unit Tests ${FPLUS_BUILD_UNITTEST}") message(STATUS "Use conan ${FPLUS_UNITTEST_USE_CONAN}") message(STATUS "Building examples ${FPLUS_BUILD_EXAMPLES}") if(NOT FPLUS_USE_TOOLCHAIN) if (MSVC) include(cmake/toolchain_msvc.cmake) else() include(cmake/toolchain.cmake) endif() endif() add_library(fplus INTERFACE) add_library(FunctionalPlus::fplus ALIAS fplus) target_include_directories(fplus INTERFACE $ ) find_package(Threads REQUIRED) target_link_libraries(fplus INTERFACE Threads::Threads) add_subdirectory(include_all_in_one) if(FPLUS_BUILD_UNITTEST) enable_testing() add_subdirectory(test) endif() if(FPLUS_BUILD_EXAMPLES) add_executable(readme_perf_examples EXCLUDE_FROM_ALL examples/readme_perf_examples.cpp) target_compile_options(readme_perf_examples PRIVATE ${COMPILE_OPTIONS}) target_link_libraries(readme_perf_examples PRIVATE fplus ${CMAKE_THREAD_LIBS_INIT}) add_executable(99_problems EXCLUDE_FROM_ALL examples/99_problems.cpp) target_compile_options(99_problems PRIVATE ${COMPILE_OPTIONS}) target_link_libraries(99_problems PRIVATE fplus ${CMAKE_THREAD_LIBS_INIT}) endif() # Run pkgconfig installation: include(cmake/pkgconfig.cmake) libfplus-0.2.13/CONTRIBUTING.md000066400000000000000000000065131376322245400156630ustar00rootroot00000000000000## Contributing to FunctionalPlus The main intention of this library is to provide small composable and [referentially transparent](https://en.wikipedia.org/wiki/Referential_transparency) functions. ### New Issues Feel free to open [issues](https://github.com/Dobiasd/FunctionalPlus/issues) for any kind of bugs, problems, feature requests or questions. A good bug report should include: - A clear title - A detailed description of the problem or error - The expected behaviour - (If possible) a minimal example or steps to reproduce - Information about used compiler and platform If you have problems installing fplus please let us know by opening an issue. This will help us optimize the setup experience. ### Open Issues 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. ### Pull requests A good [PR](https://github.com/Dobiasd/FunctionalPlus/pulls) should include: - A clear Description - Test cases - Informative commit message 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. ------------------------ ### Details of the inner workings Let's say you have an idea for a new useful function you would like to [add](https://github.com/Dobiasd/FunctionalPlus/pulls). The small example of `without` can already show a lot of things. ```c++ // API search type: without : (a, [a]) -> [a] // fwd bind count: 1 // without(0, [1, 0, 0, 5, 3, 0, 1]) == [1, 5, 3, 1] template Container without(T elem, const Container& xs) { return drop_if(is_equal_to(elem), xs); } ``` The function resides in `./include/fplus/filter.hpp`, because, well, it is some kind of filter. ;) The coding style (what is lower/upper case, spaced instead of tabs, etc.) becomes apparent by just looking at the current code. Lines should not exceed 80 characters. 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 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` the `generate/generate_fwd_defines.sh` will insert a derived function `fwd::foo : (a, b) -> (c -> d)` into `./include/fplus/fwd_instances.autogenerated_defines`. A few unit tests would also be nice. In our example they belong into `./test/filter_test.cpp` ```c++ TEST_CASE("filter_test, without") { using namespace fplus; typedef std::vector Ints; REQUIRE_EQ(without(1, Ints({1,2,3})), Ints({2,3})); REQUIRE_EQ(without(5, Ints({1,2,3})), Ints({1,2,3})); REQUIRE_EQ(without(5, Ints({})), Ints({})); } ``` Try to also cover corner cases you can think of. Please do not hesitate to create a PR even if you are not completely sure if you followed these guidelines correctly. We will help you perfect your contribution before merging. libfplus-0.2.13/INSTALL.md000066400000000000000000000103711376322245400150570ustar00rootroot00000000000000FunctionalPlus ============== Requirements and Installation ----------------------------- You can install FunctionalPlus in **one of the following 5 ways**: ### way 1: using [cmake](https://cmake.org/) ```bash git clone https://github.com/Dobiasd/FunctionalPlus cd FunctionalPlus mkdir build cd build cmake .. make sudo make install ``` And then, you can add **FunctionalPlus** as a dependency in your cmake project as in the following. ```cmake find_package(FunctionalPlus REQUIRED) add_executable(HelloWorld main.cpp) target_link_libraries(HelloWorld FunctionalPlus::fplus) ``` If you want cmake to download and install the package automatically, see [ExternalProject](#way-2-using-cmakes-externalproject) below. #### Building the unit tests Unit Tests are disabled by default. Building the tests (optional) requires [doctest](https://github.com/onqtam/doctest). First, install the required locales ````bash sudo locale-gen ru_RU sudo locale-gen ru_RU.UTF-8 sudo locale-gen el_GR sudo locale-gen el_GR.UTF-8 sudo localedef -c -i ru_RU -f CP1251 ru_RU.CP1251 sudo localedef -c -i el_GR -f CP1253 el_GR.CP1253 ```` Then, install doctest: ```bash git clone https://github.com/onqtam/doctest cd doctest mkdir -p build && cd build cmake .. -DDOCTEST_WITH_TESTS=off -DDOCTEST_WITH_MAIN_IN_STATIC_LIB=OFF make sudo make install ``` Then, compile & run the tests ````bash git clone https://github.com/Dobiasd/FunctionalPlus cd FunctionalPlus mkdir build cd build cmake .. -DFPLUS_BUILD_UNITTEST=ON make make test ```` As an alternative, doctest global installation can be skipped by using [conan](https://conan.io): ````bash # pip install conan # (if conan is not installed) git clone https://github.com/Dobiasd/FunctionalPlus cd FunctionalPlus mkdir build cd build conan install .. -obuild_unittest=True --build=missing cmake .. -DFPLUS_BUILD_UNITTEST=ON -DFPLUS_UNITTEST_USE_CONAN=ON make make test ```` ### way 2: using [cmake's ExternalProject](https://cmake.org/cmake/help/v3.0/module/ExternalProject.html) You can also add `FunctionalPlus` as an `ExternalProject` to your CMakeLists. The benefits of this: - No installation - Better version control with the `GIT_TAG` - Always get the latest version when `GIT_TAG master` - When you build your project, it will automatically update the headers if there is a change - Or get the specific version by setting it to a specific commit point ```cmake cmake_minimum_required(VERSION 3.0 FATAL_ERROR) project(FplusMinimalExternalExample) set(CMAKE_CXX_STANDARD 14) include(ExternalProject) ExternalProject_Add(functional_plus GIT_REPOSITORY https://github.com/Dobiasd/FunctionalPlus.git GIT_TAG master SOURCE_DIR "${CMAKE_BINARY_DIR}/thirdparty/fplus" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" LOG_DOWNLOAD ON LOG_BUILD ON ) set(FPLUS_INCLUDE_DIR ${CMAKE_BINARY_DIR}/thirdparty/fplus/include) include_directories(${FPLUS_INCLUDE_DIR}) add_executable(main src/main.cpp) add_dependencies(main functional_plus) ``` ### way 3: using [cget](https://github.com/pfultz2/cget/) ```bash # Setup up toolchain to use c++14 cget init --std=c++14 # Test and install cget install Dobiasd/FunctionalPlus ``` ### way 4: download manually Just [download](https://github.com/Dobiasd/FunctionalPlus/archive/master.zip)/extract FunctionalPlus and tell your compiler to use the `include` directory. ### way 5: using [Conan C/C++ package manager](https://conan.io) Just add a *conanfile.txt* with FunctionalPlus as a requirement and chose the generator for your project. ``` [requires] functionalplus/v0.2.13-p0@dobiasd/stable [generators] cmake ``` Then install it: ```bash conan install conanfile.txt ``` ### way 6: using [conda-forge](https://conda-forge.org/) ```bash conda config --add channels conda-forge conda install FunctionalPlus ``` Visit [`conda-forge/FunctionalPlus-feedstock`](https://github.com/conda-forge/FunctionalPlus-feedstock) for more details. ### way 7: using [Homebrew](https://brew.sh/) ```bash brew install functionalplus ``` And then, you can add **FunctionalPlus** as a dependency in your cmake project [as in way 1](#cmake-dependency). If you're not using cmake, you might need to add `$(brew --prefix functionalplus)/include` to the additional include paths for your compiler. libfplus-0.2.13/LICENSE000066400000000000000000000024721376322245400144370ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libfplus-0.2.13/README.md000066400000000000000000000406461376322245400147160ustar00rootroot00000000000000![logo](logo/fplus.png) [![Build Status Travis](https://travis-ci.org/Dobiasd/FunctionalPlus.svg?branch=master)][travis] [![Build Status AppVeyor](https://ci.appveyor.com/api/projects/status/github/dobiasd/FunctionalPlus)][appveyor] [![(License Boost 1.0)](https://img.shields.io/badge/license-boost%201.0-blue.svg)][license] [travis]: https://travis-ci.org/Dobiasd/FunctionalPlus [appveyor]: https://ci.appveyor.com/project/Dobiasd/functionalplus [license]: http://www.boost.org/LICENSE_1_0.txt FunctionalPlus ============== **helps you write concise and readable C++ code.** Table of contents ----------------- * [Introduction](#introduction) * [Usage examples](#usage-examples) * [Type deduction and useful error messages](#type-deduction-and-useful-error-messages) * [Tutorial](#tutorial) * [Forward application and composition](#forward-application-and-composition) * [Finding the functions you need](#finding-the-functions-you-need) * [Performance](#performance) * [Comparison with range-v3](#comparison-with-range-v3) * [Requirements and Installation](#requirements-and-installation) Introduction ------------ Great code should mostly be self-documenting, but while using C++ in reality you can find yourself dealing with low-level stuff like iterators or hand-written loops that distract from the actual essence of your code. **FunctionalPlus is a small header-only library** supporting you in reducing code noise and in dealing with only one single level of abstraction at a time. By increasing brevity and maintainability of your code it can improve productivity (and fun!) in the long run. It pursues these goals by providing pure and easy-to-use functions that free you from implementing commonly used flows of control over and over again. Say you have a list of numbers and are interested in the odd ones only. ```c++ bool is_odd_int(int x) { return x % 2 == 1; } int main() { typedef vector Ints; Ints values = {24, 11, 65, 44, 80, 18, 73, 90, 69, 18}; // todo: get odd numbers from values ... } ``` There are different possibilities to attain your goal. Some of them are: 1. write a (range based) for loop ```c++ Ints odds; for (int x : values) { if (is_odd_int(x)) { odds.push_back(x); } } ``` 2. use `std::copy_if` from the STL ```c++ Ints odds; std::copy_if(std::begin(values), std::end(values), std::back_inserter(odds), is_odd_int); ``` 3. use `keep_if` from `FunctionalPlus` ```c++ auto odds = fplus::keep_if(is_odd_int, values); ``` If you think version 3 could be the one most pleasant to work with, you might like FunctionalPlus. And if you still think the hand-written for loop is easier to understand, also consider what would happen if the loop body (i.e. a corresponding lambda function in the call to `fplus::keep_if`) would be much longer. When reading `keep_if` you would still immediately know that `odds` can only contain elements that came from `values` and were selected by some, possibly complicated, predicate. In the for loop case you have no idea what is happening until you read the whole loop body. The loop version probably would need a comment at the top stating what the use of `keep_if` would tell at first glance. Usage examples -------------- Below you find some short examples showing nice things you can do with functions and containers using FunctionalPlus. ### The same old song You can test the content of a container for various properties, e.g. ```c++ #include #include int main() { std::list things = {"same old", "same old"}; if (fplus::all_the_same(things)) std::cout << "All things being equal." << std::endl; } ``` ### The I in our `team` There also are some convenience functions for retrieving properties of containers. For example you can count the occurrences of a character in a string. ```c++ #include #include int main() { std::string team = "Our team is great. I love everybody I work with."; std::cout << "There actually are this many 'I's in team: " << fplus::count("I", fplus::split_words(false, team)) << std::endl; } ``` Output: ``` There actually are this many 'I's in team: 2 ``` ### The cutest kitty Finding the highest rated element in a container is very simple compared to a hand-written version([1](https://gist.github.com/Dobiasd/a4e7aa9c25a3dd4c0522d75a71e2a867#file-cuteness-cpp-L28-L38), [2](https://gist.github.com/Dobiasd/ca9963ecafa786a18bd5990414fd9a59#file-cuteness2-cpp-L28-L31)). ```c++ #include #include struct cat { double cuteness() const { return softness_ * temperature_ * roundness_ * fur_amount_ - size_; } std::string name_; double softness_; double temperature_; double size_; double roundness_; double fur_amount_; }; void Test_example_TheCutestCat() { std::vector cats = { {"Tigger", 5, 5, 5, 5, 5}, {"Simba", 2, 9, 9, 2, 7}, {"Muffin", 9, 4, 2, 8, 6}, {"Garfield", 6, 5, 7, 9, 5}}; auto cutest_cat = fplus::maximum_on(std::mem_fn(&cat::cuteness), cats); std::cout << cutest_cat.name_ << " is happy and sleepy. *purr* *purr* *purr*" << std::endl; } ``` Output: ``` Muffin is happy and sleepy. *purr* *purr* *purr* ``` ### Function composition, binding and map creation Let's say you have the following function [given](https://gist.github.com/Dobiasd/17f5eeab2ba0ee6631394f149fc61ce2). ```c++ std::list collatz_seq(int x); ``` And you want to create an `std::map` containing string representations of the [Collatz sequences](https://en.wikipedia.org/wiki/Collatz_conjecture) for all numbers below 30. You can implement this nicely in a functional way too. ```c++ #include #include // std::list collatz_seq(std::uint64_t x) { ... } int main() { typedef std::list Ints; // [1, 2, 3 ... 29] auto xs = fplus::numbers(1, 30); // A function that does [1, 2, 3, 4, 5] -> "[1 => 2 => 3 => 4 => 5]" auto show_ints = fplus::bind_1st_of_2(fplus::show_cont_with, " => "); // A composed function that calculates a Collatz sequence and shows it. auto show_collats_seq = fplus::compose(collatz_seq, show_ints); // Associate the numbers with the string representation of their sequences. auto collatz_dict = fplus::create_map_with(show_collats_seq, xs); // Print some of the sequences. std::cout << collatz_dict[13] << std::endl; std::cout << collatz_dict[17] << std::endl; } ``` Output: ``` [13 => 40 => 20 => 10 => 5 => 16 => 8 => 4 => 2 => 1] [17 => 52 => 26 => 13 => 40 => 20 => 10 => 5 => 16 => 8 => 4 => 2 => 1] ``` The functions shown not only work with default STL containers like `std::vector`, `std::list`, `std::deque`, `std::string` etc., but also with custom containers providing a similar interface. Type deduction and useful error messages ---------------------------------------- FunctionalPlus deduces types for you where possible. Let's take one line of code from the Collatz example: ```c++ auto show_collats_seq = fplus::compose(collatz_seq, show_ints); ``` `collatz_seq` is a function taking an `uint64_t` and returning a `list`. `show_ints` takes a `list` and returns a `string`. By making use of `function_traits`, [written by kennyim](https://github.com/kennytm/utils/blob/master/traits.hpp), it is possible to automatically deduce the expression `fplus::compose(collatz_seq, show_ints)` as being a function taking an `uint64_t` and returning a `string`, so you do not have to manually provide type hints to the compiler. If two functions whose "connecting types" do not match are passed in, an unambiguous error message describing the issue will be generated. FunctionalPlus uses compile time assertions to avoid the confusingly long error messages compilers generate when faced with type errors in function templates. Changing the way you program from "writing your own loops and nested ifs" to "composing and using small functions" will result in more errors at compile time but will pay out by having fewer errors at runtime. Also, more precise compile time errors will reduce the time spent debugging. Tutorial -------- The article "[Functional programming in C++ with the FunctionalPlus library; today: HackerRank challenge Gemstones](https://github.com/Dobiasd/articles/blob/master/functional_programming_in_cpp_with_the_functionalplus_library_today_hackerrank_challange_gemstones.md)" provides a smooth introduction into the library by showing how one could develop an elegant solution to a problem using the FunctionalPlus approach. Also on Udemy there is a [course "Functional Programming using C++"](https://www.udemy.com/functional-programming-using-cpp/) that makes heavy use of FunctionalPlus to explain general functional concepts. Forward application and composition ----------------------------------- The "Gemstones" tutorial above explains how one can apply functional thinking to arrive at the solution below for the following problem: > Find the number of characters present in every line of an input text. ```c++ std::string gemstone_count(const std::string& input) { using namespace fplus; typedef std::set characters; const auto lines = split_lines(false, input); // false = no empty lines const auto sets = transform( convert_container, lines); // Build the intersection of all given character sets (one per line). const auto gem_elements = fold_left_1( set_intersection, sets); return show(size_of_cont(gem_elements)); } ``` By using the functionality from `namespace fwd`, you can get along without temporary variables, and make it clear that the whole process is simply pushing the input through a chain of functions, similar to the pipe concept in the Unix command line. ```c++ std::string gemstone_count_fwd_apply(const std::string& input) { using namespace fplus; typedef std::set characters; return fwd::apply( input , fwd::split_lines(false) , fwd::transform(convert_container) , fwd::fold_left_1(set_intersection) , fwd::size_of_cont() , fwd::show() ); } ``` In `fplus::fwd::` you find many `fplus::` functions again, but in a partially [curried](http://stackoverflow.com/a/36321/1866775) version, i.e. `fplus::foo : (a, b, c) -> d` has its counterpart with `fplus::foo : (a, b) -> (c -> d)`. This makes the style above possible. Alternatively to the forward application version, you can also write [point-free](https://en.wikipedia.org/wiki/Tacit_programming) and define your function by composition: ```c++ using namespace fplus; typedef std::set characters; const auto gemstone_count_fwd_compose = fwd::compose( fwd::split_lines(false), fwd::transform(convert_container), fwd::fold_left_1(set_intersection), fwd::size_of_cont(), fwd::show() ); ``` By the way, in case you need the parameters of a binary function in different order, `namespace fplus::fwd::flip` also exists. `fplus::bar : (a, b) -> c` does not only have its analogue in `fplus::fwd::bar : a -> b -> c` but also in `fplus::fwd::flip::bar : b -> a -> c`. Finding the functions you need ------------------------------ If you are looking for a specific FunctionalPlus function you do not know the name of yet, you can of course use the auto-complete feature of your IDE to browse the content of the `namespace fplus`. But the recommended way is to simply use the **[FunctionalPlus API search website](http://www.editgym.com/fplus-api-search/)**. You can quickly search by keywords or function type signatures with it. If you prefer, you can also simply [browse the source code using Sourcegraph](https://sourcegraph.com/github.com/Dobiasd/FunctionalPlus/-/tree/include/fplus). Performance ----------- The basic functions are fast, thanks to C++'s concept of abstraction without overhead. Here are some measurements from the first example, taken on a standard desktop PC, compiled with GCC and the `O3` flag. ``` 5000 random numbers, keep odd ones, 20000 consecutive runs accumulated ---------------------------------------------------------------------- | Hand-written for loop | std::copy_if | fplus::keep_if | |-----------------------|--------------|----------------| | 0.632 s | 0.641 s | 0.627 s | ``` So the compiler seems to do a very good job in optimizing and inlining everything to basically equal machine code performance-wise. The more complex functions though sometimes could be written in a more optimized way. If you use FunctionalPlus in a performance-critical scenario and profiling shows you need a faster version of a function [please let me know](https://github.com/Dobiasd/FunctionalPlus/issues) or [even help improving FunctionalPlus](https://github.com/Dobiasd/FunctionalPlus/pulls). FunctionalPlus internally often can operate in-place if a given container is an r-value (e.g. in chained calls) and thus avoid many unnecessary allocations and copies. But this is not the case in all situations. However, thanks to working with a multi-paradigm language one easily can combine manually optimized imperative code with `fplus` functions. Luckily experience (aka. profiling) shows that in most cases the vast majority of code in an application is not relevant for overall performance and memory consumption. So initially focusing on developer productivity and readability of code is a good idea. Comparison with range-v3 ------------------------ [Range-v3](https://github.com/ericniebler/range-v3) and FunctionalPlus do have things in common, as the following code snippet shows. ```c++ const auto times_3 = [](int i){return 3 * i;}; const auto is_odd_int = [](int i){return i % 2 == 0;}; const auto as_string_length = [](int i){return std::to_string(i).size();}; // FunctionalPlus using namespace fplus; const auto result_fplus = fwd::apply( numbers(0, 15000000) , fwd::transform(times_3) , fwd::drop_if(is_odd_int) , fwd::transform(as_string_length) , fwd::sum()); // range-v3 const auto result_range_v3 = accumulate( view::ints(0) | view::take(15000000) | view::transform(times_3) | view::remove_if(is_odd_int) | view::transform(as_string_length) , 0); ``` There are some differences though. Range-v3 ranges are lazy, which means no intermediate memory is allocated during the single steps of a processing chain like above. Also range-v3 will probably be [part of the C++ standard](https://ericniebler.github.io/std/wg21/D4128.html) at some point in the future. When using FunctionalPlus on the other hand you work with normal STL-containers. Also [implementing a new function](https://github.com/Dobiasd/FunctionalPlus/blob/a17fc716d40a4370eed13f16e7d9105c4cc75e26/include/fplus/generate.hpp#L19) is simpler compared to [writing a new range adaptor](https://github.com/ericniebler/range-v3/blob/4cfcb59c3db1c279d72c64ccf15de3c724a0362d/include/range/v3/algorithm/generate.hpp#L32). Additionally FunctionalPlus provides much more functions out of the box and has the [API search website](http://www.editgym.com/fplus-api-search/). So the choice between the two libraries depends on your preferences and project's needs. Requirements and Installation ----------------------------- A **C++14**-compatible compiler is needed. Compilers from these versions on are fine: * GCC ( >= 4.9 ) * Clang ( >= 3.7 with libc++ >= 3.7 ) * Visual Studio ( >= 2015 ) * XCode ( >= 9 ) Guides for different ways to install FunctionalPlus can be found in [INSTALL.md](INSTALL.md). Disclaimer ---------- The functionality in this library initially grew due to my personal need for it while using C++ on a regular basis. I try my best to make it error free and as comfortable to use as I can. The API still might change in the future. If you have any suggestions, find errors, miss some functions or want to give general feedback/criticism, I'd [love to hear from you](https://github.com/Dobiasd/FunctionalPlus/issues). Of course, [contributions](CONTRIBUTING.md) are also very welcome. License ------- Distributed under the Boost Software License, Version 1.0. (See accompanying file [`LICENSE`](https://github.com/Dobiasd/FunctionalPlus/blob/master/LICENSE) or copy at [http://www.boost.org/LICENSE_1_0.txt](http://www.boost.org/LICENSE_1_0.txt)) libfplus-0.2.13/api_search/000077500000000000000000000000001376322245400155235ustar00rootroot00000000000000libfplus-0.2.13/api_search/compile_all.sh000077500000000000000000000001311376322245400203350ustar00rootroot00000000000000#!/usr/bin/env bash ./generate_database.sh cd frontend ./compile.sh ./ExploreCompile.sh libfplus-0.2.13/api_search/compile_all_and_deploy.sh000077500000000000000000000000761376322245400225430ustar00rootroot00000000000000#!/usr/bin/env bash ./compile_all.sh cd frontend ./deploy.sh libfplus-0.2.13/api_search/frontend/000077500000000000000000000000001376322245400173425ustar00rootroot00000000000000libfplus-0.2.13/api_search/frontend/ExploreCompile.sh000077500000000000000000000004271376322245400226330ustar00rootroot00000000000000#!/usr/bin/env bash elm make src/FPlusApiExplore.elm --output=build/js/fplus_api_explore.js if [ $? -eq 0 ] then cp ./src/explore.html ./build/explore.html cp ./src/htmlmain_explore.js ./build/js/htmlmain_explore.js cp ./src/style_explore.css ./build/style_explore.css filibfplus-0.2.13/api_search/frontend/TypeSignatureTestCompile.sh000077500000000000000000000001411376322245400246510ustar00rootroot00000000000000#!/usr/bin/env bash elm make src/TypeSignatureTestMain.elm --output=build/typesignaturetest.htmllibfplus-0.2.13/api_search/frontend/compile.sh000077500000000000000000000007351376322245400213360ustar00rootroot00000000000000#!/usr/bin/env bash rm -r -f build mkdir -p build mkdir -p build/js # todo: Use --optimize elm make src/FPlusApiSearch.elm --output=build/js/fplus_api_search.js if [ $? -eq 0 ] then cp ../../logo/fplus.png ./build/ cp -r ./src/highlight ./build/ cp ./src/style.css ./build/style.css cp ./src/favicon.png ./build/favicon.png cp ./src/htmlmain.js ./build/js/htmlmain.js cp ./src/index.html ./build/index.html fi ./ExploreCompile.sh ./TypeSignatureTestCompile.sh libfplus-0.2.13/api_search/frontend/elm.json000066400000000000000000000013511376322245400210120ustar00rootroot00000000000000{ "type": "application", "source-directories": [ "src" ], "elm-version": "0.19.1", "dependencies": { "direct": { "andre-dietrich/parser-combinators": "4.0.0", "elm/browser": "1.0.2", "elm/core": "1.0.5", "elm/html": "1.0.0", "elm/regex": "1.0.0", "elm-community/list-extra": "8.2.4", "elm-explorations/markdown": "1.0.0" }, "indirect": { "elm/json": "1.1.3", "elm/time": "1.0.0", "elm/url": "1.0.0", "elm/virtual-dom": "1.0.2", "pilatch/flip": "1.0.0" } }, "test-dependencies": { "direct": {}, "indirect": {} } } libfplus-0.2.13/api_search/frontend/src/000077500000000000000000000000001376322245400201315ustar00rootroot00000000000000libfplus-0.2.13/api_search/frontend/src/FPlusApiCommon.elm000066400000000000000000000214261376322245400234710ustar00rootroot00000000000000module FPlusApiCommon exposing (..) import Database import Debug import TypeSignature import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) import Markdown import Regex import String type Msg = NoOp | UpdateQuery String type alias WithParsedSignature a = { a | parsedSignature : TypeSignature.Signature , signatureFwd : Maybe String , parsedSignatureFwd : Maybe TypeSignature.Signature , signatureFwdFlip : Maybe String , parsedSignatureFwdFlip : Maybe TypeSignature.Signature } type alias Function = WithParsedSignature Database.Function parseSignatureCrashOnError : Database.Function -> TypeSignature.Signature parseSignatureCrashOnError function = case TypeSignature.parseSignature function.signature of Just sig -> sig Nothing -> "Error parsing signature of " ++ function.name ++ ": " ++ function.signature |> Debug.todo hasFwdSignature : String -> Bool hasFwdSignature documentation = String.contains "fwd bind count" documentation hasFwdFlipSignature : String -> Bool hasFwdFlipSignature documentation = String.contains "fwd bind count: 1" documentation removeFwdBindCount : String -> String removeFwdBindCount documentation = documentation |> String.lines |> List.filter (\x -> String.contains "fwd bind count" x |> not) |> String.join "\n" addParsedSignatureToFunction : Database.Function -> Function addParsedSignatureToFunction function = let parsedSig = parseSignatureCrashOnError function parsedSigFwd = if hasFwdSignature function.documentation then parsedSig |> TypeSignature.curry1 |> Maybe.Just else Maybe.Nothing parsedSigFwdFlip = if hasFwdFlipSignature function.documentation then parsedSig |> TypeSignature.curry1Flip |> Maybe.Just else Maybe.Nothing in { name = function.name , signature = function.signature , parsedSignature = parsedSig |> TypeSignature.normalizeSignature , parsedSignatureFwd = parsedSigFwd |> Maybe.map TypeSignature.normalizeSignature , signatureFwd = parsedSigFwd |> Maybe.map (TypeSignature.showSignature True) , parsedSignatureFwdFlip = parsedSigFwdFlip |> Maybe.map TypeSignature.normalizeSignature , signatureFwdFlip = parsedSigFwdFlip |> Maybe.map (TypeSignature.showSignature True) , documentation = removeFwdBindCount function.documentation , declaration = function.declaration } functions : List Function functions = List.map addParsedSignatureToFunction Database.functions showMaybeSig : Maybe TypeSignature.Signature -> String showMaybeSig maybeSig = case maybeSig of Maybe.Just s -> TypeSignature.showSignature True s Maybe.Nothing -> "" replaceInString : String -> String -> String -> String replaceInString pattern replacement = Regex.replace (Maybe.withDefault Regex.never (Regex.fromString pattern)) (always replacement) replaceSubMatchInString : String -> (String -> String) -> String -> String replaceSubMatchInString pattern replacementFunc = let f { match, submatches } = case submatches of [ Maybe.Just sm ] -> replacementFunc sm _ -> match in Regex.replace (Maybe.withDefault Regex.never (Regex.fromString pattern)) f replaceTwoSubMatchInString : String -> (String -> String -> String) -> String -> String replaceTwoSubMatchInString pattern replacementFunc = let f { match, submatches } = case submatches of [ Maybe.Just sm1, Maybe.Just sm2 ] -> replacementFunc sm1 sm2 _ -> match in Regex.replace (Maybe.withDefault Regex.never (Regex.fromString pattern)) f applyUntilIdempotent : (String -> String) -> String -> String applyUntilIdempotent f str = let res = f str in if res == str then res else applyUntilIdempotent f res cleanFunctionSignature : String -> String cleanFunctionSignature = let recursiveCases = replaceSubMatchInString "vector<([^<>]*)>" (\sm -> "[" ++ sm ++ "]") >> replaceSubMatchInString "list<([^<>]*)>" (\sm -> "[" ++ sm ++ "]") >> replaceSubMatchInString "set<([^<>]*)>" (\sm -> "Set " ++ sm) >> replaceTwoSubMatchInString "map<([^<>]*),([^<>]*)>" (\sm1 sm2 -> "Map " ++ sm1 ++ " " ++ sm2) >> replaceTwoSubMatchInString "pair<([^<>]*),([^<>]*)>" (\sm1 sm2 -> "(" ++ sm1 ++ ", " ++ sm2 ++ ")") |> applyUntilIdempotent in String.toLower >> replaceInString "std::" "" >> replaceInString "fplus::" "" >> recursiveCases >> replaceInString "unsigned int" "Int" >> replaceInString "unsigned" "Int" >> replaceInString "size_t" "Int" >> replaceInString "short" "Int" >> replaceInString "signed" "Int" >> replaceInString "long" "Int" >> replaceInString "integer" "Int" >> replaceInString "int" "Int" >> replaceInString "double" "Float" >> replaceInString "float" "Float" >> replaceInString "boolean" "Bool" >> replaceInString "char" "Char" >> replaceInString "bool" "Bool" >> replaceInString "io" "Io" >> replaceInString "maybe" "Maybe" >> replaceInString "either" "Result" >> replaceInString "result" "Result" >> replaceInString "map" "Map" >> replaceInString "dict" "Map" >> replaceInString "set" "Set" >> replaceInString "string" "String" >> replaceInString "String" "[Char]" boolToNum : Float -> Bool -> Float boolToNum value b = if b then value else 0 maybeSigIsArrow : Maybe TypeSignature.Signature -> Bool maybeSigIsArrow maybeSig = case maybeSig of Maybe.Just sig -> TypeSignature.sigIsArrow sig _ -> False stringToCode : String -> String -> Html Msg stringToCode language str = let defOpts = Markdown.defaultOptions options = { defOpts | defaultHighlighting = Just language } taggedStr = "```\n" ++ str ++ "\n```" in Markdown.toHtmlWith options [] taggedStr docFromString : String -> Html Msg docFromString str = let taggedStr = "```" ++ str ++ "```" in Markdown.toHtmlWith Markdown.defaultOptions [] taggedStr showFunctionDivs : Function -> List (Html Msg) showFunctionDivs function = let functionNameAndSig = div [ class "functionnameandsig" ] [ function.name ++ " : " ++ function.signature |> stringToCode "haskell" ] functionNameAndSigFwd = case function.signatureFwd of Maybe.Just sigFwd -> div [ class "functionnameandsig" ] [ "fwd::" ++ function.name ++ " : " ++ sigFwd |> stringToCode "haskell" ] Maybe.Nothing -> div [] [] functionNameAndSigFwdFlip = case function.signatureFwdFlip of Maybe.Just sigFwdFlip -> div [ class "functionnameandsig" ] [ "fwd::flip::" ++ function.name ++ " : " ++ sigFwdFlip |> stringToCode "haskell" ] Maybe.Nothing -> div [] [] functionDocumentation = div [ class "functiondoc" ] [ function.documentation |> docFromString ] functionDeclaration = div [ class "functiondecl" ] [ function.declaration |> stringToCode "cpp" ] in [ functionNameAndSig , functionNameAndSigFwd , functionNameAndSigFwdFlip , functionDocumentation , functionDeclaration ] libfplus-0.2.13/api_search/frontend/src/FPlusApiExplore.elm000066400000000000000000000037301376322245400236550ustar00rootroot00000000000000module FPlusApiExplore exposing (..) import FPlusApiCommon exposing (..) import Browser import Database import TypeSignature import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) import Markdown import Regex import String main = Browser.element { init = init , update = update , subscriptions = subscriptions , view = view } subscriptions : Model -> Sub Msg subscriptions model = Sub.none init : () -> ( Model, Cmd Msg ) init _ = ( defaultModel, Cmd.none ) type alias Model = { allFunctions : List Function } defaultModel : Model defaultModel = { allFunctions = functions } update : Msg -> Model -> ( Model, Cmd Msg ) update action model = case action of NoOp -> ( model, Cmd.none ) UpdateQuery str -> ( model, Cmd.none ) view : Model -> Html Msg view model = div [ class "mainwrapper" ] [ let url = "https://github.com/Dobiasd/FunctionalPlus/" in div [ class "main" ] [ div [ class "githublink" ] [ a [ href url ] [ img [ class "logo", src "fplus.png" ] [] ] , p [] [ a [ href url ] [ text url ] ] ] , hr [] [] , model.allFunctions |> showFunctions , hr [] [] , showFooter ] ] showFooter : Html Msg showFooter = footer [ class "footer" ] [ text "Copyright © 2017 Tobias Hermann. All rights reserved." ] showNonRatedFunction : Function -> Html Msg showNonRatedFunction function = div [ class "function" ] ( showFunctionDivs function ) showFunctions : List Function -> Html Msg showFunctions ratedFunctions = List.map showNonRatedFunction ratedFunctions |> List.intersperse (hr [] []) |> div [ class "functions" ] libfplus-0.2.13/api_search/frontend/src/FPlusApiSearch.elm000066400000000000000000000231531376322245400234450ustar00rootroot00000000000000module FPlusApiSearch exposing (..) import FPlusApiCommon exposing (..) import TypeSignature import Browser import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) import Markdown import Regex import String main = Browser.element { init = init , update = update , subscriptions = subscriptions , view = view } subscriptions : Model -> Sub Msg subscriptions model = Sub.none maxVisibleFunctions : Int maxVisibleFunctions = 20 init : () -> ( Model, Cmd Msg ) init _ = ( defaultModel, Cmd.none ) type alias Model = { query : String , querySigStr : String , searchResult : List ( Function, Float ) } defaultModel : Model defaultModel = { query = "" , querySigStr = "" , searchResult = [] } update : Msg -> Model -> ( Model, Cmd Msg ) update action model = case action of NoOp -> ( model, Cmd.none ) UpdateQuery str -> if String.isEmpty str then ( { model | query = str , querySigStr = "" , searchResult = [] } , Cmd.none ) else let singletonSignatureToNothing sig = if String.contains "->" (TypeSignature.showSignature True sig) then Just sig else Nothing newQuerySig = str |> cleanFunctionSignature |> TypeSignature.parseSignature |> Maybe.map TypeSignature.normalizeSignature |> \x -> Maybe.andThen singletonSignatureToNothing x newQuerySigLower = newQuerySig |> Maybe.map (TypeSignature.showSignature False >> String.toLower ) |> (\x -> Maybe.andThen (TypeSignature.parseSignature >> Maybe.map TypeSignature.normalizeSignature ) x ) newQuerySigStr = newQuerySig |> showMaybeSig in ( { model | query = str , querySigStr = newQuerySigStr , searchResult = searchFunctions str newQuerySig newQuerySigLower } , Cmd.none ) view : Model -> Html Msg view model = div [ class "mainwrapper" ] [ let url = "https://github.com/Dobiasd/FunctionalPlus/" in div [ class "main" ] [ div [ class "githublink" ] [ a [ href url ] [ img [ class "logo", src "fplus.png" ] [] ] , p [] [ a [ href url ] [ text url ] ] ] , hr [] [] , input [ placeholder "search query" , autofocus True , autocomplete True , style "width" "500px" , onInput UpdateQuery ] [] , if String.isEmpty model.querySigStr then div [ class "queryhelper" ] [ text ("search by function name, docs or type signature," ++ " e.g. (vector, string) -> string" ) ] else div [ class "parsedsignature" ] [ "as parsed type: " ++ model.querySigStr |> stringToCode "Haskell" ] , hr [] [] , model.searchResult |> showFunctions , hr [] [] , showFooter ] ] showFooter : Html Msg showFooter = footer [ class "footer" ] [ text "Copyright © 2016 Tobias Hermann. All rights reserved." ] searchFunctions : String -> Maybe TypeSignature.Signature -> Maybe TypeSignature.Signature -> List ( Function, Float ) searchFunctions query querySig querySigLower = let ratedFunctions = functions |> List.map (\f -> ( f, functionRating query querySig querySigLower f )) in ratedFunctions |> List.filter (\( _, rating ) -> rating > 0) |> List.sortBy (\( _, rating ) -> 0 - rating) |> List.take maxVisibleFunctions typeRating : Float -> TypeSignature.Signature -> TypeSignature.Signature -> Float typeRating weight query db = TypeSignature.functionCompatibility db query |> (*) weight adjustQuery : String -> String adjustQuery query = case query of "map" -> "transform" _ -> query functionRating : String -> Maybe TypeSignature.Signature -> Maybe TypeSignature.Signature -> Function -> Float functionRating queryOrig querySig querySigLower function = let query = queryOrig |> String.toLower |> adjustQuery queryWords = String.words query stringLengthFloat = String.length >> toFloat queryWordLengthSum = queryWords |> List.map stringLengthFloat |> List.sum wordRatingSum = queryWords |> List.map (\queryWord -> functionWordRating (stringLengthFloat queryWord / queryWordLengthSum ) function queryWord ) |> List.sum |> \x -> if maybeSigIsArrow querySig then 0 else x bestTypeRating = let maybeSigRating factor maybeSig = case maybeSig of Just sig -> let sigRating = Basics.max (typeRating factor sig function.parsedSignature ) (case function.parsedSignatureFwd of Maybe.Just psFwd -> typeRating factor sig psFwd Maybe.Nothing -> 0 ) name_shortness_factor = 120 / (120 + stringLengthFloat function.name) in sigRating * name_shortness_factor _ -> 0 in Basics.max (maybeSigRating 1000 querySig) (maybeSigRating 400 querySigLower) functionNameLengthRating = -0.1 * stringLengthFloat function.name in wordRatingSum + bestTypeRating + functionNameLengthRating functionWordRating : Float -> Function -> String -> Float functionWordRating weight function query = let isSubStr = String.contains query function.name queryLength = String.length query |> toFloat functionNameLength = String.length function.name |> toFloat lengthDiff = queryLength - functionNameLength |> abs queryAndFunctionNameMaxLength = Basics.max queryLength functionNameLength relLengthDiff = lengthDiff / queryAndFunctionNameMaxLength nameRating = weight * Basics.max 0 (boolToNum 100 isSubStr - 5 * relLengthDiff) docRating = String.contains query (String.toLower function.documentation) |> boolToNum 40 in nameRating + docRating showFunctions : List ( Function, Float ) -> Html Msg showFunctions ratedFunctions = List.map showRatedFunction ratedFunctions |> List.intersperse (hr [] []) |> div [ class "functions" ] ratingToHtml : Float -> Html Msg ratingToHtml rating = "search rating: " ++ String.fromInt (round rating) |> docFromString showRatedFunction : ( Function, Float ) -> Html Msg showRatedFunction ( function, rating ) = let ratingOfFunction = div [ class "functionrating" ] [ rating |> ratingToHtml ] in div [ class "function" ] ( showFunctionDivs function ++ [ratingOfFunction] ) libfplus-0.2.13/api_search/frontend/src/TypeSignature.elm000066400000000000000000000275221376322245400234430ustar00rootroot00000000000000module TypeSignature exposing (Signature, parseSignature, showSignature, normalizeSignature, functionCompatibility, curry1, curry1Flip, sigIsArrow) {-| This module provides the possibility to parse Haskell and Elm type signatures. -} import Combine as C import Combine.Char as CC import Combine.Num as CN import Char import Debug import Dict import Tuple exposing (first) import List exposing ((::)) import List.Extra exposing (permutations, subsequences) import Maybe import Result import String type Signature = Arrow Signature Signature | ListType Signature -- A Tuple with an empty List is the unit type. | Tuple (List Signature) | TypeConstructor String | TypeApplication Signature Signature | VariableType String showSignature : Bool -> Signature -> String showSignature charListAsString = showSignatureHelper charListAsString False False splitLast : List a -> ( List a, a ) splitLast xs = case List.reverse xs of y :: ys -> ( List.reverse ys, y ) _ -> "Error splitLast" |> Debug.todo curry1 : Signature -> Signature curry1 sig = case sig of Arrow (Tuple []) ret -> "Error curry1 (empty tuple): " ++ showSignature True sig |> Debug.todo Arrow (Tuple params) ret -> let ( ps, x ) = splitLast params in case ps of [p] -> Arrow p (Arrow x ret) _ -> Arrow (Tuple ps) (Arrow x ret) Arrow sig2 ret -> Arrow (Tuple []) (Arrow sig2 ret) _ -> "Error curry1: " ++ showSignature True sig |> Debug.todo curry1Flip : Signature -> Signature curry1Flip sig = case (sig |> curry1) of Arrow a (Arrow b ret) -> Arrow b (Arrow a ret) _ -> "Error curry1Flip: " ++ showSignature True sig |> Debug.todo mapS : (s -> String -> ( s, String )) -> s -> List Signature -> ( List Signature, s ) mapS f s = let go sig ( sigs, s2 ) = let ( sig_, s_ ) = mapLRS f s2 sig in ( sig_ :: sigs, s_ ) in List.foldl go ( [], s ) >> \( xs, s2 ) -> ( List.reverse xs, s2 ) {- http://stackoverflow.com/a/37455356/1866775 -} mapLRS : (s -> String -> ( s, String )) -> s -> Signature -> ( Signature, s ) mapLRS f s sig = case sig of Arrow a b -> let ( a_, s_ ) = mapLRS f s a ( b_, s__ ) = mapLRS f s_ b in ( Arrow a_ b_, s__ ) TypeConstructor x -> ( TypeConstructor x, s ) VariableType x -> let ( s_, x_ ) = f s x in ( VariableType x_, s_ ) TypeApplication a b -> let ( a_, s_ ) = mapLRS f s a ( b_, s__ ) = mapLRS f s_ b in ( TypeApplication a_ b_, s__ ) ListType x -> let ( x_, s_ ) = mapLRS f s x in ( ListType x_, s_ ) Tuple xs -> let ( xs_, s_ ) = mapS f s xs in ( Tuple xs_, s_ ) nthVarName : Int -> String nthVarName i = let charPart = 97 + (remainderBy 26 i) |> Char.fromCode |> String.fromChar addNumber = i // 26 numStr = if addNumber == 0 then "" else String.fromInt addNumber in charPart ++ numStr {- Asserts varNames being generated by the same functions. -} nextFreeVarName : List String -> String nextFreeVarName varNames = nthVarName (List.length varNames) normalizeSignatureGo : Dict.Dict String String -> String -> ( Dict.Dict String String, String ) normalizeSignatureGo dict str = let nextFree = nextFreeVarName (Dict.keys dict) str_ = Dict.get str dict |> Maybe.withDefault nextFree in ( Dict.insert str str_ dict, str_ ) normalizeSignature : Signature -> Signature normalizeSignature = mapLRS normalizeSignatureGo Dict.empty >> first --mapLRS (\s -> ( s + 1, s |> Char.fromCode |> String.fromChar )) 97 >> first addParenthesis : String -> String addParenthesis x = "(" ++ x ++ ")" showSignatureHelper : Bool -> Bool -> Bool -> Signature -> String showSignatureHelper charListAsString arrowsInParens typeAppInParens sig = let optArrowParens = if arrowsInParens then addParenthesis else identity optTypeApplicationParens = if typeAppInParens then addParenthesis else identity in case sig of Arrow a b -> showSignatureHelper charListAsString True False a ++ " -> " ++ showSignatureHelper charListAsString False False b |> optArrowParens TypeConstructor x -> x VariableType x -> x TypeApplication a b -> showSignatureHelper charListAsString False False a ++ " " ++ showSignatureHelper charListAsString True True b |> optTypeApplicationParens ListType (TypeConstructor "Char") -> if charListAsString then "String" else "[Char]" ListType x -> "[" ++ showSignatureHelper charListAsString False False x ++ "]" Tuple xs -> String.join ", " (List.map (showSignatureHelper charListAsString False False) xs) |> addParenthesis listParser : C.Parser s Signature listParser = C.brackets (C.lazy <| \() -> signatureParser) |> C.map ListType trimSpaces : C.Parser s a -> C.Parser s a trimSpaces = let skipSpaces = C.skipMany <| C.choice [ CC.space, CC.tab ] in C.between skipSpaces skipSpaces tupleParser : C.Parser s Signature tupleParser = let innerParser = C.sepBy (trimSpaces <| CC.char ',') (C.lazy <| \() -> signatureParser) |> C.map simplify simplify xs = case xs of [ x ] -> x _ -> Tuple xs in trimSpaces innerParser |> C.parens arrowParser : C.Parser s Signature arrowParser = let arrowOp = C.onsuccess Arrow (trimSpaces (C.string "->")) in C.chainr arrowOp (C.lazy <| \() -> nonAppSignatureParser) isValidTypeApplication : Signature -> Bool isValidTypeApplication sig = case sig of TypeConstructor _ -> True TypeApplication a b -> isValidTypeApplication a _ -> False typeApplicationParser : C.Parser s Signature typeApplicationParser = let typeApplyOp = C.onsuccess TypeApplication (C.many1 CC.space) validate ta = if isValidTypeApplication ta then C.succeed ta else C.fail "invalid type application" in C.andThen validate (C.chainl typeApplyOp (C.lazy <| \() -> nonOpSignatureParser)) typeStartsWithParser : C.Parser s Char -> (String -> Signature) -> C.Parser s Signature typeStartsWithParser p tagger = [ p |> C.map (\x -> [ x ]) , C.many <| C.choice [ CC.lower, CC.upper, CC.char '.', CC.char '_', CC.digit ] ] |> C.sequence |> C.map List.concat |> C.map (String.fromList >> tagger) variableTypeParser : C.Parser s Signature variableTypeParser = typeStartsWithParser CC.lower VariableType stringToListChar : Signature -> Signature stringToListChar sig = case sig of TypeConstructor "String" -> ListType (TypeConstructor "Char") _ -> sig fixedTypeParser : C.Parser s Signature fixedTypeParser = typeStartsWithParser CC.upper TypeConstructor |> C.map stringToListChar nonOpSignatureParser : C.Parser s Signature nonOpSignatureParser = C.choice [ C.lazy <| \() -> listParser , C.lazy <| \() -> tupleParser , variableTypeParser , fixedTypeParser ] nonAppSignatureParser : C.Parser s Signature nonAppSignatureParser = C.choice [ C.lazy <| \() -> typeApplicationParser , C.lazy <| \() -> nonOpSignatureParser ] signatureParser : C.Parser s Signature signatureParser = C.choice [ C.lazy <| \() -> arrowParser , nonAppSignatureParser ] |> trimSpaces parseSignature : String -> Maybe Signature parseSignature inputData = case C.parse signatureParser inputData of Ok (state, { input }, result) -> if String.isEmpty input then Maybe.Just result else Maybe.Nothing Err (state, stream, errors) -> Maybe.Nothing equalityToFloat : Float -> Float -> a -> a -> Float equalityToFloat valueTrue valueFalse x y = if x == y then valueTrue else valueFalse sigIsArrow : Signature -> Bool sigIsArrow sig = case sig of Arrow _ _ -> True _ -> False functionCompatibility : Signature -> Signature -> Float functionCompatibility db query = case ( db, query ) of ( VariableType _, TypeConstructor _ ) -> 0.95 ( VariableType _, ListType _ ) -> 0.8 ( TypeApplication (TypeConstructor "Maybe") (VariableType x), VariableType y ) -> 0.8 * equalityToFloat 1.0 0.0 x y ( TypeApplication (TypeConstructor "Maybe") (TypeConstructor x), TypeConstructor y ) -> 0.8 * equalityToFloat 1.0 0.0 x y ( Arrow a b, Arrow x y ) -> functionCompatibility a x * functionCompatibility b y ( TypeConstructor x, TypeConstructor y ) -> equalityToFloat 1.0 0.0 x y ( VariableType x, VariableType y ) -> equalityToFloat 1.0 0.85 x y ( TypeApplication a b, TypeApplication x y ) -> functionCompatibility a x * functionCompatibility b y ( ListType a, ListType x ) -> functionCompatibility a x ( Tuple xs, Tuple ys ) -> if List.length xs > List.length ys then List.map (\xs_ -> List.map2 functionCompatibility xs_ ys |> List.product |> (\x -> x * toFloat (List.length ys) / toFloat (List.length xs) ) ) (subsequences xs) |> List.maximum |> Maybe.withDefault 0 else if List.length xs == List.length ys then List.map (\ys_ -> List.map2 functionCompatibility xs ys_ |> List.product ) (permutations ys) |> List.maximum |> Maybe.withDefault 0 else 0 ( Tuple xs, y ) -> List.map (\x -> functionCompatibility x y / toFloat (List.length xs) ) xs |> List.maximum |> Maybe.withDefault 0 _ -> 0 libfplus-0.2.13/api_search/frontend/src/TypeSignatureTestMain.elm000066400000000000000000000043111376322245400250770ustar00rootroot00000000000000module TypeSignatureTestMain exposing (..) import Browser import TypeSignature import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) import Maybe import Result import String main = Browser.sandbox { init = init , update = update , view = view } type alias Model = String init : Model init = "" type Msg = UpdateStr String update : Msg -> Model -> Model update action model = case action of UpdateStr str -> str showMaybeSig : Maybe TypeSignature.Signature -> String showMaybeSig maybeSig = case maybeSig of Maybe.Just s -> TypeSignature.showSignature True s Maybe.Nothing -> "" view : Model -> Html Msg view str = let maybeSignature = str |> TypeSignature.parseSignature maybeSignatureNormalized = maybeSignature |> Maybe.map TypeSignature.normalizeSignature signatureString = maybeSignatureNormalized |> showMaybeSig maybeParsedAgainSignature = signatureString |> TypeSignature.parseSignature signatureAgainString = showMaybeSig maybeParsedAgainSignature isIdempotent = signatureString == signatureAgainString in div [] [ input [ placeholder "please enter type signature" , autofocus True , style "width" "500px" , onInput UpdateStr ] [] , div [ style "margin" "10px" ] [ "parse result: " ++ (maybeSignature |> Debug.toString) |> text ] , div [ style "margin" "10px" ] [ "parse result: " ++ (maybeSignatureNormalized |> Debug.toString) |> text ] , div [] [ "as string: " ++ signatureString |> text ] , if not isIdempotent then div [] [ div [] [ "Error: Parsing was not isIdempotent!" |> text ] , div [] [ signatureAgainString |> text ] ] else div [] [] ] libfplus-0.2.13/api_search/frontend/src/explore.html000066400000000000000000000016111376322245400224740ustar00rootroot00000000000000 FunctionalPlus API explore
libfplus-0.2.13/api_search/frontend/src/favicon.png000066400000000000000000000007331376322245400222670ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME 11tEXtCommentCreated with GIMPWCIDAT8˕=0V 8(NJt D'I;(ED?zryOI48р"RD?'F#hqB!դ&( t:jURlf xiϋ6 z@L`Y>a8^v[L}il\JXXyB,0`8 ݠ^RIjz۶LIy@xp8tyŢ?^ @2绞8B_dCu-yӿ~4ө?y =IENDB`libfplus-0.2.13/api_search/frontend/src/highlight/000077500000000000000000000000001376322245400221005ustar00rootroot00000000000000libfplus-0.2.13/api_search/frontend/src/highlight/LICENSE000066400000000000000000000027321376322245400231110ustar00rootroot00000000000000Copyright (c) 2006, Ivan Sagalaev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of highlight.js nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libfplus-0.2.13/api_search/frontend/src/highlight/highlight.pack.js000066400000000000000000000341251376322245400253270ustar00rootroot00000000000000/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/&/g,"&").replace(//g,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return w(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||w(i))return i}function o(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){s+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var l=0,s="",f=[];e.length||r.length;){var g=i();if(s+=n(a.substring(l,g[0].offset)),l=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===l);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return s+n(a.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(n){return o(e,{v:null},n)})),e.cached_variants||e.eW&&[o(e)]||[e]}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var o={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):x(a.k).forEach(function(e){u(e,a.k[e])}),a.k=o}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]),a.c=Array.prototype.concat.apply([],a.c.map(function(e){return l("self"===e?a:e)})),a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var c=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=c.length?t(c.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function l(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":I.classPrefix,i='',i+n+o}function h(){var e,t,r,a;if(!E.k)return n(k);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(k);r;)a+=n(k.substring(t,r.index)),e=l(E,r),e?(B+=e[1],a+=p(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(k);return a+n(k.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!y[E.sL])return n(k);var t=e?f(E.sL,k,!0,x[E.sL]):g(k,E.sL.length?E.sL:void 0);return E.r>0&&(B+=t.r),e&&(x[E.sL]=t.top),p(t.language,t.value,!1,!0)}function b(){L+=null!=E.sL?d():h(),k=""}function v(e){L+=e.cN?p(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(k+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?k+=n:(t.eB&&(k+=n),b(),t.rB||t.eB||(k=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?k+=n:(a.rE||a.eE||(k+=n),b(),a.eE&&(k=n));do E.cN&&(L+=C),E.skip||(B+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return k+=n,n.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,E=i||N,x={},L="";for(R=E;R!==N;R=R.parent)R.cN&&(L=p(R.cN,"",!0)+L);var k="",B=0;try{for(var M,j,O=0;;){if(E.t.lastIndex=O,M=E.t.exec(t),!M)break;j=m(t.substring(O,M.index),M[0]),O=M.index+j}for(m(t.substr(O)),R=E;R.parent;R=R.parent)R.cN&&(L+=C);return{r:B,value:L,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function g(e,t){t=t||I.languages||x(y);var r={r:0,value:n(e)},a=r;return t.filter(w).forEach(function(n){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function p(e){return I.tabReplace||I.useBR?e.replace(M,function(e,n){return I.useBR&&"\n"===e?"
":I.tabReplace?n.replace(/\t/g,I.tabReplace):""}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function d(e){var n,t,r,o,l,s=i(e);a(s)||(I.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,l=n.textContent,r=s?f(s,l,!0):g(l),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),l)),r.value=p(r.value),e.innerHTML=r.value,e.className=h(e.className,s,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function b(e){I=o(I,e)}function v(){if(!v.called){v.called=!0;var e=document.querySelectorAll("pre code");E.forEach.call(e,d)}}function m(){addEventListener("DOMContentLoaded",v,!1),addEventListener("load",v,!1)}function N(n,t){var r=y[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function R(){return x(y)}function w(e){return e=(e||"").toLowerCase(),y[e]||y[L[e]]}var E=[],x=Object.keys,y={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",I={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=f,e.highlightAuto=g,e.fixMarkup=p,e.highlightBlock=d,e.configure=b,e.initHighlighting=v,e.initHighlightingOnLoad=m,e.registerLanguage=N,e.listLanguages=R,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("elm",function(e){var i={v:[e.C("--","$"),e.C("{-","-}",{c:["self"]})]},t={cN:"type",b:"\\b[A-Z][\\w']*",r:0},c={b:"\\(",e:"\\)",i:'"',c:[{cN:"type",b:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},i]},n={b:"{",e:"}",c:c.c};return{k:"let in if then else case of where module import exposing type alias as infix infixl infixr port effect command subscription",c:[{bK:"port effect module",e:"exposing",k:"port effect module where command subscription exposing",c:[c,i],i:"\\W\\.|;"},{b:"import",e:"$",k:"import as exposing",c:[c,i],i:"\\W\\.|;"},{b:"type",e:"$",k:"type alias",c:[t,c,n,i]},{bK:"infix infixl infixr",e:"$",c:[e.CNM,i]},{b:"port",e:"$",k:"port",c:[i]},e.QSM,e.CNM,t,e.inherit(e.TM,{b:"^[_a-z][\\w']*"}),i,{b:"->|<-"}],i:/;/}});hljs.registerLanguage("cpp",function(t){var e={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[{b:'(u8?|U)?L?"',e:'"',i:"\\n",c:[t.BE]},{b:'(u8?|U)?R"',e:'"',c:[t.BE]},{b:"'\\\\?.",e:"'",i:"."}]},s={cN:"number",v:[{b:"\\b(0b[01']+)"},{b:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{b:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],r:0},i={cN:"meta",b:/#\s*[a-z]+\b/,e:/$/,k:{"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include"},c:[{b:/\\\n/,r:0},t.inherit(r,{cN:"meta-string"}),{cN:"meta-string",b:/<[^\n>]*>/,e:/$/,i:"\\n"},t.CLCM,t.CBCM]},a=t.IR+"\\s*\\(",c={keyword:"int float while private char catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and or not",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr",literal:"true false nullptr NULL"},n=[e,t.CLCM,t.CBCM,s,r];return{aliases:["c","cc","h","c++","h++","hpp"],k:c,i:"",k:c,c:["self",e]},{b:t.IR+"::",k:c},{v:[{b:/=/,e:/;/},{b:/\(/,e:/\)/},{bK:"new throw return else",e:/;/}],k:c,c:n.concat([{b:/\(/,e:/\)/,k:c,c:n.concat(["self"]),r:0}]),r:0},{cN:"function",b:"("+t.IR+"[\\*&\\s]+)+"+a,rB:!0,e:/[{;=]/,eE:!0,k:c,i:/[^\w\s\*&]/,c:[{b:a,rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:c,r:0,c:[t.CLCM,t.CBCM,r,s,e]},t.CLCM,t.CBCM,i]},{cN:"class",bK:"class struct",e:/[{;:]/,c:[{b://,c:["self"]},t.TM]}]),exports:{preprocessor:i,strings:r,k:c}}});hljs.registerLanguage("haskell",function(e){var i={v:[e.C("--","$"),e.C("{-","-}",{c:["self"]})]},a={cN:"meta",b:"{-#",e:"#-}"},l={cN:"meta",b:"^#",e:"$"},c={cN:"type",b:"\\b[A-Z][\\w']*",r:0},n={b:"\\(",e:"\\)",i:'"',c:[a,l,{cN:"type",b:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TM,{b:"[_a-z][\\w']*"}),i]},s={b:"{",e:"}",c:n.c};return{aliases:["hs"],k:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",c:[{bK:"module",e:"where",k:"module where",c:[n,i],i:"\\W\\.|;"},{b:"\\bimport\\b",e:"$",k:"import qualified as hiding",c:[n,i],i:"\\W\\.|;"},{cN:"class",b:"^(\\s*)?(class|instance)\\b",e:"where",k:"class family instance where",c:[c,n,i]},{cN:"class",b:"\\b(data|(new)?type)\\b",e:"$",k:"data family type newtype deriving",c:[a,c,n,s,i]},{bK:"default",e:"$",c:[c,n,i]},{bK:"infix infixl infixr",e:"$",c:[e.CNM,i]},{b:"\\bforeign\\b",e:"$",k:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",c:[c,e.QSM,i]},{cN:"meta",b:"#!\\/usr\\/bin\\/env runhaskell",e:"$"},a,l,e.QSM,e.CNM,c,e.inherit(e.TM,{b:"^[_a-z][\\w']*"}),i,{b:"->|<-"}]}});hljs.registerLanguage("xml",function(s){var e="[A-Za-z0-9\\._:-]+",t={eW:!0,i:/`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0}]},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[t],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[t],starts:{e:"",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},t]}]}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}});libfplus-0.2.13/api_search/frontend/src/highlight/styles/000077500000000000000000000000001376322245400234235ustar00rootroot00000000000000libfplus-0.2.13/api_search/frontend/src/highlight/styles/default.css000066400000000000000000000022071376322245400255620ustar00rootroot00000000000000/* Original highlight.js style (c) Ivan Sagalaev */ .hljs { display: block; overflow-x: auto; padding: 0.5em; background: #F0F0F0; } /* Base color: saturation 0; */ .hljs, .hljs-subst { color: #444; } .hljs-comment { color: #888888; } .hljs-keyword, .hljs-attribute, .hljs-selector-tag, .hljs-meta-keyword, .hljs-doctag, .hljs-name { font-weight: bold; } /* User color: hue: 0 */ .hljs-type, .hljs-string, .hljs-number, .hljs-selector-id, .hljs-selector-class, .hljs-quote, .hljs-template-tag, .hljs-deletion { color: #880000; } .hljs-title, .hljs-section { color: #880000; font-weight: bold; } .hljs-regexp, .hljs-symbol, .hljs-variable, .hljs-template-variable, .hljs-link, .hljs-selector-attr, .hljs-selector-pseudo { color: #BC6060; } /* Language color: hue: 90; */ .hljs-literal { color: #78A960; } .hljs-built_in, .hljs-bullet, .hljs-code, .hljs-addition { color: #397300; } /* Meta color: hue: 200 */ .hljs-meta { color: #1f7199; } .hljs-meta-string { color: #4d99bf; } /* Misc effects */ .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: bold; } libfplus-0.2.13/api_search/frontend/src/highlight/styles/monokai-sublime.css000066400000000000000000000020021376322245400272220ustar00rootroot00000000000000/* Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/ */ .hljs { display: block; overflow-x: auto; padding: 0.5em; background: #23241f; } .hljs, .hljs-tag, .hljs-subst { color: #f8f8f2; } .hljs-strong, .hljs-emphasis { color: #a8a8a2; } .hljs-bullet, .hljs-quote, .hljs-number, .hljs-regexp, .hljs-literal, .hljs-link { color: #ae81ff; } .hljs-code, .hljs-title, .hljs-section, .hljs-selector-class { color: #a6e22e; } .hljs-strong { font-weight: bold; } .hljs-emphasis { font-style: italic; } .hljs-keyword, .hljs-selector-tag, .hljs-name, .hljs-attr { color: #f92672; } .hljs-symbol, .hljs-attribute { color: #66d9ef; } .hljs-params, .hljs-class .hljs-title { color: #f8f8f2; } .hljs-string, .hljs-type, .hljs-built_in, .hljs-builtin-name, .hljs-selector-id, .hljs-selector-attr, .hljs-selector-pseudo, .hljs-addition, .hljs-variable, .hljs-template-variable { color: #e6db74; } .hljs-comment, .hljs-deletion, .hljs-meta { color: #75715e; } libfplus-0.2.13/api_search/frontend/src/htmlmain.js000066400000000000000000000001771376322245400223050ustar00rootroot00000000000000function init() { var mainDiv = document.getElementById('main'); elmContent = Elm.FPlusApiSearch.init({ node: mainDiv }); }libfplus-0.2.13/api_search/frontend/src/htmlmain_explore.js000066400000000000000000000002001376322245400240260ustar00rootroot00000000000000function init() { var mainDiv = document.getElementById('main'); elmContent = Elm.FPlusApiExplore.init({ node: mainDiv }); }libfplus-0.2.13/api_search/frontend/src/index.html000066400000000000000000000015761376322245400221370ustar00rootroot00000000000000 FunctionalPlus API search
libfplus-0.2.13/api_search/frontend/src/style.css000066400000000000000000000016601376322245400220060ustar00rootroot00000000000000body { color: #f8f8f2; background-color: #23241f; font-size: 110%; font-family: monospace; } input { font-size: 110%; } pre { padding: 0px; border: 0px; margin: 0px; } code { white-space: pre; padding: 0px; border: 0px; margin: 0px; } .mainwrapper { } .main { margin: 0 auto; width: 800px; } .logo { margin: 4px; } @media only screen and (max-height: 740px) { .logo { margin: -176px 0 0 0px; } } .githublink { text-align: center; } .githublink a:link { color: #f8f8f2 } .githublink a:visited { color: #f8f8f2 } .githublink a:hover { color: #f8f8f2 } .githublink a:active { color: #f8f8f2 } .functionnameandsig { } .parsedsignature { color: #75715e; margin: 8px; } .queryhelper { color: #75715e; margin: 8px; } .functionnameandsig { } .functiondoc { } .functiondecl { } .functionrating { color: #75715e; } .footer { } libfplus-0.2.13/api_search/frontend/src/style_explore.css000066400000000000000000000010631376322245400235410ustar00rootroot00000000000000body { font-family: monospace; } pre { padding: 0px; border: 0px; margin: 0px; } code { white-space: pre; padding: 0px; border: 0px; margin: 0px; } .mainwrapper { } .main { margin: 0 auto; width: 800px; } .logo { margin: 4px; } @media only screen and (max-height: 740px) { .logo { margin: -176px 0 0 0px; } } .githublink { text-align: center; } .functionnameandsig { } .functionnameandsig { } .functiondoc { } .function { margin-top: 16px; margin-bottom: 16px; } .footer { } libfplus-0.2.13/api_search/generate_database.sh000077500000000000000000000010651376322245400215020ustar00rootroot00000000000000#!/usr/bin/env bash 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 if [ -f ./temp_FunctionalPlus_api__clang ]; then g++ -E -CC -std=c++14 -w -I../include ../include/fplus/fplus.hpp > temp_preprocessor_output.txt ./temp_FunctionalPlus_api__clang temp_preprocessor_output.txt rm ./temp_FunctionalPlus_api__clang rm -f temp_preprocessor_output.txt fi libfplus-0.2.13/api_search/parse_source_files.cpp000066400000000000000000000176751376322245400221230ustar00rootroot00000000000000// Copyright Tobias Hermann 2015. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include typedef std::vector string_vec; const std::string api_search_type_key = "API search type: "; const std::string comment_token = "// "; const auto is_comment = fplus::bind_1st_of_2( fplus::is_prefix_of, comment_token); struct function_help { std::string name; std::string signature; std::string documentation; std::string declaration; }; std::string get_function_help_name(const function_help& f) { return f.name; } std::string get_function_help_signature(const function_help& f) { return f.signature; } std::string get_function_help_documentation(const function_help& f) { return f.documentation; } std::string get_function_help_declaration(const function_help& f) { return f.declaration; } std::ostream & operator<<(std::ostream &os, const function_help& f) { return os << "Name: " << f.name << "\n" << "Type: " << f.signature << "\n" << "Doc: " << f.documentation << "\n" << "Decl: " << f.declaration; } function_help lines_to_function_help(const string_vec& lines) { const auto trim_line = fplus::bind_1st_of_2( fplus::trim_token_left, comment_token); const auto trim_type_line = fplus::bind_1st_of_2( fplus::trim_token_left, api_search_type_key); const auto trim_lines = fplus::bind_1st_of_2( fplus::transform, trim_line); const auto search_type = trim_type_line(trim_line(lines[0])); const auto doc_and_decl = fplus::span(is_comment, fplus::tail(lines)); const auto doc_and_decl_trimmed = fplus::transform_pair( trim_lines, trim_lines, doc_and_decl); auto name_and_type = fplus::split_by_token(std::string(" : "), false, search_type); assert(name_and_type.size() == 2); return { name_and_type[0], name_and_type[1], fplus::join(std::string("\n"), doc_and_decl_trimmed.first), fplus::join(std::string("\n"), doc_and_decl_trimmed.second) }; } bool begins_with_curly_open(const std::string& line) { return (!line.empty() && line.front() == '{'); } const auto no_curly_open = fplus::logical_not(begins_with_curly_open); std::vector parse_code_file(const std::string& code_file) { using namespace std; typedef vector strings; const auto lines = fplus::read_text_file_lines(true, code_file)(); const auto is_search_type = fplus::bind_1st_of_2( fplus::is_infix_of, api_search_type_key); const auto functions_lines = fplus::split_by_keep_separators( is_search_type, lines); if (functions_lines.size() < 2) return {}; const auto get_non_impl_lines = fplus::bind_1st_of_2( fplus::take_while, no_curly_open); const auto functions_docs = fplus::transform( get_non_impl_lines, fplus::tail(functions_lines)); return fplus::transform(lines_to_function_help, functions_docs); } std::vector get_broken_function_helps( const std::vector& helps) { auto help_is_ok = [](const function_help& help) { return !help.name.empty() && !help.signature.empty() && !help.declaration.empty() && !help.documentation.empty(); }; return fplus::drop_if(help_is_ok, helps); } std::string functions_to_elm_code(const std::vector functions) { auto escape_backslashes = [](const std::string& str) -> std::string { return fplus::replace_tokens( std::string("\\"), std::string("\\\\"), str); }; auto escape_quotation_marks = [](const std::string& str) -> std::string { return fplus::replace_tokens( std::string("\""), std::string("\\\""), str); }; auto escape_newlines = [](const std::string& str) { return fplus::replace_tokens( std::string("\n"), std::string("\\n"), str); }; auto escape_special_characters = fplus::compose( escape_backslashes, escape_quotation_marks, escape_newlines); auto show_function = [&](const function_help& f) -> std::string { std::string str; str += std::string("{ name = \"") + f.name + "\""; str += std::string(", signature = \"") + f.signature + "\""; str += std::string(", documentation = \"") + escape_special_characters(f.documentation) + "\""; str += std::string(", declaration = \"") + escape_special_characters(f.declaration) + "\" }"; return str; }; auto function_strings = fplus::transform(show_function, functions); const auto chunks = fplus::split_every(64, function_strings); std::string result; result += "module Database exposing (..)\n\n\n"; result += "type alias Function =\n"; result += " { name : String\n"; result += " , signature : String\n"; result += " , documentation : String\n"; result += " , declaration : String\n"; result += " }\n\n\n"; result += "functions : List Function\n"; result += "functions =\n"; const auto show_chunk = [](const string_vec& strs) -> std::string { std::string res; res += " [ "; res += fplus::join(std::string("\n , "), strs); res += "\n ]"; return res; }; result += fplus::join( std::string("\n ++\n"), fplus::transform(show_chunk, chunks)); return result; } void print_duplicates(const string_vec& strs) { const auto occurences = fplus::count_occurrences(strs); typedef decltype(occurences)::value_type pair; const string_vec allowed_dups = {"and_then_maybe", "and_then_result", "compose", "show"}; const auto dups = fplus::keep_if([&](const pair& p) -> bool { return p.second > 1 && !fplus::is_elem_of(p.first, allowed_dups); }, fplus::map_to_pairs(occurences)); if (!dups.empty()) { std::cerr << "Duplicates!\n"; std::cerr << fplus::show_cont(dups) << std::endl; } std::cout << "---" << std::endl; } int main (int argc, char *argv[]) { if (argc != 2) { std::cerr << "Provide code file via command line." << std::endl; return 1; } const auto functions = fplus::sort_on(get_function_help_name, parse_code_file(std::string(argv[1]))); const auto broken = get_broken_function_helps(functions); std::cout << "broken:" << std::endl; if (fplus::is_not_empty(broken)) { std::cout << fplus::show_cont_with_frame_and_newlines( "\n-----\n", "[", "]", get_broken_function_helps(functions), 0) << std::endl; return 1; } std::cout << "---" << std::endl; using fplus::transform; std::cout << "duplicate help names:" << std::endl; print_duplicates(transform(get_function_help_name, functions)); //print_duplicates(transform(get_function_help_signature, functions)); std::cout << "duplicate help documentations:" << std::endl; print_duplicates(transform(get_function_help_documentation, functions)); std::cout << "duplicate help declarations:" << std::endl; print_duplicates(transform(get_function_help_declaration, functions)); auto output = functions_to_elm_code(functions); std::string out_file = "frontend/src/Database.elm"; if (fplus::write_text_file(out_file, output)()) { std::cout << out_file << " written." << std::endl; } else { std::cerr << "Error: Unable to write " << out_file << std::endl; } } libfplus-0.2.13/appveyor.yml000066400000000000000000000030451376322245400160170ustar00rootroot00000000000000version: '{build}' branches: only: - master clone_folder: c:\projects\fplus image: - Visual Studio 2015 - Visual Studio 2017 - Visual Studio 2019 configuration: - Release platform: - x64 environment: matrix: - arch: Win64 # - arch: #does not work, Release|x64 not a valid target matrix: fast_finish: true # skip unsupported combinations init: - set arch= - if "%arch%"=="Win64" ( set arch= Win64) - echo %arch% - echo %APPVEYOR_BUILD_WORKER_IMAGE% - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" ( set generator="Visual Studio 14 2015%arch%" ) - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" ( set generator="Visual Studio 15 2017%arch%" ) - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" ( set generator="Visual Studio 16 2019" -A x64 ) - echo %generator% before_build: - cmd: |- cd c:\projects git clone https://github.com/onqtam/doctest.git cd doctest mkdir build cd build cmake .. -DDOCTEST_WITH_TESTS=off -DDOCTEST_WITH_MAIN_IN_STATIC_LIB=OFF -DCMAKE_INSTALL_PREFIX=../install -G %generator% cmake --build . --config Release --target INSTALL cd c:\projects\fplus mkdir build cd build cmake --version cmake .. -DFPLUS_BUILD_EXAMPLES=ON -DFPLUS_BUILD_UNITTEST=ON -DCMAKE_INSTALL_PREFIX=c:/projects/fplus/install -Ddoctest_DIR=C:/projects/doctest/install/lib/cmake/doctest -G %generator% build: project: c:\projects\fplus\build\FunctionalPlus.sln verbosity: minimal parallel: true test_script: - ps: cd c:\projects\fplus\build - ctest -C Release --output-on-failure libfplus-0.2.13/cmake/000077500000000000000000000000001376322245400145055ustar00rootroot00000000000000libfplus-0.2.13/cmake/Config.cmake.in000066400000000000000000000002351376322245400173210ustar00rootroot00000000000000@PACKAGE_INIT@ find_package(Threads REQUIRED) include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") check_required_components("@PROJECT_NAME@") libfplus-0.2.13/cmake/Finddoctest.cmake000066400000000000000000000011521376322245400177540ustar00rootroot00000000000000find_package(PkgConfig) pkg_check_modules(PKG_doctest QUIET doctest) set(doctest_DEFINITIONS ${PKG_doctest_CFLAGS_OTHER}) find_path(doctest_INCLUDE_DIR "doctest.h" HINTS ${PKG_doctest_INCLUDE_DIRS} "${doctest_DIR}/include" ) set(doctest_INCLUDE_DIRS ${doctest_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(doctest DEFAULT_MSG doctest_INCLUDE_DIR ) mark_as_advanced(doctest_INCLUDE_DIR) libfplus-0.2.13/cmake/install_libcxx.sh000077500000000000000000000031461376322245400200670ustar00rootroot00000000000000#!/usr/bin/env bash BUILD_JOBS=4 VERSION=$(echo "${CLANG_VERSION}" | sed 's/\.//g') echo "Fetching libc++ and libc++abi '${VERSION}'..." # Checkout LLVM sources git clone --depth=1 --branch=release_${VERSION} https://github.com/llvm-mirror/llvm.git llvm-source git clone --depth=1 --branch=release_${VERSION} https://github.com/llvm-mirror/libcxx.git llvm-source/projects/libcxx git clone --depth=1 --branch=release_${VERSION} https://github.com/llvm-mirror/libcxxabi.git llvm-source/projects/libcxxabi mkdir llvm-build cd llvm-build # - libc++ versions < 4.x do not have the install-cxxabi and install-cxx targets # - only ASAN is enabled for clang/libc++ versions < 4.x if [[ ${VERSION} == "3"* ]]; then echo "Clang 3.x" cmake -DCMAKE_C_COMPILER=${CC} \ -DCMAKE_CXX_COMPILER=${CXX} \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=/usr \ ../llvm-source if [[ $SANITIZER == "Address;Undefined" ]]; then ASAN_FLAGS="-fsanitize=address" cmake -DCMAKE_CXX_FLAGS="${ASAN_FLAGS}" \ -DCMAKE_EXE_LINKER_FLAGS="${ASAN_FLAGS}" \ ../llvm-source fi make cxx -j${BUILD_JOBS} sudo cp -r lib/* /usr/lib/ sudo cp -r include/c++ /usr/include/ else cmake -DCMAKE_C_COMPILER=${CC} \ -DCMAKE_CXX_COMPILER=${CXX} \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=/usr \ -DLIBCXX_ABI_UNSTABLE=ON \ -DLLVM_USE_SANITIZER=${SANITIZER} \ ../llvm-source make cxx -j${BUILD_JOBS} sudo make install-cxxabi install-cxx fi exit 0 libfplus-0.2.13/cmake/pkgconfig.cmake000066400000000000000000000040341376322245400174570ustar00rootroot00000000000000# Installation (https://github.com/forexample/package-example) { # Layout. This works for all platforms: # * /lib/cmake/ # * /lib/ # * /include/ set(config_install_dir "lib/cmake/${PROJECT_NAME}") set(include_install_dir "include") set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") # Configuration set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(namespace "${PROJECT_NAME}::") # Include module with fuction 'write_basic_package_version_file' include(CMakePackageConfigHelpers) # Configure 'ConfigVersion.cmake' # Use: # * PROJECT_VERSION write_basic_package_version_file( "${version_config}" COMPATIBILITY SameMajorVersion ) # Configure 'Config.cmake' # Use variables: # * TARGETS_EXPORT_NAME # * PROJECT_NAME configure_package_config_file( "cmake/Config.cmake.in" "${project_config}" INSTALL_DESTINATION "${config_install_dir}" ) # Targets: # * header location after install: /include/fplus/fplus.hpp # * headers can be included by C++ code `#include ` install( TARGETS fplus EXPORT "${TARGETS_EXPORT_NAME}" LIBRARY DESTINATION "lib" ARCHIVE DESTINATION "lib" RUNTIME DESTINATION "bin" INCLUDES DESTINATION "${include_install_dir}" ) # Headers: # * include/fplus/fplus.hpp -> /include/fplus/fplus.hpp install( DIRECTORY "include/fplus" # no trailing slash DESTINATION "${include_install_dir}" ) # Config # * /lib/cmake/FunctionalPlus/FunctionalPlusConfig.cmake # * /lib/cmake/FunctionalPlus/FunctionalPlusConfigVersion.cmake install( FILES "${project_config}" "${version_config}" DESTINATION "${config_install_dir}" ) # Config # * /lib/cmake/FunctionalPlus/FunctionalPlusTargets.cmake install( EXPORT "${TARGETS_EXPORT_NAME}" NAMESPACE "${namespace}" DESTINATION "${config_install_dir}" ) # } libfplus-0.2.13/cmake/toolchain.cmake000066400000000000000000000004171376322245400174710ustar00rootroot00000000000000set(COMPILE_OPTIONS -Wall -Wextra -pedantic -Werror -Weffc++ -Wconversion -Wsign-conversion -Wctor-dtor-privacy -Wreorder -Wold-style-cast -Wparentheses ) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) libfplus-0.2.13/cmake/toolchain_msvc.cmake000066400000000000000000000041211376322245400205150ustar00rootroot00000000000000set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # Set warning level 4 (the most elevated level is /Wall but it is too noisy) add_compile_options("/W4") # Warnings as errors add_compile_options("/WX") set(msvc_disabled_warnings 4459 # declaration of 'xs' hides global declaration 4127 # conditional expression is constant 4100 # unreferenced formal parameter 4189 # local variable is initialized but not referenced 4244 # '=': conversion from 'unsigned int' to 'char', possible loss of data ) foreach(warningNumber ${msvc_disabled_warnings}) add_compile_options(/wd${warningNumber}) endforeach() # Additional warnings on top of the level 4 warnings set(msvc_additional_warnings 4263 # member function does not override any base class virtual member function) 4264 # no override available for virtual member function; function is hidden 4242 # conversion from ‘type1’ to ‘type2’, possible loss of data 4266 # no override available for virtual member function from base ‘type’; function is hidden 4302 # truncation from ‘type 1’ to ‘type 2’ 4826 # conversion from ‘type1’ to ‘type2’ is sign-extended. This may cause unexpected runtime behavior 4905 # wide string literal cast to ‘LPSTR’ 4906 # string literal cast to ‘LPWSTR’ 4389 # signed/unsigned mismatch 4239 # nonstandard extension used: 'argument': conversion from T to T & 4928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied 4505 # unreferenced local function has been removed 4265 # 'class': class has virtual functions, but destructor is not virtual 4191 # unsafe conversion from ‘type of expression’ to ‘type required’ 4456 # declaration of variable hides previous local declaration 4458 # declaration of variable hides class member # 4711 # The compiler performed inlining on the given function, although it was not marked for inlining ) foreach(warningNumber ${msvc_additional_warnings}) add_compile_options(/we${warningNumber}) endforeach() libfplus-0.2.13/conan_build.py000066400000000000000000000017641376322245400162640ustar00rootroot00000000000000from cpt.packager import ConanMultiPackager import os if __name__ == "__main__": if os.getenv("CXX") == "g++-7": version = os.getenv("TRAVIS_TAG") if not version: version = "dev" reference = "functionalplus/%s" % version username = "dobiasd" channel = "stable" upload_remote = "https://api.bintray.com/conan/dobiasd/public-conan" if version is not "dev" else None print("Conan package metadata:", reference, username, channel, upload_remote) builder = ConanMultiPackager(reference=reference, username=username, channel=channel, upload=upload_remote) builder.add(settings={ 'os': 'Linux', 'compiler.version': '7', 'compiler.libcxx': 'libstdc++11', 'arch': 'x86_64', 'build_type': 'Release', 'compiler': 'gcc' }) builder.run() libfplus-0.2.13/conanfile.py000066400000000000000000000017021376322245400157350ustar00rootroot00000000000000from conans import ConanFile, CMake class FunctionalPlusConan(ConanFile): name = "functionalplus" version = "v0.2.13-p0" license = "Boost Software License 1.0" url = "https://github.com/Dobiasd/FunctionalPlus" description = "Functional Programming Library for C++. Write concise and readable C++ code." exports_sources = ["examples*", "cmake*", "include*", "CMakeLists.txt", "LICENSE"] options = { "build_unittest": [True, False], } default_options = "build_unittest=False", generators = ["cmake"] def requirements(self): if self.options.build_unittest: self.requires.add('doctest/2.3.4@bincrafters/stable') def package(self): # CMake cmake = CMake(self) cmake.configure() cmake.install() # File copy self.copy("*LICENSE", dst="licenses") self.copy("*.hpp", src="include") self.copy("*.autogenerated_defines", src=".") libfplus-0.2.13/examples/000077500000000000000000000000001376322245400152435ustar00rootroot00000000000000libfplus-0.2.13/examples/99_problems.cpp000066400000000000000000000144121376322245400201150ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include using namespace fplus; typedef std::vector Ints; typedef std::vector Intss; typedef std::vector Idxs; Ints xs = {0,2,2,2,1,3,3,4}; Intss xss = {{0,1,2},{3,4}}; typedef std::pair size_t_int_pair; typedef std::vector size_t_int_pairs; size_t_int_pairs xs_run_length_encoded = {{1, 0}, {3, 2}, {1, 1}, {2, 3}, {1, 4}}; template void print_result(const T& x) { std::cout << show(x) << std::endl; } // P01 (*) Find the last box of a list. void problem_01() { print_result(last(xs)); } // P02 (*) Find the last but one box of a list. void problem_02() { print_result(elem_at_idx(size_of_cont(xs) - 2, xs)); } // P03 (*) Find the K'th element of a list. void problem_03() { print_result(elem_at_idx(2, xs)); } // P04 (*) Find the number of elements of a list. void problem_04() { print_result(size_of_cont(xs)); } // P05 (*) Reverse a list. void problem_05() { print_result(reverse(xs)); } // P06 (*) Find out whether a list is a palindrome. void problem_06() { print_result(xs == reverse(xs)); } // P07 (**) Flatten a nested list structure. void problem_07() { print_result(concat(xss)); } // P08 (**) Eliminate consecutive duplicates of list elements. void problem_08() { print_result(unique(xs)); } // P09 (**) Pack consecutive duplicates of list elements into sublists. void problem_09() { print_result(group(xs)); } // P10 (*) Run-length encoding of a list. void problem_10() { auto group_to_pair = [](const Ints& group) -> size_t_int_pair { return std::make_pair(size_of_cont(group), group.front()); }; print_result(transform(group_to_pair, group(xs))); } // P11 (*) Modified run-length encoding. void problem_11() { const auto modify = [](const auto& p) -> Ints { return p.first == 1 ? Ints({p.second}) : Ints({static_cast(p.first), p.second}); }; print_result(fwd::apply(xs, fwd::run_length_encode(), fwd::transform(modify) )); } // P12 (**) Decode a run-length encoded list. void problem_12() { const auto pair_to_vec = [](const size_t_int_pair& p) -> Ints { return replicate(p.first, p.second); }; print_result(concat(transform(pair_to_vec, xs_run_length_encoded))); } // P13 (**) Run-length encoding of a list (direct solution). void problem_13() { const auto f = [](const Intss& acc, int x) -> Intss { if (is_empty(acc)) { return singleton_seq(singleton_seq(x)); } else if (size_of_cont(acc.back()) == 1 && acc.back().back() == x) { return replace_elem_at_idx(size_of_cont(acc) - 1, {2, x}, acc); } else { if (acc.back().back() == x) { return replace_elem_at_idx( size_of_cont(acc) - 1, {acc.back().front() + 1, x}, acc); } else { return append_elem(singleton_seq(x), acc); } } }; print_result(fold_left(f, Intss(), xs)); } // P14 (*) Duplicate the elements of a list. void problem_14() { print_result(replicate_elems(2, xs)); } // P15 (**) Replicate the elements of a list a given number of times. void problem_15() { print_result(replicate_elems(3, xs)); } // P16 (**) Drop every N'th element from a list. void problem_16() { std::size_t n = 3; print_result( drop_idxs(numbers_step(std::size_t(0), size_of_cont(xs), n), xs)); } // P17 (*) Split a list into two parts; the length of the first part is given. void problem_17() { print_result(split_at_idx(2, xs)); } // P18 (**) Extract a slice from a list. void problem_18() { print_result(get_segment(2, 4+1, xs)); } // P19 (**) Rotate a list N places to the left. void problem_19() { print_result(apply_function_n_times(rotate_left, 3, xs)); } // P20 (*) Remove the K'th element from a list. void problem_20() { print_result(drop_idx(0, xs)); } // P21 (*) Insert an element at a given position into a list. void problem_21() { print_result(insert_at_idx(3, -1, xs)); } // P22 (*) Create a list containing all integers within a given range. void problem_22() { print_result(numbers(3, 8)); } // P23 (**) Extract a given number of randomly selected elements from a list. void problem_23() { print_result(sample(std::random_device()(), 3, xs)); } // P24 (*) Lotto: Draw N different random numbers from the set 1..M. void problem_24() { print_result(sample(std::random_device()(), 3, numbers(1, 100))); } // P25 (*) Generate a random permutation of the elements of a list. void problem_25() { print_result(shuffle(std::random_device()(), xs)); } // P26 (**) Generate the combinations of K distinct objects chosen from the N elements of a list void problem_26() { print_result(combinations(3, xs)); } // P27 (**) Group the elements of a set into disjoint subsets. void problem_27() { // todo ;) } // P28 (**) Sorting a list of lists according to length of sublists void problem_28() { print_result(sort_on(size_of_cont, xss)); } // Let's follow tradition for once and at least do the list-related problems. // L-99: Ninety-Nine Lisp Problems: http://www.ic.unicamp.br/~meidanis/courses/mc336/2006s2/funcional/L-99_Ninety-Nine_Lisp_Problems.html // P-99: Ninety-Nine Prolog Problems: https://sites.google.com/site/prologsite/prolog-problems // H-99: Ninety-Nine Haskell Problems: https://wiki.haskell.org/H-99:_Ninety-Nine_Haskell_Problems int main() { problem_01(); problem_02(); problem_03(); problem_04(); problem_05(); problem_06(); problem_07(); problem_08(); problem_09(); problem_10(); problem_11(); problem_12(); problem_13(); problem_14(); problem_15(); problem_16(); problem_17(); problem_18(); problem_19(); problem_20(); problem_21(); problem_22(); problem_23(); problem_24(); problem_25(); problem_26(); problem_27(); problem_28(); } libfplus-0.2.13/examples/compile_and_run.sh000077500000000000000000000026631376322245400207470ustar00rootroot00000000000000#!/usr/bin/env bash g++ -std=c++14 -O3 -Wall -Wextra -pedantic -Wshadow -Werror -Weffc++ -Wconversion -Wsign-conversion -Wctor-dtor-privacy -Wreorder -Wold-style-cast -Wparentheses -pthread -o ./temp_99_problems__gcc -I./../include 99_problems.cpp if [ -f ./temp_99_problems__gcc ]; then ./temp_99_problems__gcc rm ./temp_99_problems__gcc fi clang++ -std=c++14 -O3 -Wall -Wextra -pedantic -Wshadow -Werror -Weffc++ -Wconversion -Wsign-conversion -Wctor-dtor-privacy -Wreorder -Wold-style-cast -Wparentheses -pthread -o ./temp_99_problems__clang -I./../include 99_problems.cpp if [ -f ./temp_99_problems__clang ]; then ./temp_99_problems__clang rm ./temp_99_problems__clang fi g++ -std=c++14 -O3 -Wall -Wextra -pedantic -Wshadow -Werror -Weffc++ -Wconversion -Wsign-conversion -Wctor-dtor-privacy -Wreorder -Wold-style-cast -Wparentheses -pthread -o ./temp_readme_perf_examples__gcc -I./../include readme_perf_examples.cpp if [ -f ./temp_readme_perf_examples__gcc ]; then ./temp_readme_perf_examples__gcc rm ./temp_readme_perf_examples__gcc fi clang++ -std=c++14 -O3 -Wall -Wextra -pedantic -Wshadow -Werror -Weffc++ -Wconversion -Wsign-conversion -Wctor-dtor-privacy -Wreorder -Wold-style-cast -Wparentheses -pthread -o ./temp_readme_perf_examples__clang -I./../include readme_perf_examples.cpp if [ -f ./temp_readme_perf_examples__clang ]; then ./temp_readme_perf_examples__clang rm ./temp_readme_perf_examples__clang fi libfplus-0.2.13/examples/performance_range_v3.cpp000066400000000000000000000042071376322245400220370ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include const auto times_3 = [](int i){return 3 * i;}; const auto is_odd_int = [](int i){return i % 2 == 0;}; const auto as_string_length = [](int i){return std::to_string(i).size();}; int main() { const auto times_3 = [](int i){return 3 * i;}; const auto is_odd_int = [](int i){return i % 2 == 0;}; const auto as_string_length = [](int i){return std::to_string(i).size();}; typedef std::chrono::time_point Time; for (int i = 0; i < 3; ++i) { // FunctionalPlus Time startTimeFPlus = std::chrono::system_clock::now(); using namespace fplus; const auto result_fplus = fwd::apply( numbers(0, 15000000) , fwd::transform(times_3) , fwd::drop_if(is_odd_int) , fwd::transform(as_string_length) , fwd::sum()); Time endTimeFPlus = std::chrono::system_clock::now(); std::chrono::duration elapsed_s_fplus = endTimeFPlus - startTimeFPlus; std::cout << "(check: " << result_fplus << "), elapsed time fplus: " << elapsed_s_fplus.count() << "s\n"; // range-v3 Time startTimeRangev3 = std::chrono::system_clock::now(); using namespace ranges; const auto result_range_v3 = accumulate( view::ints(0) | view::take(15000000) | view::transform(times_3) | view::remove_if(is_odd_int) | view::transform(as_string_length) , 0); Time endTimeRangev3 = std::chrono::system_clock::now(); std::chrono::duration elapsed_s_rangev3 = endTimeRangev3 - startTimeRangev3; std::cout << "(check: " << result_range_v3 << "), elapsed time range-v3: " << elapsed_s_rangev3.count() << "s\n"; } } libfplus-0.2.13/examples/readme_perf_examples.cpp000066400000000000000000000053761376322245400221310ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include #include bool is_odd_int(int x) { return x % 2 == 1; } void Test_example_KeepIf() { typedef std::vector Ints; Ints values = { 24, 11, 65, 44, 80, 18, 73, 90, 69, 18 }; { // Version 1: hand written range based for loop Ints odds; for (int x : values) if (is_odd_int(x)) odds.push_back(x); } { // Version 2: STL Ints odds; std::copy_if(std::begin(values), std::end(values), std::back_inserter(odds), is_odd_int); } { // Version : FunctionalPlus auto odds = fplus::keep_if(is_odd_int, values); } } void run_n_times(std::function(std::vector)> f, std::size_t n, const std::string& name, const std::vector& inList) { typedef std::chrono::time_point Time; Time startTime = std::chrono::system_clock::now(); std::size_t lengthSum = 0; for (std::size_t i = 0; i < n; ++i) { lengthSum += f(inList).size(); } Time endTime = std::chrono::system_clock::now(); std::chrono::duration elapsed_seconds = endTime - startTime; std::cout << name << "(check: " << lengthSum << "), elapsed time: " << elapsed_seconds.count() << "s\n"; } void Test_example_KeepIf_performance() { using namespace fplus; typedef std::vector Ints; auto run_loop = [&](const Ints values) { Ints odds; for (int x : values) if (is_odd_int(x)) odds.push_back(x); return odds; }; auto run_stl = [&](const Ints values) { Ints odds; std::copy_if(std::begin(values), std::end(values), std::back_inserter(odds), is_odd_int); return odds; }; auto run_FunctionalPlus = [&](const Ints values) { return keep_if(is_odd_int, values); }; // make debug runs faster #if defined NDEBUG || defined _DEBUG std::size_t numRuns = 10; #else std::size_t numRuns = 20000; #endif Ints values = generate(rand, 5000); run_n_times(run_loop, numRuns, "Hand-written for loop", values); run_n_times(run_stl, numRuns, "std::copy_if", values); run_n_times(run_FunctionalPlus, numRuns, "FunctionalPlus::keep_if", values); } int main() { Test_example_KeepIf_performance(); } libfplus-0.2.13/generate/000077500000000000000000000000001376322245400152175ustar00rootroot00000000000000libfplus-0.2.13/generate/generate_derived_functions.sh000077500000000000000000000023761376322245400231520ustar00rootroot00000000000000#!/usr/bin/env bash g++ -E -CC -std=c++14 -I../include ../include/fplus/fplus.hpp > temp_preprocessor_output.txt echo "// THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT EDIT." > ../include/fplus/fwd_instances.autogenerated_defines cat temp_preprocessor_output.txt | grep -B 1 -e "// fwd bind count" | perl -pe "s/^...//g" | sed ':a;N;$!ba;s/\n/ /g' | perl -pe "s/ -- /\n/g" | perl -pe "s/API search type: ([^ ]*).*fwd bind count: ([0-9])/fplus_fwd_define_fn_\2(\1)/g" >> ../include/fplus/fwd_instances.autogenerated_defines cat temp_preprocessor_output.txt | grep -B 1 -e "// fwd bind count: 1" | perl -pe "s/^...//g" | sed ':a;N;$!ba;s/\n/ /g' | perl -pe "s/ -- /\n/g" | perl -pe "s/API search type: ([^ ]*).*fwd bind count: ([0-9])/fplus_fwd_flip_define_fn_\2(\1)/g" >> ../include/fplus/fwd_instances.autogenerated_defines echo "// THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT EDIT." > ../include/fplus/curry_instances.autogenerated_defines cat temp_preprocessor_output.txt | grep -B 1 -e "// fwd bind count" | perl -pe "s/^...//g" | sed ':a;N;$!ba;s/\n/ /g' | perl -pe "s/ -- /\n/g" | perl -pe "s/API search type: ([^ ]*).*fwd bind count: ([0-9])/fplus_curry_define_fn_\2(\1)/g" >> ../include/fplus/curry_instances.autogenerated_defines rm -f temp_preprocessor_output.txt libfplus-0.2.13/include/000077500000000000000000000000001376322245400150505ustar00rootroot00000000000000libfplus-0.2.13/include/fplus/000077500000000000000000000000001376322245400162015ustar00rootroot00000000000000libfplus-0.2.13/include/fplus/benchmark_session.hpp000066400000000000000000000311621376322245400224120ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #pragma once namespace fplus { using FunctionName = std::string; struct benchmark_function_report { std::size_t nb_calls; ExecutionTime total_time; ExecutionTime average_time; ExecutionTime deviation; }; namespace internal { std::string show_benchmark_function_report( const std::map & reports); } // benchmark_session stores timings during a benchmark session // and is able to emit a report at the end class benchmark_session { public: benchmark_session() : functions_times_mutex_(), functions_times_() {}; // report() shall return a string with a summary of the session // Example below: // Function |Nb calls|Total time|Av. time|Deviation| // ----------------------+--------+----------+--------+---------+ // convert_charset_string| 4000| 4.942ms| 1.236us| 1.390us| // split_lines | 1000| 4.528ms| 4.528us| 1.896us| inline std::string report() const { const auto reports = report_list(); return fplus::internal::show_benchmark_function_report(reports); } std::map report_list() const { std::lock_guard lock(functions_times_mutex_); std::map report; for (const auto & one_function_time : functions_times_) { report[one_function_time.first] = make_bench_report(one_function_time.second); } return report; } inline void store_one_time(const FunctionName & function_name, ExecutionTime time) { std::lock_guard lock(functions_times_mutex_); functions_times_[function_name].push_back(time); } private: benchmark_function_report make_bench_report( const std::vector & times) const { benchmark_function_report result; result.nb_calls = times.size(); auto mean_and_dev = fplus::mean_stddev(times); result.average_time = mean_and_dev.first; result.deviation = mean_and_dev.second; result.total_time = fplus::sum(times); return result; } mutable std::mutex functions_times_mutex_; std::map> functions_times_; }; namespace internal { template class bench_function_impl { public: explicit bench_function_impl( benchmark_session & benchmark_sess, FunctionName function_name, Fn fn) : benchmark_session_(benchmark_sess) , function_name_(function_name) , fn_(fn) {}; template auto operator()(Args... args) { return _bench_result(args...); } private: template auto _bench_result(Args... args) { fplus::stopwatch timer; auto r = fn_(args...); benchmark_session_.store_one_time(function_name_, timer.elapsed()); return r; } benchmark_session & benchmark_session_; FunctionName function_name_; Fn fn_; }; template class bench_void_function_impl { public: explicit bench_void_function_impl( benchmark_session & benchmark_sess, FunctionName function_name, Fn fn) : benchmark_session_(benchmark_sess) , function_name_(function_name) , fn_(fn) {}; template auto operator()(Args... args) { _bench_result(args...); } private: template auto _bench_result(Args... args) { fplus::stopwatch timer; fn_(args...); benchmark_session_.store_one_time(function_name_, timer.elapsed()); } benchmark_session & benchmark_session_; FunctionName function_name_; Fn fn_; }; } // namespace internal // API search type: make_benchmark_function : (benchmark_session, string, (a... -> b)) -> (a... -> b) // Transforms a function into a function with the *same* signature // and behavior, except that it also stores stats into the benchmark session (first parameter), // under the name given by the second parameter. // - // Notes: // Side effects: make_benchmark_function *will add side effects* to the function, since it stores data // into the benchmark session at each call. // If you intend to benchmark only one function, prefer to use the simpler "make_timed_function" // Use "make_benchmark_void_function" if your function returns void // - // Example of a minimal benchmark session (read benchmark_session_test.cpp for a full example) // fplus::benchmark_session benchmark_sess; // void foo() { // auto add_bench = fplus::make_benchmark_function(benchmark_sess, "add", add); // auto printf_bench = fplus::make_benchmark_void_function(benchmark_sess, "printf", printf); // int forty_five = add_bench(20, add_bench(19, 6)); // int forty_two = benchmark_expression(benchmark_sess, "sub", forty_five - 3); // printf_bench("forty_two is %i\n", forty_two); // } // int main() { // foo(); // std::cout << benchmark_sess.report(); // } // This will output a report like this // Function|Nb calls|Total time|Av. time|Deviation| // --------+--------+----------+--------+---------+ // printf | 1| 0.010ms| 9.952us| 0.000us| // add | 2| 0.000ms| 0.050us| 0.009us| // sub | 1| 0.000ms| 0.039us| 0.000us| // - // As an alternative to make_benchmark_function, you can also benchmark an expression. // For example, in order to benchmark the following line: // auto sorted = fplus::sort(my_vector); // Just copy/paste this expression into "bench_expression" like shown below: this expression // will then be benchmarked with the name "sort_my_vector" // auto sorted = benchmark_expression( // my_benchmark_session, // "sort_my_vector", // fplus::sort(my_vector); // ); // Notes : // benchmark_expression is a preprocessor macro that uses an immediately invoked lambda (IIL). // The expression can be copy-pasted with no modification, and it is possible to not remove the ";" // (although it also works if it is not present) // You can also benchmark an expression that returns void using benchmark_void_expression template auto make_benchmark_function(benchmark_session & session, const FunctionName & name, Fn f) { // transforms f into a function with the same // signature, that will store timings into the benchmark session return internal::bench_function_impl(session, name, f); } // API search type: make_benchmark_void_function : (benchmark_session, string, (a... -> Void)) -> (a... -> Void) // Transforms a function that returns a void into a function with the *same* signature // and behavior, except that it also stores stats into the benchmark session (first parameter), // under the name given by the second parameter // Note that make_benchmark_void_function *will add side effects* to the function // (since it stores data into the benchmark session at each call) // - // Example: // benchmark_session bench_session; // ... // void foo() { // std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // } // ... // auto foo_bench = make_benchmark_void_function(bench_session, "foo", foo); // foo_bench(); // ... // std::cout << benchmark_session.report(); template auto make_benchmark_void_function(benchmark_session & session, const FunctionName & name, Fn f) { // transforms a void returning function into a function with the same // signature, that will store timings into the benchmark session return internal::bench_void_function_impl(session, name, f); } #define benchmark_expression(bench_session, name, expression) \ make_benchmark_function( \ bench_session, \ name, \ [&]() { return expression; } \ )(); #define benchmark_void_expression(bench_session, name, expression) \ make_benchmark_void_function( \ bench_session, \ name, \ [&]() { expression; } \ )(); namespace internal { inline std::string show_table(const std::vector>& rows) { if (rows.empty() || rows[0].empty()) return ""; const std::vector columns_width = [&]() { auto string_size = [](const std::string & s) -> std::size_t { return s.size(); }; auto largest_string_size = [&](const std::vector & strings) -> std::size_t { return string_size(fplus::maximum_on(string_size, strings)); }; return fplus::transform(largest_string_size, fplus::transpose(rows)); }(); auto show_one_element = [](const std::pair & elem_and_width) { const std::string & element = elem_and_width.first; const auto col_width = elem_and_width.second; bool is_number = element.size() > 0 && isdigit(element[0]); if (is_number) return fplus::show_fill_left(' ', col_width, element) + "|"; else return fplus::show_fill_right(' ', col_width, element) + "|"; }; auto show_one_separator = [](std::size_t col_width) { return fplus::show_fill_left('-', col_width, "") + "+"; }; auto show_one_row = [&](const std::vector & row) { return fplus::sum(fplus::transform( show_one_element, fplus::zip(row, columns_width))); }; auto firstrow_separator = fplus::sum(fplus::transform(show_one_separator, columns_width)); auto rows_formatted = fplus::transform(show_one_row, rows); auto rows_separated = fplus::insert_at_idx(1, firstrow_separator, rows_formatted); return fplus::join( std::string("\n"), rows_separated) + "\n"; } inline std::vector< std::pair > make_ordered_reports( const std::map & report_map) { auto report_pairs = fplus::map_to_pairs(report_map); auto report_pairs_sorted = fplus::sort_by([](const auto &a, const auto &b) { return a.second.total_time > b.second.total_time; }, report_pairs); return report_pairs_sorted; } inline std::string show_benchmark_function_report(const std::map & reports) { auto ordered_reports = make_ordered_reports(reports); auto my_show_time_ms = [](double time) -> std::string { std::stringstream ss; ss << std::fixed << std::setprecision(3); ss << (time * 1000.); return ss.str() + "ms"; }; auto my_show_time_us = [](double time) -> std::string { std::stringstream ss; ss << std::fixed << std::setprecision(3); ss << (time * 1000000.); return ss.str() + "us"; }; std::vector header_row{ { "Function", "Nb calls", "Total time", "Av. time", "Deviation" } }; auto value_rows = fplus::transform([&](const auto & kv) { const auto & report = kv.second; const auto & function_name = kv.first; std::vector row; row.push_back(function_name); row.push_back(fplus::show(report.nb_calls)); row.push_back(my_show_time_ms(report.total_time)); row.push_back(my_show_time_us(report.average_time)); row.push_back(my_show_time_us(report.deviation)); return row; }, ordered_reports); return fplus::internal::show_table(fplus::insert_at_idx(0, header_row, value_rows)); } } // namespace internal } libfplus-0.2.13/include/fplus/compare.hpp000066400000000000000000000373201376322245400203450ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include namespace fplus { namespace internal { template void check_unary_predicate_for_type() { internal::trigger_static_asserts(); static_assert(std::is_convertible< internal::invoke_result_t, bool>::value, "Predicate must return bool."); } template void check_compare_preprocessors_for_types() { internal::trigger_static_asserts(); internal::trigger_static_asserts(); static_assert(std::is_same< std::decay_t>, std::decay_t>>::value, "Both functions must return the same type."); } } // namespace internal // API search type: identity : a -> a // fwd bind count: 0 // identity(x) == x template T identity(const T& x) { return x; } // API search type: is_equal : (a, a) -> Bool // fwd bind count: 1 // x == y // Equality check. template bool is_equal(const T& x, const T& y) { return x == y; } // API search type: always : a -> (b -> a) // always(x)(y) == x template auto always(const X& x) { return [x](const auto&) { return x; }; } // API search type: always_arg_1_of_2 : (a, b) -> a // always_arg_1_of_2(x, y) == x template X always_arg_1_of_2(const X& x, const Y&) { return x; } // API search type: always_arg_2_of_2 : (a, b) -> a // always_arg_2_of_2(x, y) == x template Y always_arg_2_of_2(const X&, const Y& y) { return y; } // API search type: is_equal_by_and_by : ((a -> b), (c -> b)) -> ((a, c) -> Bool) // f(x) == g(y) // Provides an equality check of two values // after applying a transformation function each. template auto is_equal_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); return is_equal(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_equal_by : (a -> b) -> (a -> Bool) // f(x) == f(y) // Provides an equality check of two values // after applying the same transformation function to both. template auto is_equal_by(F f) { return is_equal_by_and_by(f, f); } // API search type: is_equal_by_to : ((b -> a), a) -> (b -> Bool) // f(y) == x // Provides an equality check to a fixed value // after applying a transformation function. template auto is_equal_by_to(F f, const X& x) { return [f, x](const auto& y) { internal::trigger_static_asserts(); return is_equal(internal::invoke(f, y), x); }; } // API search type: is_equal_to : a -> (a -> Bool) // x == y // curried version of is_equal // Provides an equality check with a fixed value. template auto is_equal_to(const X& x) { return is_equal_by_to(identity, x); } // API search type: is_not_equal : (a, a) -> Bool // fwd bind count: 1 // x != y // Unequally check. template bool is_not_equal(const T& x, const T& y) { return x != y; } // API search type: is_not_equal_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) != g(y) // Provides an unequality check of two values // after applying a transformation function eac template auto is_not_equal_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); using FOut = std::decay_t>; using GOut = std::decay_t>; static_assert(std::is_same::value, "Functions must return the same type."); return is_not_equal(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_not_equal_by : (a -> b) -> ((a, a) -> Bool) // f(x) != f(y) // Provides an unequality check of two values // after applying the same transformation function to both. template auto is_not_equal_by(F f) { return is_not_equal_by_and_by(f, f); } // API search type: is_not_equal_by_to : ((a -> b), b) -> (a -> Bool) // f(y) != x // Provides an unequality check to a fixed value // after applying a transformation function. template auto is_not_equal_by_to(F f, const X& x) { return [f, x](const auto& y) { internal::trigger_static_asserts(); return is_not_equal(internal::invoke(f, y), x); }; } // API search type: is_not_equal_to : a -> (a -> Bool) // y != x // curried version of is_not_equal // Provides an unequality check with a fixed value. template auto is_not_equal_to(const X& x) { return is_not_equal_by_to(identity, x); } // API search type: is_less : (a, a) -> Bool // fwd bind count: 1 // x < y // Less check. template bool is_less(const T& x, const T& y) { return x < y; } // API search type: is_less_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) < g(y) // Provides a less check of two values // after applying a transformation function each. template auto is_less_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); using FOut = std::decay_t>; using GOut = std::decay_t>; static_assert(std::is_same::value, "Functions must return the same type."); return is_less(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_less_by : (a -> b) -> ((a, a) -> Bool) // f(x) < f(y) // Provides a less check of two values // after applying the same transformation function to both. template auto is_less_by(F f) { return is_less_by_and_by(f, f); } // API search type: is_less_by_than : ((a -> b), b) -> (a -> Bool) // f(y) < x // Provides a less check to a fixed value // after applying a transformation function. template auto is_less_by_than(F f, const X& x) { return [f, x](const auto& y) { internal::trigger_static_asserts(); return is_less(internal::invoke(f, y), x); }; } // API search type: is_less_than : a -> (a -> Bool) // y < x // curried version of is_less // Provides a less check with a fixed value. template auto is_less_than(const X& x) { return is_less_by_than(identity, x); } // API search type: is_less_or_equal : (a, a) -> Bool // fwd bind count: 1 // x <= y // Less-or-equal check. template bool is_less_or_equal(const T& x, const T& y) { return x <= y; } // API search type: is_less_or_equal_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) <= g(y) // Provides a less-or-equal check of two values // after applying a transformation function each. template auto is_less_or_equal_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { using FIn = decltype(x); using GIn = decltype(y); internal::check_compare_preprocessors_for_types(); return is_less_or_equal(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_less_or_equal_by : (a -> b) -> ((a, a) -> Bool) // f(x) <= f(y) // Provides a less-or-equal check of two values // after applying the same transformation function to both. template auto is_less_or_equal_by(F f) { return is_less_or_equal_by_and_by(f, f); } // API search type: is_less_or_equal_by_than : ((a -> b), b) -> (a -> Bool) // f(y) <= x // Provides a less-or-equal check to a fixed value // after applying a transformation function. template auto is_less_or_equal_by_than(F f, const X& x) { return [f, x](const auto& y) { internal:: trigger_static_asserts(); return is_less_or_equal(internal::invoke(f, y), x); }; } // API search type: is_less_or_equal_than : a -> (a -> Bool) // y <= x // curried version of is_less_or_equal // Provides a less-or-equal check with a fixed value template auto is_less_or_equal_than(const X& x) { return is_less_or_equal_by_than(identity, x); } // API search type: is_greater : a -> a -> Bool // fwd bind count: 1 // x > y // Greater check. template bool is_greater(const T& x, const T& y) { return x > y; } // API search type: is_greater_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) > g(y) // Provides a greater check of two values // after applying a transformation function each. template auto is_greater_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { using FIn = decltype(x); using GIn = decltype(y); internal::check_compare_preprocessors_for_types(); return is_greater(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_greater_by : (a -> b) -> ((a, a) -> Bool) // f(x) > f(y) // Provides a greater check of two values // after applying the same transformation function to both. template auto is_greater_by(F f) { return is_greater_by_and_by(f, f); } // API search type: is_greater_by_than : ((a -> b), b) -> (a -> Bool) // f(y) > x // Provides a greater check to a fixed value // after applying a transformation function. template auto is_greater_by_than(F f, const X& x) { return [f, x](const auto& y) { return is_greater(internal::invoke(f, y), x); }; } // API search type: is_greater_than : a -> (a -> Bool) // y > x // curried version of is_greater // Provides a greater check with a fixed value. template auto is_greater_than(const X& x) { return is_greater_by_than(identity, x); } // API search type: is_greater_or_equal : (a, a) -> Bool // fwd bind count: 1 // x >= y // Greater-or-equal check. template bool is_greater_or_equal(const T& x, const T& y) { return x >= y; } // API search type: is_greater_or_equal_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) >= g(y) // Provides a greater-or-equal check of two values // after applying a transformation function each. template auto is_greater_or_equal_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { using FIn = decltype(x); using GIn = decltype(y); internal::check_compare_preprocessors_for_types(); return is_greater_or_equal(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_greater_or_equal_by : (a -> b) -> ((a, a) -> Bool) // f(x) >= f(y) // Provides a greater-or-equal check of two values // after applying the same transformation function to both. template auto is_greater_or_equal_by(F f) { return is_greater_or_equal_by_and_by(f, f); } // API search type: is_greater_or_equal_by_than : ((a -> b), b) -> (a -> Bool) // f(y) >= x // Provides a greater-or-equal check to a fixed value // after applying a transformation function. template auto is_greater_or_equal_by_than(F f, const X& x) { return [f, x](const auto& y) { internal::trigger_static_asserts(); return is_greater_or_equal(internal::invoke(f, y), x); }; } // API search type: is_greater_or_equal_than : a -> (a -> Bool) // y >= x // curried version of is_less_or_equal // Provides a greater-or-equal check with a fixed valu template auto is_greater_or_equal_than(const X& x) { return is_greater_or_equal_by_than(identity, x); } // API search type: xor_bools : (Bool, Bool) -> Bool // fwd bind count: 1 // Exclusive or. template bool xor_bools(const T& x, const T& y) { static_assert(std::is_convertible::value, "Type must be convertible to bool."); return (x && !y) || (!x && y); } // API search type: ord_to_eq : ((a, a) -> Bool) -> ((a, a) -> Bool) // ord_to_eq((<)) == (==) // Takes a less-than function and converts it // into an equality check function // which considers two values as equal if none are lesser than the other one. template auto ord_to_eq(Compare comp) { return [comp](auto x, auto y) { static_assert(std::is_same::value, "Argument types must be the same"); auto p = internal::ord_to_impl(comp)(x, y); return !p.first && !p.second; }; } // API search type: ord_to_not_eq : ((a, a) -> Bool) -> ((a, a) -> Bool) // ord_to_not_eq((<)) == (!=) // Takes a less-than function and converts it // into an inequality check function // which considers to values as unequal if one is less than the other one. template auto ord_to_not_eq(Compare comp) { return logical_not(ord_to_eq(comp)); } // API search type: ord_eq_to_eq : ((a, a) -> Bool) -> ((a, a) -> Bool) // ord_eq_to_eq((<=)) == (==) // ord_to_eq((<)) == (==) // Takes a less-or-equal-than function and converts it // into an equality check function // which considers to values as equal if a <= b and b <= a. template auto ord_eq_to_eq(Compare comp) { return [comp](auto x, auto y) { static_assert(std::is_same::value, "Argument types must be the same"); auto p = internal::ord_to_impl(comp)(x, y); return p.first && p.second; }; } // API search type: ord_eq_to_not_eq : ((a, a) -> Bool) -> ((a, a) -> Bool) // ord_eq_to_not_eq((<=)) == (!=) // Takes a less-or-equal-than function and converts it // into an inequality check function // which considers to values as equal if not a <= b and not b <= a. template auto ord_eq_to_not_eq(Compare comp) { return logical_not(ord_eq_to_eq(comp)); } } // namespace fplus libfplus-0.2.13/include/fplus/composition.hpp000066400000000000000000000327521376322245400212660ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace fplus { // API search type: bind_1st_of_2 : (((a, b) -> c), a) -> (b -> c) // Bind first parameter of binary function. template auto bind_1st_of_2(F f, T x) { return [f, x](auto&& y) { internal::trigger_static_asserts(); return internal::invoke(f, x, std::forward(y)); }; } // API search type: bind_2nd_of_2 : (((a, b) -> c), b) -> (a -> c) // Bind second parameter of binary function. template auto bind_2nd_of_2(F f, T y) { return [f, y](auto&& x) { internal::trigger_static_asserts(); return internal::invoke(f, std::forward(x), y); }; } // API search type: bind_1st_of_3 : (((a, b, c) -> d), a) -> ((b, c) -> d) // Bind first parameter of ternary function. template auto bind_1st_of_3(F f, X x) { return [f, x](auto&& y, auto&& z) { internal::trigger_static_asserts(); return internal::invoke( f, x, std::forward(y), std::forward(z)); }; } // API search type: bind_1st_and_2nd_of_3 : (((a, b, c) -> d), a, b) -> (c -> d) // Bind first and second parameter of ternary function. template auto bind_1st_and_2nd_of_3(F f, X x, Y y) { return [f, x, y](auto&& z) { internal::trigger_static_asserts(); return internal::invoke(f, x, y, std::forward(z)); }; } // API search type: bind_2nd_and_3rd_of_3 : (((a, b, c) -> d), b, c) -> (a -> d) // Bind first and second parameter of ternary function. template auto bind_2nd_and_3rd_of_3(F f, Y y, Z z) { return [f, y, z](auto&& x) { internal::trigger_static_asserts(); return internal::invoke(f, std::forward(x), y, z); }; } // API search type: flip : (a -> b) -> (b -> a) // Flips the arguments of a binary function // Note: The callable can take a variadic number of arguments template auto flip(F f) { return [f](auto&&... args) { return internal::apply_impl( f, std::forward_as_tuple(std::forward(args)...), internal::make_reverse_index_sequence{}); }; } // API search type: forward_apply : (a, (a -> b)) -> b // Forward application. // Returns the result of applying the function f to the value x. template auto forward_apply(X&& x, F f) { internal::trigger_static_asserts(); return internal::invoke(f, std::forward(x)); } // API search type: lazy : ((a -> b), a) -> (() -> b) // Lazy evaluation. // Returns a function evaluating f with the given arguments when called. // Also known as defer. // Note: f can take a variadic number of parameters template auto lazy(F f, Args ... args) { return [f, args...] { internal::trigger_static_asserts(); return internal::invoke(f, args...); }; } // API search type: fixed : a -> (() -> a) // Identity as a nullary function. // Returns a function returning x when called. // Like lazy with identity as f. template auto fixed(T x) { return [x]() -> T { return x; }; } // API search type: compose : ((a -> b), (b -> c)) -> (a -> c) // Forward function composition. // compose(f, g)(x) = g(f(x)) // It is possible to compose a variadic number of callables. // The first callable can also take a variadic number of parameters. // compose(f, g, h)(x, y, z) = h(g(f(x, y, z))) template auto compose(Fs&&... fs) { return internal::compose_impl(std::forward(fs)...); } // API search type: logical_not : (a -> Bool) -> (a -> Bool) // Converts a predicate p into a new one, // always returning the exact opposite of p. // logical_not(f) = \x -> !x // Note: F can take a variadic number of parameters. // Equivalent to std::not_fn (C++17) template auto logical_not(Predicate f) { return [f](auto&&... args) { internal::trigger_static_asserts(); using Res = std::decay_t>; static_assert(std::is_same::value, "Function must return bool."); return !internal::invoke(f, std::forward(args)...); }; } // API search type: logical_or : ((a -> Bool), (a -> Bool)) -> (a -> Bool) // logical_or(f, g) = \x -> f(x) or g(x) // Combines to unary predicates into a single one // that holds true if at least one of the original predicated is true. template auto logical_or(UnaryPredicateF f, UnaryPredicateG g) { auto op = [](auto f1, auto f2, auto x) { return internal::invoke(f1, x) || internal::invoke(f2, x); }; return internal::logical_binary_op(op, f, g); } // API search type: logical_and : ((a -> Bool), (a -> Bool)) -> (a -> Bool) // logical_and(f, g) = \x -> f(x) and g(x) // Combines to unary predicates into a single one // that holds true if both original predicated are true. template auto logical_and(UnaryPredicateF f, UnaryPredicateG g) { auto op = [](auto f1, auto f2, auto x) { return internal::invoke(f1, x) && internal::invoke(f2, x); }; return internal::logical_binary_op(op, f, g); } // API search type: logical_xor : ((a -> Bool), (a -> Bool)) -> (a -> Bool) // logical_xor(f, g) = \x -> f(x) xor g(x) // Combines to unary predicates into a single one // that holds true if exactly one of the original predicated is true. template auto logical_xor(UnaryPredicateF f, UnaryPredicateG g) { auto op = [](auto f1, auto f2, auto x) { return internal::invoke(f1, x) != internal::invoke(f2, x); }; return internal::logical_binary_op(op, f, g); } // API search type: memoize : (a -> b) -> (a -> b) // Provides Memoization for a given (referentially transparent) // unary function. // Returns a closure mutating an internally held dictionary // mapping input values to output values. template ::template arg<0>::type, typename FOut = typename std::result_of::type, typename MemoMap = std::unordered_map< typename std::remove_reference::type>::type, FOut>> std::function memoize(F f) { static_assert(utils::function_traits::arity == 1, "Wrong arity."); MemoMap storage; return [=](FIn x) mutable -> FOut { const auto it = storage.find(x); if (it == storage.end()) { return storage.emplace(x, internal::invoke(f, x)).first->second; } else { return it->second; } }; } namespace internal { template ::template arg<0>::type, typename FIn2 = typename utils::function_traits::template arg<1>::type, typename FOut = typename std::result_of::type, typename ResultF = std::function> ResultF memoize_recursive_helper(const F f, std::shared_ptr storage) { return [f, storage](FIn2 x) { const auto it = storage->find(x); if (it == storage->end()) { const auto g = memoize_recursive_helper(f, storage); (*storage)[x] = f(g, x); } return (*storage)[x]; }; } } // namespace internal // API search type: memoize_recursive : (a -> b) -> (a -> b) // Provides Memoization for a given (referentially transparent) // recursive binary function that takes a continuation as first argument. // e.g. // uint64_t fibo_cont(const std::function& cont, uint64_t n) // { // if (n < 2) return n; // else return cont(n-1) + cont(n-2); // } // Returns a closure mutating an internally held dictionary // mapping input values to output values. template ::template arg<0>::type, typename FIn2 = typename utils::function_traits::template arg<1>::type, typename FOut = typename std::result_of::type, typename MemoMap = std::unordered_map< typename std::remove_reference::type>::type, FOut>> std::function memoize_recursive(F f) { std::shared_ptr storage = std::make_shared(); return internal::memoize_recursive_helper(f, storage); } // API search type: memoize_binary : ((a, b) -> c) -> ((a, b) -> c) // Provides Memoization for a given (referentially transparent) // binary function. // Returns a closure mutating an internally held dictionary // mapping input values to output values. template ::template arg<0>::type, typename FIn2 = typename utils::function_traits::template arg<1>::type, typename FOut = typename std::result_of::type, typename ParamPair = std::pair< typename std::remove_reference::type>::type, typename std::remove_reference::type>::type>, typename MemoMap = std::unordered_map> std::function memoize_binary(F f) { const auto unary_f = [f](const ParamPair& params) -> FOut { return internal::invoke(f, params.first, params.second); }; auto unary_f_memoized = memoize>(unary_f); return [unary_f_memoized](FIn1 a, FIn2 b) mutable -> FOut { return unary_f_memoized(std::make_pair(a, b)); }; } // API search type: constructor_as_function : a -> b // struct foo // { // foo(int a, int b) : a_(a), b_(2*b) {} // int a_; // int b_; // }; // const auto create_foo = constructor_as_function; // create_foo(1,2) == foo(1, 2); template T constructor_as_function(Types ... args) { return T(args...); } } // namespace fplus #define fplus_get_mem(fplus_get_mem_name) \ [](const auto& fplus_get_mem_x) \ { \ return fplus_get_mem_x.fplus_get_mem_name; \ } #define fplus_get_ptr_mem(fplus_get_ptr_mem_name) \ [](const auto& fplus_get_ptr_mem_x) \ { \ return fplus_get_ptr_mem_x->fplus_get_ptr_mem_name; \ } #define fplus_get_c_mem_t(fplus_get_c_mem_t_c, fplus_get_c_mem_t_name, fplus_get_c_mem_t_t) \ [](const fplus_get_c_mem_t_c& fplus_get_c_mem_t_x) -> fplus_get_c_mem_t_t \ { \ return fplus_get_c_mem_t_x.fplus_get_c_mem_t_name; \ } #define fplus_get_c_ptr_mem_t(fplus_get_c_ptr_mem_t_c, fplus_get_c_ptr_mem_t_name, fplus_get_c_ptr_mem_t_t) \ [](const fplus_get_c_ptr_mem_t_c& fplus_get_c_ptr_mem_t_x) -> fplus_get_c_ptr_mem_t_t \ { \ return fplus_get_c_ptr_mem_t_x->fplus_get_c_ptr_mem_t_name; \ } #define fplus_mem_fn(fplus_mem_fn_name) \ [](const auto& fplus_mem_fn_x) \ { \ return fplus_mem_fn_x.fplus_mem_fn_name(); \ } #define fplus_ptr_mem_fn(fplus_ptr_mem_fn_name) \ [](const auto& fplus_ptr_mem_fn_x) \ { \ return fplus_ptr_mem_fn_x->fplus_ptr_mem_fn_name(); \ } #define fplus_c_mem_fn_t(fplus_c_mem_fn_t_c, fplus_c_mem_fn_t_name, fplus_c_mem_fn_t_t) \ [](const fplus_c_mem_fn_t_c& fplus_c_mem_fn_t_x) -> fplus_c_mem_fn_t_t \ { \ return fplus_c_mem_fn_t_x.fplus_c_mem_fn_t_name(); \ } #define fplus_c_ptr_mem_fn_t(fplus_c_ptr_mem_fn_t_c, fplus_c_ptr_mem_fn_t_name, fplus_c_ptr_mem_fn_t_t) \ [](const fplus_c_ptr_mem_fn_t_c& fplus_c_ptr_mem_fn_t_x) -> fplus_c_ptr_mem_fn_t_t \ { \ return fplus_c_ptr_mem_fn_t_x->fplus_c_ptr_mem_fn_t_name(); \ } libfplus-0.2.13/include/fplus/container_common.hpp000066400000000000000000002153561376322245400222600ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fplus { namespace internal { template void check_unary_predicate_for_container() { internal::check_unary_predicate_for_type(); } template void check_index_with_type_predicate_for_container() { typedef typename Container::value_type T; internal::trigger_static_asserts(); static_assert(std::is_convertible< internal::invoke_result_t, bool>::value, "Function must return bool."); } template void check_compare_for_container() { typedef typename Container::value_type T; internal::trigger_static_asserts(); } template void check_binary_predicate_for_container() { typedef typename Container::value_type T; internal::trigger_static_asserts(); } // PrepareContainer and BackInserter are overloaded // to increase performance on std::vector and std::string // by using std::vector::reserve // and std::back_inserter instead of std::back_inserter. // In VC2015, release mode, Celsius W520 Xeon // this leads to an increase in performance of about a factor of 3 // for transform. template void prepare_container(const std::basic_string, std::allocator>& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::vector& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::array&, std::size_t size) { assert(size == N); unused(size); } template void prepare_container(std::unordered_set& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::unordered_map& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::unordered_multiset& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::unordered_multimap& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(Container&, std::size_t) { } template std::back_insert_iterator get_back_inserter(std::string& ys) { return std::back_inserter(ys); } template std::back_insert_iterator get_back_inserter(std::vector& ys) { return std::back_inserter(ys); } template std::back_insert_iterator get_back_inserter(std::list& ys) { return std::back_inserter(ys); } template std::back_insert_iterator get_back_inserter(std::deque& ys) { return std::back_inserter(ys); } template struct array_back_insert_iterator : public std::back_insert_iterator> { typedef std::back_insert_iterator> base_type; explicit array_back_insert_iterator(std::array& arr) : base_type(arr), arr_ptr_(&arr), pos_(0) {} array_back_insert_iterator(const array_back_insert_iterator& other) : base_type(*other.arr_ptr_), arr_ptr_(other.arr_ptr_), pos_(other.pos_) {} array_back_insert_iterator& operator=(const array_back_insert_iterator& other) { arr_ptr_ = other.arr_ptr_; pos_ = other.pos_; return *this; } ~array_back_insert_iterator() { assert(pos_ == 0 || pos_ == N); } array_back_insert_iterator& operator=(const T& x) { assert(pos_ < N); (*arr_ptr_)[pos_] = x; ++pos_; return *this; } array_back_insert_iterator& operator=(T&& x) { assert(pos_ < N); (*arr_ptr_)[pos_] = std::move(x); ++pos_; return *this; } array_back_insert_iterator& operator*() { return *this; } array_back_insert_iterator& operator++() { return *this; } array_back_insert_iterator operator++(int) { return *this; } private: std::array* arr_ptr_; std::size_t pos_; }; #if defined(_MSC_VER) && _MSC_VER >= 1900 template struct std::_Is_checked_helper> : public true_type { // mark array_back_insert_iterator as checked }; #endif template array_back_insert_iterator get_back_inserter(std::array& ys) { return array_back_insert_iterator(ys); } template std::insert_iterator get_back_inserter(Container& ys) { return std::inserter(ys, std::end(ys)); } template void advance_iterator(Iterator& it, std::size_t distance) { std::advance(it, static_cast(distance)); } template void advance_iterator(T*& it, std::size_t distance) { it += static_cast(distance); } template Iterator add_to_iterator(Iterator it, std::size_t distance = 1) { return std::next(it, static_cast(distance)); } } // namespace internal // API search type: is_empty : [a] -> Bool // fwd bind count: 0 // Returns true if the container holds no elements. // is_empty([1, 2]) == false // is_empty([]) == true template bool is_empty(const Container& xs) { return xs.empty(); } // API search type: is_not_empty : [a] -> Bool // fwd bind count: 0 // Returns true if the container holds at least one element. // is_not_empty([1, 2]) == true template bool is_not_empty(const Container& xs) { return !is_empty(xs); } // API search type: size_of_cont : [a] -> Int // fwd bind count: 0 // Returns the number of elements in the given container. // size_of_cont([3, 4]) == 2 template std::size_t size_of_cont(const Container& xs) { return xs.size(); } // API search type: convert : a -> b // fwd bind count: 0 // Converts one type of element into another. template Dest convert(const Source& x) { return Dest(x); } // API search type: convert_elems : [a] -> [b] // fwd bind count: 0 // Converts all elements in a sequence to a different type. // convert_elems([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)] template ::type> ContainerOut convert_elems(const ContainerIn& xs) { static_assert(std::is_constructible::value, "Elements not convertible."); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); // using 'for (const auto& x ...)' is even for ints as fast as // using 'for (int x ...)' (GCC, O3), so there is no need to // check if the type is fundamental and then dispatch accordingly. for (const auto& x : xs) { *it = convert(x); } return ys; } // API search type: convert_container : [a] -> [a] // fwd bind count: 0 // Change the type of the container // while keeping the elements in the sequence the same. // convert_container([1, 2, 3]) == [1, 2, 3] // Useful for example if you want to convert an std::list to an std::vector. template ContainerOut convert_container(const ContainerIn& xs) { typedef typename ContainerIn::value_type SourceElem; typedef typename ContainerOut::value_type DestElem; static_assert(std::is_same::value, "Source and dest container must have the same value_type"); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto itOut = internal::get_back_inserter(ys); std::copy(std::begin(xs), std::end(xs), itOut); return ys; } // API search type: convert_container_and_elems : [a] -> [b] // fwd bind count: 0 // Converts between different containers and elements. // Dest elements are allowed to have explicit constructors. // convert([1, 2, 3]) == [1, 2, 3] template ContainerOut convert_container_and_elems(const ContainerIn& xs) { static_assert(std::is_convertible::value, "Elements not convertible."); typedef typename ContainerOut::value_type DestElem; ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); for (const auto& x : xs) { *it = convert(x); } return ys; } namespace internal { template Container get_segment(internal::reuse_container_t, std::size_t idx_begin, std::size_t idx_end, Container&& xs) { idx_end = std::min(idx_end, size_of_cont(xs)); if (idx_end <= idx_begin) { xs.clear(); return std::forward(xs); } auto itBegin = std::begin(xs); internal::advance_iterator(itBegin, idx_begin); auto itEnd = itBegin; internal::advance_iterator(itEnd, idx_end - idx_begin); xs.erase(std::copy(itBegin, itEnd, std::begin(xs)), std::end(xs)); return std::forward(xs); } template Container get_segment(internal::create_new_container_t, std::size_t idx_begin, std::size_t idx_end, const Container& xs) { idx_end = std::min(idx_end, size_of_cont(xs)); if (idx_end <= idx_begin) { return {}; } Container result; auto itBegin = std::begin(xs); internal::advance_iterator(itBegin, idx_begin); auto itEnd = itBegin; internal::advance_iterator(itEnd, idx_end - idx_begin); std::copy(itBegin, itEnd, internal::get_back_inserter(result)); return result; } } // namespace internal // API search type: get_segment : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Return a defined segment from the sequence. // get_segment(2, 5, [0,1,2,3,4,5,6,7,8]) == [2,3,4] // get_segment(2, 15, [0,1,2,3,4,5,6,7,8]) == [2,3,4,5,6,7,8] // get_segment(5, 2, [0,1,2,3,4,5,6,7,8]) == [] // Also known as slice. template > ContainerOut get_segment (std::size_t idx_begin, std::size_t idx_end, Container&& xs) { return internal::get_segment(internal::can_reuse_v{}, idx_begin, idx_end, std::forward(xs)); } namespace internal { template Container set_segment(internal::reuse_container_t, std::size_t idx_begin, const ContainerToken& token, Container&& xs) { assert(idx_begin + size_of_cont(token) < size_of_cont(xs)); auto itBegin = std::begin(xs); internal::advance_iterator(itBegin, idx_begin); std::copy(std::begin(token), std::end(token), itBegin); return std::forward(xs); } template Container set_segment(internal::create_new_container_t, std::size_t idx_begin, const ContainerToken& token, const Container& xs) { Container result = xs; return set_segment(internal::reuse_container_t(), idx_begin, token, std::move(result)); } } // namespace internal // API search type: set_segment : (Int, [a], [a]) -> [a] // fwd bind count: 2 // Replace part of a sequence with a token. // set_segment(2, [9,9,9], [0,1,2,3,4,5,6,7,8]) == [0,1,9,9,9,5,6,7,8] // Crashes on invalid indices. // Also known as replace_segment. template > ContainerOut set_segment (std::size_t idx_begin, const ContainerToken& token, Container&& xs) { return internal::set_segment(internal::can_reuse_v{}, idx_begin, token, std::forward(xs)); } namespace internal { template Container remove_segment(internal::reuse_container_t, std::size_t idx_begin, std::size_t idx_end, Container&& xs) { assert(idx_begin <= idx_end); assert(idx_end <= size_of_cont(xs)); auto firstBreakIt = std::begin(xs); internal::advance_iterator(firstBreakIt, idx_begin); auto secondBreakIt = std::begin(xs); internal::advance_iterator(secondBreakIt, idx_end); xs.erase( std::copy(secondBreakIt, std::end(xs), firstBreakIt), std::end(xs)); return std::forward(xs); } template Container remove_segment(internal::create_new_container_t, std::size_t idx_begin, std::size_t idx_end, const Container& xs) { assert(idx_begin <= idx_end); assert(idx_end <= size_of_cont(xs)); Container result; std::size_t length = idx_end - idx_begin; internal::prepare_container(result, size_of_cont(xs) - length); auto firstBreakIt = std::begin(xs); internal::advance_iterator(firstBreakIt, idx_begin); std::copy(std::begin(xs), firstBreakIt, internal::get_back_inserter(result)); auto secondBreakIt = std::begin(xs); internal::advance_iterator(secondBreakIt, idx_end); std::copy(secondBreakIt, std::end(xs), internal::get_back_inserter(result)); return result; } } // namespace internal // API search type: remove_segment : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Cuts our a defined segment from the sequence. // remove_segment(2, 5, [0,1,2,3,4,5,6,7]) == [0,1,5,6,7] // crashes on invalid indices template > ContainerOut remove_segment( std::size_t idx_begin, std::size_t idx_end, Container&& xs) { return internal::remove_segment(internal::can_reuse_v{}, idx_begin, idx_end, std::forward(xs)); } // API search type: insert_at : (Int, [a], [a]) -> [a] // fwd bind count: 2 // Inserts a token into a sequence at a specific position. // insert_at(2, [8,9], [0,1,2,3,4]) == [0,1,8,9,2,3,4] // Unsafe! Crashes on invalid index. template Container insert_at(std::size_t idx_begin, const Container& token, const Container& xs) { assert(idx_begin <= size_of_cont(xs)); Container result; internal::prepare_container(result, size_of_cont(xs) + size_of_cont(token)); auto breakIt = std::begin(xs); internal::advance_iterator(breakIt, idx_begin); std::copy(std::begin(xs), breakIt, internal::get_back_inserter(result)); std::copy(std::begin(token), std::end(token), internal::get_back_inserter(result)); std::copy(breakIt, std::end(xs), internal::get_back_inserter(result)); return result; } // API search type: elem_at_idx : (Int, [a]) -> a // fwd bind count: 1 // Return the nth element of a sequence. // elem_at_idx(2, [7,6,5,4,3]) == 5 // Unsafe! Crashes on invalid index. template auto elem_at_idx(std::size_t idx, const Container& xs) { assert(idx < size_of_cont(xs)); auto it = std::begin(xs); internal::advance_iterator(it, idx); return *it; } // API search type: elem_at_idx_maybe : (Int, [a]) -> Maybe a // fwd bind count: 1 // Return the nth element of a sequence if existing. // elem_at_idx_maybe(2, [7,6,5,4,3]) == Just 5 // elem_at_idx_maybe(9, [7,6,5,4,3]) == Nothing // Use elem_at_idx_or_nothing if you want to provide a signed index type. template maybe elem_at_idx_maybe(std::size_t idx, const Container& xs) { if (size_of_cont(xs) < idx) { return {}; } auto it = std::begin(xs); internal::advance_iterator(it, idx); return *it; } // API search type: elems_at_idxs : ([Int], [a]) -> [a] // fwd bind count: 1 // Construct a subsequence from the elements with the given indices. // elem_at_idxs([1, 3], [7,6,5,4,3]) == [6, 4] template > std::vector elems_at_idxs(const ContainerIdxs& idxs, const Container& xs) { static_assert(std::is_same::value, "Indices must be std::size_t"); ContainerOut result; internal::prepare_container(result, size_of_cont(idxs)); auto itOut = internal::get_back_inserter(result); for (std::size_t idx : idxs) { *itOut = elem_at_idx(idx, xs); } return result; } namespace internal { template Container transform(internal::reuse_container_t, F f, Container&& xs) { internal::trigger_static_asserts(); std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); return std::forward(xs); } template ContainerOut transform(internal::create_new_container_t, F f, const ContainerIn& xs) { internal::trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); std::transform(std::begin(xs), std::end(xs), it, f); return ys; } } // namespace internal // API search type: transform : ((a -> b), [a]) -> [b] // fwd bind count: 1 // Apply a function to every element in a sequence. // transform((*2), [1, 3, 4]) == [2, 6, 8] // Also known as map or fmap. template , F, 0>::type> ContainerOut transform(F f, ContainerIn&& xs) { using reuse_t = typename std::conditional< std::is_same< internal::can_reuse_v, internal::reuse_container_t>::value && std::is_base_of< std::true_type, internal::has_order>::value && std::is_same< internal::remove_const_and_ref_t, ContainerOut>::value, internal::reuse_container_t, internal::create_new_container_t>::type; return internal::transform( reuse_t{}, f, std::forward(xs)); } // API search type: transform_convert : ((a -> b), [a]) -> [b] // fwd bind count: 1 // transform_convert((*2), [1, 3, 4]) == [2, 6, 8] // Same as transform, but makes it easy to // use an output container type different from the input container type. template ContainerOut transform_convert(F f, const ContainerIn& xs) { internal::trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); std::transform(std::begin(xs), std::end(xs), it, f); return ys; } // API search type: transform_inner : ((a -> b), [[a]]) -> [[b]] // fwd bind count: 1 // Applies a function to the elements of the inner containers // of a nested sequence. // transform_inner((*2), [[1, 3, 4], [1, 2]]) == [[2, 6, 8], [2, 4]] // Also known as transform_nested, map_nested or map_inner. template ::type, 0 >::type> ContainerOut transform_inner(F f, const ContainerIn& xs) { internal::trigger_static_asserts(); return fplus::transform( fplus::bind_1st_of_2( fplus::transform, f), xs); } namespace internal { template Container reverse(internal::reuse_container_t, Container&& xs) { static_assert(internal::has_order::value, "Reverse: Container has no order."); std::reverse(std::begin(xs), std::end(xs)); return std::forward(xs); } template Container reverse(internal::create_new_container_t, const Container& xs) { static_assert(internal::has_order::value, "Reverse: Container has no order."); Container ys = xs; std::reverse(std::begin(ys), std::end(ys)); return ys; } } // namespace internal // API search type: reverse : [a] -> [a] // fwd bind count: 0 // Reverse a sequence. // reverse([0,4,2,6]) == [6,2,4,0] template > ContainerOut reverse(Container&& xs) { return internal::reverse(internal::can_reuse_v{}, std::forward(xs)); } // API search type: take : (Int, [a]) -> [a] // fwd bind count: 1 // Return the first n elements of a sequence xs. // In case n >= length(xs), xs is returned. // take(3, [0,1,2,3,4,5,6,7]) == [0,1,2] // take(10, [0,1,2]) == [0,1,2] template > ContainerOut take(std::size_t amount, Container&& xs) { if (amount >= size_of_cont(xs)) return xs; return get_segment(0, amount, std::forward(xs)); } // API search type: take_exact : (Int, [a]) -> [a] // fwd bind count: 1 // Return exactly the first n elements of a sequence xs. // Unsafe! Crashes then sequence is too short. // take_exact(3, [0,1,2,3,4,5,6,7]) == [0,1,2] // take_exact(10, [0,1,2]) == crash template > ContainerOut take_exact(std::size_t amount, Container&& xs) { return get_segment(0, amount, std::forward(xs)); } // API search type: take_cyclic : (Int, [a]) -> [a] // fwd bind count: 1 // Takes n elements from a sequence considering it as cyclic. // take_cyclic(5, [0,1,2,3]) == [0,1,2,3,0] // take_cyclic(7, [0,1,2,3]) == [0,1,2,3,0,1,2] // take_cyclic(7, [0,1]) == [0,1,0,1,0,1,0] // take_cyclic(2, [0,1,2,3]) == [0,1] // take_cyclic(3, [0]) == [0,0,0] // take_cyclic(3, []) == crash! // Also known as take_wrap. // xs must be non-empty. template Container take_cyclic(std::size_t amount, const Container& xs) { assert(!xs.empty()); Container ys; internal::prepare_container(ys, size_of_cont(xs)); auto it_out = internal::get_back_inserter(ys); auto it_in = std::begin(xs); while (amount != 0) { *it_out = *it_in; --amount; ++it_in; if (it_in == std::end(xs)) { it_in = std::begin(xs); } } return ys; } // API search type: drop : (Int, [a]) -> [a] // fwd bind count: 1 // Skip the first n elements of a sequence xs. // If n > length(xs) an empty sequence is returned. // drop(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] // Also known as skip. template > ContainerOut drop(std::size_t amount, Container&& xs) { if (amount >= size_of_cont(xs)) return ContainerOut(); return get_segment(amount, size_of_cont(xs), std::forward(xs)); } // API search type: take_last : (Int, [a]) -> [a] // fwd bind count: 1 // Return the last n elements of a sequence xs. // In case n >= length(xs), xs is returned. // take_last(3, [0,1,2,3,4,5,6,7]) == [5,6,7] // take_last(10, [0,1,2]) == [0,1,2] template > ContainerOut take_last(std::size_t amount, Container&& xs) { if (amount >= size_of_cont(xs)) return xs; return drop(size_of_cont(xs) - amount, std::forward(xs)); } // API search type: drop_last : (Int, [a]) -> [a] // fwd bind count: 1 // Skip the last n elements of a sequence xs. // If n > length(xs) an empty sequence is returned. // drop_last(3, [0,1,2,3,4,5,6,7]) == [0,1,2,3,4] template > ContainerOut drop_last(std::size_t amount, Container&& xs) { if (amount >= size_of_cont(xs)) return ContainerOut(); return take(size_of_cont(xs) - amount, std::forward(xs)); } // API search type: drop_exact : (Int, [a]) -> [a] // fwd bind count: 1 // Skip exactly the first n elements of a sequence xs. // Unsafe! Crashes when xs is too short. // drop_exact(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] // drop_exact(10, [0,1,2,3,4,5,6,7]) == crash template > ContainerOut drop_exact(std::size_t amount, Container&& xs) { return get_segment(amount, size_of_cont(xs), std::forward(xs)); } // API search type: take_while : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Take elements from the beginning of a sequence // as long as they are fulfilling a predicate. // take_while(is_even, [0,2,4,5,6,7,8]) == [0,2,4] template Container take_while(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto itFirst = std::find_if( std::begin(xs), std::end(xs), logical_not(pred)); if (itFirst == std::end(xs)) return xs; return Container(std::begin(xs), itFirst); } // API search type: drop_while : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Remove elements from the beginning of a sequence // as long as they are fulfilling a predicate. // drop_while(is_even, [0,2,4,5,6,7,8]) == [5,6,7,8] // Also known as trim_left_by. template Container drop_while(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto itFirstNot = std::find_if_not(std::begin(xs), std::end(xs), pred); if (itFirstNot == std::end(xs)) return Container(); return Container(itFirstNot, std::end(xs)); } // API search type: fold_left : (((a, b) -> a), a, [b]) -> a // fwd bind count: 2 // fold_left((+), 0, [1, 2, 3]) == ((0+1)+2)+3 == 6 // Takes the second argument and the first item of the list // and applies the function to them, // then feeds the function with this result and the second argument and so on. template Acc fold_left(F f, const Acc& init, const Container& xs) { using std::begin; using std::end; return internal::accumulate(begin(xs), end(xs), init, f); } // API search type: reduce : (((a, a) -> a), a, [a]) -> a // fwd bind count: 2 // reduce((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 // Combines the initial value and all elements of the sequence // using the given function. // The set of f, init and value_type should form a monoid. template typename Container::value_type reduce( F f, const typename Container::value_type& init, const Container& xs) { return fold_left(f, init, xs); } // API search type: fold_left_1 : (((a, a) -> a), [a]) -> a // fwd bind count: 1 // fold_left_1((+), [1, 2, 3]) == (1+2)+3 == 6 // Takes the first 2 items of the list and applies the function to them, // then feeds the function with this result and the third argument and so on. // xs must be non-empty. template auto fold_left_1(F f, const Container& xs) { assert(!xs.empty()); using std::begin; using std::end; const auto it = begin(xs); return internal::accumulate(std::next(it), end(xs), *it, f); } // API search type: reduce_1 : (((a, a) -> a), [a]) -> a // fwd bind count: 1 // reduce_1((+), [1, 2, 3]) == (1+2+3) == 6 // Joins all elements of the sequence using the given function. // The set of f and value_type should form a semigroup. template typename Container::value_type reduce_1(F f, const Container& xs) { assert(!xs.empty()); return fold_left_1(f, xs); } // API search type: fold_right : (((a, b) -> b), b) -> [a] -> b // fwd bind count: 2 // fold_right((+), 0, [1, 2, 3]) == 1+(2+(3+0)) == 6 // Takes the second argument and the last item of the list // and applies the function, // then it takes the penultimate item from the end and the result, and so on. template Acc fold_right(F f, const Acc& init, const Container& xs) { return internal::accumulate(xs.rbegin(), xs.rend(), init, flip(f)); } // API search type: fold_right_1 : (((a, a) -> a), [a]) -> a // fwd bind count: 1 // fold_right_1((+), [1, 2, 3]) == 1+(2+3)) == 6 // Takes the last two items of the list and applies the function, // then it takes the third item from the end and the result, and so on. template auto fold_right_1(F f, const Container& xs) { assert(!xs.empty()); const auto it = xs.rbegin(); return internal::accumulate(std::next(it), xs.rend(), *it, flip(f)); } // API search type: scan_left : (((a, b) -> a), a, [b]) -> [a] // fwd bind count: 2 // scan_left((+), 0, [1, 2, 3]) == [0, 1, 3, 6] // Takes the second argument and the first item of the list // and applies the function to them, // then feeds the function with this result and the second argument and so on. // It returns the list of intermediate and final results. template auto scan_left(F f, const Acc& init, const ContainerIn& xs) { using ContainerOut = typename internal::same_cont_new_t::type; ContainerOut result; internal::prepare_container(result, size_of_cont(xs) + 1); using std::begin; using std::end; internal::scan_impl( f, init, internal::get_back_inserter(result), begin(xs), end(xs)); return result; } // API search type: scan_left_1 : (((a, a) -> a), [a]) -> [a] // fwd bind count: 1 // scan_left_1((+), [1, 2, 3]) == [1, 3, 6] // Takes the first 2 items of the list and applies the function to them, // then feeds the function with this result and the third argument and so on. // It returns the list of intermediate and final results. // xs must be non-empty. template auto scan_left_1(F f, const ContainerIn& xs) { assert(!xs.empty()); using std::begin; using std::end; const auto beginIt = begin(xs); using ContainerOut = typename internal::same_cont_new_t< ContainerIn, internal::uncvref_t, 0>::type; ContainerOut result; internal::prepare_container(result, size_of_cont(xs)); internal::scan_impl(f, *beginIt, internal::get_back_inserter(result), std::next(beginIt), end(xs)); return result; } // API search type: scan_right : (((a, b) -> b), b, [a]) -> [b] // fwd bind count: 2 // scan_right((+), 0, [1, 2, 3]) == [6, 5, 3, 0] // Takes the second argument and the last item of the list // and applies the function, // then it takes the penultimate item from the end and the result, and so on. // It returns the list of intermediate and final results. template ::template arg<1>::type, typename ContainerOut = typename internal::same_cont_new_t::type> ContainerOut scan_right(F f, const Acc& init, const ContainerIn& xs) { return reverse(scan_left(flip(f), init, reverse(xs))); } // API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a] // fwd bind count: 1 // scan_right_1((+), [1, 2, 3]) == [6, 5, 3] // Takes the last two items of the list and applies the function, // then it takes the third item from the end and the result, and so on. // It returns the list of inntermediate and final results. template ::type> ContainerOut scan_right_1(F f, const ContainerIn& xs) { return reverse(scan_left_1(flip(f), reverse(xs))); } // API search type: sum : [a] -> a // fwd bind count: 0 // Adds up all values in a sequence. // sum([0,3,1]) == 4 // sum([]) == 0 template T sum(const Container& xs) { T result = T(); for (const auto& x : xs) { result = result + x; } return result; } // API search type: product : [a] -> a // fwd bind count: 0 // Returns the product of all values in a sequence. // product([3,1,2]) == 6 // product([]) == 1 template T product(const Container& xs) { T result{1}; for (const auto& x : xs) { result = result * x; } return result; } namespace internal { template Container append_elem(internal::reuse_container_t, const T& y, Container&& xs) { *internal::get_back_inserter(xs) = y; return std::forward(xs); } template Container append_elem(internal::create_new_container_t, const T& y, const Container& xs) { Container result; internal::prepare_container(result, size_of_cont(xs) + 1); std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); *internal::get_back_inserter(result) = y; return result; } } // namespace internal // API search type: append_elem : (a, [a]) -> [a] // fwd bind count: 1 // Extends a sequence with one element at the back. // append_elem([1, 2], 3) == [1, 2, 3] template , typename T = typename ContainerOut::value_type> ContainerOut append_elem(const T& y, Container&& xs) { return internal::append_elem(internal::can_reuse_v{}, y, std::forward(xs)); } namespace internal { template std::list prepend_elem(internal::reuse_container_t, const T& y, std::list&& xs) { xs.push_front(y); return std::forward>(xs); } template Container prepend_elem(internal::reuse_container_t, const T& y, Container&& xs) { xs.resize(size_of_cont(xs) + 1); std::copy(++xs.rbegin(), xs.rend(), xs.rbegin()); *std::begin(xs) = y; return std::forward(xs); } template Container prepend_elem(internal::create_new_container_t, const T& y, const Container& xs) { Container result; internal::prepare_container(result, size_of_cont(xs) + 1); *internal::get_back_inserter(result) = y; std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); return result; } } // namespace internal // API search type: prepend_elem : (a, [a]) -> [a] // fwd bind count: 1 // Extends a sequence with one element in the front. // prepend_elem([2, 3], 1) == [1, 2, 3] template , typename T = typename ContainerOut::value_type> ContainerOut prepend_elem(const T& y, Container&& xs) { return internal::prepend_elem(internal::can_reuse_v{}, y, std::forward(xs)); } // API search type: append : ([a], [a]) -> [a] // fwd bind count: 1 // Concatenates two sequences. // append([1, 2], [3, 4, 5]) == [1, 2, 3, 4, 5] template ContainerOut append(const ContainerIn1& xs, const ContainerIn2& ys) { ContainerOut result; internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); std::copy(std::begin(ys), std::end(ys), internal::get_back_inserter(result)); return result; } // API search type: append_convert : ([a], [a]) -> [a] // fwd bind count: 1 // Same as append, but makes it easier to // use an output container type different from the input container type. template ContainerOut append_convert(const ContainerIn1& xs, const ContainerIn2& ys) { return append(xs, ys); } // API search type: concat : [[a]] -> [a] // fwd bind count: 0 // Concatenates multiple sequences. // concat([[1, 2], [], [3]]) == [1, 2, 3] // Also known as flatten. template ContainerOut concat(const ContainerIn& xss) { std::size_t length = sum( transform(size_of_cont, xss)); ContainerOut result; internal::prepare_container(result, length); using std::begin; using std::end; for(const auto& xs : xss) { result.insert(end(result), begin(xs), end(xs)); } return result; } // API search type: interweave : ([a], [a]) -> [a] // fwd bind count: 1 // Return a sequence that contains elements from the two provided sequences // in alternating order. If one list runs out of items, // appends the items from the remaining list. // interweave([1,3], [2,4]) == [1,2,3,4] // interweave([1,3,5,7], [2,4]) == [1,2,3,4,5,7] // See interleave for interweaving more than two sequences. template Container interweave(const Container& xs, const Container& ys) { Container result; internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); auto it = internal::get_back_inserter(result); auto it_xs = std::begin(xs); auto it_ys = std::begin(ys); while (it_xs != std::end(xs) || it_ys != std::end(ys)) { if (it_xs != std::end(xs)) *it = *(it_xs++); if (it_ys != std::end(ys)) *it = *(it_ys++); } return result; } // API search type: unweave : [a] -> ([a], [a]) // fwd bind count: 0 // Puts the elements with an even index into the first list, // and the elements with an odd index into the second list. // Inverse of interweave. // unweave([0,1,2,3]) == ([0,2], [1,3]) // unweave([0,1,2,3,4]) == ([0,2,4], [1,3]) template std::pair unweave(const Container& xs) { std::pair result; if (size_of_cont(xs) % 2 == 0) internal::prepare_container(result.first, size_of_cont(xs) / 2); else internal::prepare_container(result.first, size_of_cont(xs) / 2 + 1); internal::prepare_container(result.second, size_of_cont(xs) / 2); auto it_even = internal::get_back_inserter(result.first); auto it_odd = internal::get_back_inserter(result.second); std::size_t counter = 0; for (const auto& x : xs) { if (counter++ % 2 == 0) *it_even = x; else *it_odd = x; } return result; } namespace internal { template std::list sort_by(internal::reuse_container_t, Compare comp, std::list&& xs) { xs.sort(comp); return std::forward>(xs); } template std::list sort_by(internal::create_new_container_t, Compare comp, const std::list& xs) { auto result = xs; result.sort(comp); return result; } template Container sort_by(internal::reuse_container_t, Compare comp, Container&& xs) { std::sort(std::begin(xs), std::end(xs), comp); return std::forward(xs); } template Container sort_by(internal::create_new_container_t, Compare comp, const Container& xs) { auto result = xs; std::sort(std::begin(result), std::end(result), comp); return result; } } // namespace internal // API search type: sort_by : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Sort a sequence by given less comparator. template > ContainerOut sort_by(Compare comp, Container&& xs) { return internal::sort_by(internal::can_reuse_v{}, comp, std::forward(xs)); } namespace internal { // workarounds for clang bug 24115 // (std::sort and std::unique with std::function as comp) // https://llvm.org/bugs/show_bug.cgi?id=24115 template struct is_less_by_struct { is_less_by_struct(F f) : f_(f) {}; template bool operator()(const T& x, const T& y) { return f_(x) < f_(y); } private: F f_; }; template struct is_equal_by_struct { is_equal_by_struct(F f) : f_(f) {}; template bool operator()(const T& x, const T& y) { return f_(x) == f_(y); } private: F f_; }; } // API search type: sort_on : ((a -> b), [a]) -> [a] // fwd bind count: 1 // Sort a sequence by a given transformer. template > ContainerOut sort_on(F f, Container&& xs) { return sort_by(internal::is_less_by_struct(f), std::forward(xs)); } // API search type: sort : [a] -> [a] // fwd bind count: 0 // Sort a sequence to ascending order using std::less. template > ContainerOut sort(Container&& xs) { typedef typename std::remove_reference::type::value_type T; return sort_by(std::less(), std::forward(xs)); } namespace internal { template std::list stable_sort_by(internal::reuse_container_t, Compare comp, std::list&& xs) { xs.sort(comp); // std::list::sort ist already stable. return std::forward>(xs); } template std::list stable_sort_by(internal::create_new_container_t, Compare comp, const std::list& xs) { auto result = xs; result.sort(comp); // std::list::sort ist already stable. return result; } template Container stable_sort_by(internal::reuse_container_t, Compare comp, Container&& xs) { std::sort(std::begin(xs), std::end(xs), comp); return std::forward(xs); } template Container stable_sort_by(internal::create_new_container_t, Compare comp, const Container& xs) { auto result = xs; std::sort(std::begin(result), std::end(result), comp); return result; } } // namespace internal // API search type: stable_sort_by : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Sort a sequence stably by given less comparator. template > ContainerOut stable_sort_by(Compare comp, Container&& xs) { return internal::stable_sort_by(internal::can_reuse_v{}, comp, std::forward(xs)); } // API search type: stable_sort_on : ((a -> b), [a]) -> [a] // fwd bind count: 1 // Sort a sequence stably by given transformer. template > ContainerOut stable_sort_on(F f, Container&& xs) { return stable_sort_by(internal::is_less_by_struct(f), std::forward(xs)); } // API search type: stable_sort : [a] -> [a] // fwd bind count: 0 // Sort a sequence stably to ascending order using std::less. template > ContainerOut stable_sort(Container&& xs) { typedef typename std::remove_reference::type::value_type T; return stable_sort_by(std::less(), std::forward(xs)); } namespace internal { template Container partial_sort_by(internal::reuse_container_t, Compare comp, std::size_t count, Container&& xs) { if (count > xs.size()) { count = xs.size(); } auto middle = std::begin(xs); internal::advance_iterator(middle, count); std::partial_sort(std::begin(xs), middle, std::end(xs), comp); return std::forward(get_segment(internal::reuse_container_t(), 0, count, xs)); } template Container partial_sort_by(internal::create_new_container_t, Compare comp, std::size_t count, const Container& xs) { auto result = xs; return partial_sort_by( internal::reuse_container_t(), comp, count, std::move(result)); } } // namespace internal // API search type: partial_sort_by : (((a, a) -> Bool), Int, [a]) -> [a] // fwd bind count: 2 // Partially sort a sequence by a given less comparator. // Returns only the sorted segment. template > ContainerOut partial_sort_by(Compare comp, std::size_t count, Container&& xs) { return internal::partial_sort_by(internal::can_reuse_v{}, comp, count, std::forward(xs)); } // API search type: partial_sort_on : ((a -> b), Int, [a]) -> [a] // fwd bind count: 2 // Partially sort a sequence by a given transformer. // Returns only the sorted segment. template > ContainerOut partial_sort_on(F f, std::size_t count, Container&& xs) { return partial_sort_by(internal::is_less_by_struct(f), count, std::forward(xs)); } // API search type: partial_sort : (Int, [a]) -> [a] // fwd bind count: 1 // Partially sort a sequence in ascending order using std::less. // Returns only the sorted segment. template > ContainerOut partial_sort(std::size_t count, Container&& xs) { typedef typename std::remove_reference::type::value_type T; return partial_sort_by(std::less(), count, std::forward(xs)); } // API search type: nth_element_by : (((a, a) -> Bool), Int, [a]) -> a // fwd bind count: 2 // Return the nth largest element of a sequence by a given less comparator. template T nth_element_by(Compare comp, std::size_t n, const Container& xs) { assert(n < xs.size()); auto result = xs; auto middle = std::begin(result); internal::advance_iterator(middle, n); std::nth_element(std::begin(result), middle, std::end(result), comp); return *middle; } // API search type: nth_element_on : ((a -> b), Int, [a]) -> a // fwd bind count: 2 // Return the nth largest element of a sequence by a given transformer. template T nth_element_on(F f, std::size_t n, const Container& xs) { return nth_element_by(internal::is_less_by_struct(f), n, xs); } // API search type: nth_element : (Int, [a]) -> a // fwd bind count: 1 // Return the nth largest element of a sequence using std::less. template T nth_element(std::size_t n, const Container& xs) { return nth_element_by(std::less(), n, xs); } namespace internal { template Container unique_by(internal::reuse_container_t, BinaryPredicate pred, Container&& xs) { internal::check_binary_predicate_for_container(); const auto it_end = std::unique(std::begin(xs), std::end(xs), pred); xs.erase(it_end, std::end(xs)); return std::forward(xs); } template Container unique_by(internal::create_new_container_t, BinaryPredicate pred, const Container& xs) { auto result = xs; return unique_by(internal::reuse_container_t(), pred, std::move(result)); } } // namespace internal // API search type: unique_by : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Like unique, eliminates all but the first element // from every consecutive group of equivalent elements from the sequence; // but with a user supplied equality predicate. // See nub_by for making elements globally unique in a sequence. // O(n) template > ContainerOut unique_by(BinaryPredicate pred, Container&& xs) { return internal::unique_by(internal::can_reuse_v{}, pred, std::forward(xs)); } // API search type: unique_on : ((a -> b), [a]) -> [a] // fwd bind count: 1 // Like unique, eliminates all but the first element // from every consecutive group of equivalent elements from the sequence; // but with a user supplied transformation (e.g. getter). // See nub_on for making elements globally unique in a sequence. // O(n) // Also known as drop_repeats. template > ContainerOut unique_on(F f, Container&& xs) { return unique_by(internal::is_equal_by_struct(f), std::forward(xs)); } // API search type: unique : [a] -> [a] // fwd bind count: 0 // Eliminates all but the first element // from every consecutive group of equivalent elements from the sequence. // unique([1,2,2,3,2]) == [1,2,3,2] // See nub for making elements globally unique in a sequence. // O(n) template > ContainerOut unique(Container&& xs) { typedef typename std::remove_reference::type::value_type T; return unique_on(identity, std::forward(xs)); } // API search type: intersperse : (a, [a]) -> [a] // fwd bind count: 1 // Insert a value between all adjacent values in a sequence. // intersperse(0, [1, 2, 3]) == [1, 0, 2, 0, 3] // Also known as interpose. template Container intersperse(const X& value, const Container& xs) { if (xs.empty()) return Container(); if (size_of_cont(xs) == 1) return xs; Container result; internal::prepare_container(result, std::max(0, size_of_cont(xs)*2-1)); auto it = internal::get_back_inserter(result); for_each(std::begin(xs), --std::end(xs), [&value, &it](const X& x) { *it = x; *it = value; }); *it = xs.back(); return result; } // API search type: join : ([a], [[a]]) -> [a] // fwd bind count: 1 // Inserts a separator sequence in between the elements // of a sequence of sequences and concatenates the result. // Also known as intercalate or implode. // join(", ", ["a", "bee", "cee"]) == "a, bee, cee" // join([0, 0], [[1], [2], [3, 4]]) == [1, 0, 0, 2, 0, 0, 3, 4] template X join(const X& separator, const Container& xs) { return concat(intersperse(separator, xs)); } // API search type: join_elem : (a, [[a]]) -> [a] // fwd bind count: 1 // Inserts a separator in between the elements // of a sequence of sequences and concatenates the result. // Also known as intercalate_elem. // join_elem(';', ["a", "bee", "cee"]) == "a;bee;cee" // join_elem(0, [[1], [2], [3, 4]]) == [1, 0, 2, 0, 3, 4]] template Inner join_elem(const X& separator, const Container& xs) { return join(Inner(1, separator), xs); } // API search type: is_elem_of_by : ((a -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if at least one element of the container fulfils a predicate. // is_elem_of_by((==), [1,2,3]) == true template bool is_elem_of_by(UnaryPredicate pred, const Container& xs) { return std::find_if(std::begin(xs), std::end(xs), pred) != std::end(xs); } // API search type: is_elem_of : (a, [a]) -> Bool // fwd bind count: 1 // Checks if an element is a member of a container. // is_elem_of(2, [1,2,3]) == true // Also known as flip(contains). template bool is_elem_of(const typename Container::value_type& x, const Container& xs) { return is_elem_of_by(is_equal_to(x), xs); } // API search type: nub_by : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Makes the elements in a container unique with respect to a predicate // nub_by((==), [1,2,2,3,2]) == [1,2,3] // O(n^2) template Container nub_by(BinaryPredicate p, const Container& xs) { Container result; auto itOut = internal::get_back_inserter(result); for (const auto &x : xs) { auto eqToX = bind_1st_of_2(p, x); if (!is_elem_of_by(eqToX, result)) { *itOut = x; } } return result; } // API search type: nub_on : ((a -> b), [a]) -> [a] // fwd bind count: 1 // Makes the elements in a container unique // with respect to their function value. // nub_on((mod 10), [12,32,15]) == [12,15] // O(n^2) template Container nub_on(F f, const Container& xs) { return nub_by(is_equal_by(f), xs); } // API search type: nub : [a] -> [a] // fwd bind count: 0 // Makes the elements in a container unique. // nub([1,2,2,3,2]) == [1,2,3] // O(n^2) // Also known as distinct. template Container nub(const Container& xs) { typedef typename Container::value_type T; return nub_by(std::equal_to(), xs); } // API search type: all_unique_by_eq : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if all elements in a container are unique // with respect to a predicate. // Returns true for empty containers. // O(n^2) template bool all_unique_by_eq(BinaryPredicate p, const Container& xs) { internal::check_binary_predicate_for_container(); return size_of_cont(nub_by(p, xs)) == size_of_cont(xs); } // API search type: all_unique_on : ((a -> b), [a]) -> Bool // fwd bind count: 1 // Checks if all elements in a container are unique // with respect to their function values. // Returns true for empty containers. // O(n^2) template bool all_unique_on(F f, const Container& xs) { return all_unique_by_eq(is_equal_by(f), xs); } // API search type: all_unique : [a] -> Bool // fwd bind count: 0 // Checks if all elements in a container are unique. // Returns true for empty containers. // O(n^2) template bool all_unique(const Container& xs) { typedef typename Container::value_type T; auto comp = std::equal_to(); return all_unique_by_eq(comp, xs); } // API search type: is_strictly_sorted_by : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if a container already is strictly sorted using a predicate. // comp(a, b) must return true only if a < b. // O(n) template bool is_strictly_sorted_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); if (size_of_cont(xs) < 2) return true; auto it1 = std::begin(xs); for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) if (!internal::invoke(comp, *it1, *it2)) return false; return true; } // API search type: is_strictly_sorted_on : ((a -> b), [a]) -> Bool // fwd bind count: 1 // Checks if a container already is strictly sorted using a transformer. // O(n) template bool is_strictly_sorted_on(F f, const Container& xs) { return is_strictly_sorted_by(is_less_by(f), xs); } // API search type: is_strictly_sorted : [a] -> Bool // fwd bind count: 0 // Checks if a container already is strictly sorted // in ascending order using std::less. // O(n) template bool is_strictly_sorted(const Container& xs) { typedef typename Container::value_type T; auto comp = std::less(); return is_strictly_sorted_by(comp, xs); } // API search type: is_sorted_by : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if a container already is sorted using a predicate. // comp(a, b) must return true only if a < b. // O(n) template bool is_sorted_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); if (size_of_cont(xs) < 2) return true; auto it1 = std::begin(xs); for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) if (internal::invoke(comp, *it2, *it1)) return false; return true; } // API search type: is_sorted_on : ((a -> b), [a]) -> Bool // fwd bind count: 1 // Checks if a container already is strictly sorted using a transformer. // O(n) template bool is_sorted_on(F f, const Container& xs) { return is_sorted_by(is_less_by(f), xs); } // API search type: is_sorted : [a] -> Bool // fwd bind count: 0 // Checks if a container already is sorted // in ascending order using std::less. // O(n) template bool is_sorted(const Container& xs) { typedef typename Container::value_type T; auto comp = std::less(); return is_sorted_by(comp, xs); } // API search type: is_prefix_of : ([a], [a]) -> Bool // fwd bind count: 1 // Checks if a containers starts with a token. // is_prefix_of("Fun", "FunctionalPlus") == true template bool is_prefix_of(const Container& token, const Container& xs) { if (size_of_cont(token) > size_of_cont(xs)) return false; return get_segment(0, size_of_cont(token), xs) == token; } // API search type: is_suffix_of : ([a], [a]) -> Bool // fwd bind count: 1 // Checks if a containers contains a token as a segment. // is_suffix_of("us", "FunctionalPlus") == true template bool is_suffix_of(const Container& token, const Container& xs) { if (size_of_cont(token) > size_of_cont(xs)) return false; return get_segment(size_of_cont(xs) - size_of_cont(token), size_of_cont(xs), xs) == token; } // API search type: all_by : ((a -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if a containers contains a token as a segment. // all_by(is_even, [2, 4, 6]) == true // Returns true for empty containers. template bool all_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return std::all_of(std::begin(xs), std::end(xs), p); } // API search type: all : [Bool] -> Bool // fwd bind count: 0 // Checks if all values in a container evaluate to true. // all([true, false, true]) == false // Returns true for empty containers. template bool all(const Container& xs) { typedef typename Container::value_type T; return all_by(identity, xs); } // API search type: all_the_same_by : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if all values in a container are equal using a binary predicate. // Returns true for empty containers. template bool all_the_same_by(BinaryPredicate p, const Container& xs) { internal::check_binary_predicate_for_container(); if (size_of_cont(xs) < 2) return true; auto unaryPredicate = bind_1st_of_2(p, xs.front()); return all_by(unaryPredicate, xs); } // API search type: all_the_same_on : ((a -> b), [a]) -> Bool // fwd bind count: 1 // Checks if all values in a container are equal using a transformer. // Returns true for empty containers. template bool all_the_same_on(F f, const Container& xs) { if (size_of_cont(xs) < 2) return true; auto unaryPredicate = is_equal_by_to(f, f(xs.front())); return all_by(unaryPredicate, xs); } // API search type: all_the_same : [a] -> Bool // fwd bind count: 0 // Checks if all values in a container are equal. // Returns true for empty containers. template bool all_the_same(const Container& xs) { typedef typename Container::value_type T; auto binaryPredicate = std::equal_to(); return all_the_same_by(binaryPredicate, xs); } // API search type: numbers_step : (a, a, a) -> [a] // fwd bind count: 2 // Return a sequence of numbers using a specific step. // numbers_step(2, 9, 2) == [2, 4, 6, 8] template > ContainerOut numbers_step (const T start, const T end, const T step) { ContainerOut result; if ((step > 0 && start >= end) || (step < 0 && start <= end) || step == 0) { return result; } std::size_t size = static_cast((end - start) / step); internal::prepare_container(result, size); auto it = internal::get_back_inserter(result); for (T x = start; x < end; x += step) *it = x; return result; } // API search type: numbers : (a, a) -> [a] // fwd bind count: 1 // Return an ascending sequence of numbers.. // Also known as range. // numbers(2, 9) == [2, 3, 4, 5, 6, 7, 8] template > ContainerOut numbers(const T start, const T end) { return numbers_step(start, end, 1); } // API search type: singleton_seq : a -> [a] // fwd bind count: 0 // Construct a sequence containing a single value. // singleton_seq(3) == [3]. template > ContainerOut singleton_seq(const T& x) { return ContainerOut(1, x); } // API search type: all_idxs : [a] -> [Int] // fwd bind count: 0 // Returns a vector containing all valid indices of sequence xs. // all_idxs([6,4,7,6]) == [0,1,2,3] template std::vector all_idxs(const Container& xs) { return numbers(0, size_of_cont(xs)); } // API search type: init : [a] -> [a] // fwd bind count: 0 // init([0,1,2,3]) == [0,1,2] // Unsafe! xs must be non-empty. template > ContainerOut init(Container&& xs) { assert(!is_empty(xs)); return get_segment(0, size_of_cont(std::forward(xs)) - 1, xs); } // API search type: tail : [a] -> [a] // fwd bind count: 0 // Drops the first element of a container, keeps the rest. Unsafe! // tail([0,1,2,3]) == [1,2,3] // Unsafe! xs must be non-empty. template > ContainerOut tail(Container&& xs) { assert(!is_empty(xs)); return get_segment(1, size_of_cont(std::forward(xs)), xs); } // API search type: head : [a] -> a // fwd bind count: 0 // Return the first element of a container. // head([0,1,2,3]) == 0 // Unsafe! xs must be non-empty. template typename Container::value_type head(const Container& xs) { assert(!is_empty(xs)); return xs.front(); } // API search type: last : [a] -> a // fwd bind count: 0 // Return the last element of a container. // last([0,1,2,3]) == 3 // Unsafe! xs must be non-empty. template typename Container::value_type last(const Container& xs) { assert(!is_empty(xs)); return xs.back(); } // API search type: mean_stddev : [a] -> (a, a) // fwd bind count: 0 // Calculates the mean and the population standard deviation. // mean_stddev([4, 8]) == (6, 2) // mean_stddev([1, 3, 7, 4]) == (3.75, 2.5) // xs must be non-empty. template std::pair mean_stddev(const Container& xs) { assert(size_of_cont(xs) != 0); // http://stackoverflow.com/a/7616783/1866775 Result sum = static_cast( internal::accumulate(xs.begin(), xs.end(), static_cast(0))); Result mean = sum / static_cast(xs.size()); std::vector diff(xs.size()); std::transform(xs.begin(), xs.end(), diff.begin(), [mean](Result x) { return x - mean; }); Result sq_sum = std::inner_product( diff.begin(), diff.end(), diff.begin(), static_cast(0)); Result stddev = std::sqrt(sq_sum / static_cast(xs.size())); return std::make_pair(mean, stddev); } // API search type: count_occurrences_by : ((a -> b), [a]) -> Map b Int // fwd bind count: 1 // Returns a discrete frequency distribution of the elements in a container // applying a specific transformer. // count_occurrences_by(floor, [1.1, 2.3, 2.7, 3.6, 2.4]) == [(1, 1), (2, 3), (3, 1)] // O(n) template auto count_occurrences_by(F f, const ContainerIn& xs) { using In = typename ContainerIn::value_type; using MapOut = std::map>, std::size_t>; internal::trigger_static_asserts(); MapOut result; for (const auto& x : xs) { ++result[internal::invoke(f, x)]; } return result; } // API search type: count_occurrences : [a] -> Map a Int // fwd bind count: 0 // Returns a discrete frequency distribution of the elements in a container // applying a specific transformer. // Can be used to create a histogram. // count_occurrences([1,2,2,3,2]) == [(1, 1), (2, 3), (3, 1)] // O(n) template > MapOut count_occurrences(const ContainerIn& xs) { return count_occurrences_by(identity, xs); } // API search type: lexicographical_less_by : (((a, a) -> Bool), [a], [a]) -> Bool // fwd bind count: 2 // Lexicographical less-than comparison using a specific predicate. // lexicographical_less_by((<), [0,1,2,2,4,5], [0,1,2,3,4,5]) == true // lexicographical_less_by((<), "012245", "012345") == true // lexicographical_less_by((<), "01234", "012345") == true // lexicographical_less_by((<), "012345", "01234") == false // lexicographical_less_by((<), "012345", "012345") == false template bool lexicographical_less_by(BinaryPredicate p, const Container& xs, const Container& ys) { internal::check_binary_predicate_for_container(); auto itXs = std::begin(xs); auto itYs = std::begin(ys); while (itXs != std::end(xs) && itYs != std::end(ys)) { if (internal::invoke(p, *itXs, *itYs)) { return true; } if (internal::invoke(p, *itYs, *itXs)) { return false; } ++itXs; ++itYs; } if (size_of_cont(xs) < size_of_cont(ys)) { return true; } return false; } // API search type: lexicographical_less : ([a], [a]) -> Bool // fwd bind count: 1 // Lexicographical less-than comparison. // lexicographical_less([0,1,2,2,4,5], [0,1,2,3,4,5]) == true // lexicographical_less("012245", "012345") == true // lexicographical_less("01234", "012345") == true // lexicographical_less("012345", "01234") == false // lexicographical_less("012345", "012345") == false template bool lexicographical_less(const Container& xs, const Container& ys) { return lexicographical_less_by( is_less, xs, ys); } // API search type: lexicographical_sort : [[a]] -> [[a]] // fwd bind count: 0 // sort by lexicographical_less template > ContainerOut lexicographical_sort(Container&& xs) { typedef typename std::remove_reference::type::value_type T; return sort_by(lexicographical_less, std::forward(xs)); } // API search type: replicate : (Int, a) -> [a] // fwd bind count: 1 // Create a sequence containing x n times. // replicate(3, 1) == [1, 1, 1] template > ContainerOut replicate(std::size_t n, const T& x) { return ContainerOut(n, x); } namespace internal { template T instead_of_if(internal::reuse_container_t, UnaryPredicate pred, const T& alt, T&& x) { if (internal::invoke(pred, x)) return alt; else return std::forward(x); } template T instead_of_if(internal::create_new_container_t, UnaryPredicate pred, const T& alt, const T& x) { if (internal::invoke(pred, x)) return alt; else return x; } } // namespace internal // API search type: instead_of_if : ((a -> Bool), a, a) -> a // fwd bind count: 2 // Return alt if pred(x), otherwise x itself. template auto instead_of_if(UnaryPredicate pred, const TAlt& alt, T&& x) { return internal::instead_of_if(internal::can_reuse_v{}, pred, alt, std::forward(x)); } // API search type: instead_of_if_empty : ((a -> Bool), [a], [a]) -> [a] // fwd bind count: 2 // Return alt if xs is empty, otherwise xs itself. template > ContainerOut instead_of_if_empty(const ContainerAlt& alt, Container&& xs) { return instead_of_if( is_empty>, alt, std::forward(xs)); } } // namespace fplus libfplus-0.2.13/include/fplus/container_properties.hpp000066400000000000000000000600241376322245400231520ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace fplus { // API search type: any_by : ((a -> Bool), [a]) -> Bool // fwd bind count: 1 // Check if all elements in a container fulfill a predicate. // any_by(is_odd, [2, 4, 6]) == false template bool any_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return std::any_of(std::begin(xs), std::end(xs), p); } // API search type: any : [Bool] -> Bool // fwd bind count: 0 // Checks if all elements in a container are true. // any([false, true, false]) == true template bool any(const Container& xs) { typedef typename Container::value_type T; return any_by(identity, xs); } // API search type: none_by : ((a -> Bool), [a]) -> Bool // fwd bind count: 1 // Check no element in a container fulfills a predicate. // none_by(is_even, [3, 4, 5]) == false template bool none_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return std::none_of(std::begin(xs), std::end(xs), p); } // API search type: none : [Bool] -> Bool // fwd bind count: 0 // Checks if all elements in a container are false. // none([false, true, false]) == false template bool none(const Container& xs) { typedef typename Container::value_type T; return none_by(identity, xs); } // API search type: minimum_idx_by : (((a, a) -> Bool), [a]) -> Int // fwd bind count: 1 // Return the index of the first minimum element using a less comparator. // minimum_idx_by(lessLength, ["123", "12", "1234", "123"]) -> "1" // Unsafe! Crashes on an empty sequence. template typename std::size_t minimum_idx_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); assert(is_not_empty(xs)); return static_cast(std::distance(std::begin(xs), std::min_element(std::begin(xs), std::end(xs), comp))); } // API search type: minimum_idx_by_maybe : (((a, a) -> Bool), [a]) -> Int // fwd bind count: 1 // Return the index of the first minimum element using a less comparator // if sequence is not empty. // minimum_idx_by_maybe(lessLength, ["123", "12", "1234", "123"]) -> Just "1" // minimum_idx_by_maybe(lessLength, []) -> Nothing template maybe minimum_idx_by_maybe(Compare comp, const Container& xs) { if (is_empty(xs)) return {}; else return minimum_idx_by(comp, xs); } // API search type: maximum_idx_by : (((a, a) -> Bool), [a]) -> Int // fwd bind count: 1 // Return the index of the first maximum element using a less comparator. // maximum_idx_by(lessLength, ["123", "12", "1234", "123"]) == "2" // Unsafe! Crashes on an empty sequence. template typename std::size_t maximum_idx_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); assert(is_not_empty(xs)); return static_cast(std::distance(std::begin(xs), std::max_element(std::begin(xs), std::end(xs), comp))); } // API search type: maximum_idx_by_maybe : (((a, a) -> Bool), [a]) -> Maybe Int // fwd bind count: 1 // Return the index of the first maximum element using a less comparator // if sequence is not empty. // maximum_idx_by_maybe(lessLength, ["123", "12", "1234", "123"]) == Just "2" // maximum_idx_by_maybe(lessLength, []) == Nothing template maybe maximum_idx_by_maybe(Compare comp, const Container& xs) { if (is_empty(xs)) return {}; else return maximum_idx_by(comp, xs); } // API search type: minimum_idx : [a] -> Int // fwd bind count: 0 // Return the index of the first minimum element. // minimum_idx([3, 1, 4, 2]) == 1 // Unsafe! Crashes on an empty sequence. template typename std::size_t minimum_idx(const Container& xs) { return minimum_idx_by(is_less, xs); } // API search type: minimum_idx_maybe : [a] -> Maybe Int // fwd bind count: 0 // Return the index of the first minimum element if sequence is not empty. // minimum_idx_maybe([3, 1, 4, 2]) == Just 1 // minimum_idx_maybe([]) == Nothing template maybe minimum_idx_maybe(const Container& xs) { if (is_empty(xs)) return {}; else return minimum_idx(xs); } // API search type: maximum_idx : [a] -> Int // fwd bind count: 0 // Return the index of the first maximum element. // maximum_idx([3, 1, 4, 2]) == 2 // Unsafe! Crashes on an empty sequence. template typename std::size_t maximum_idx(const Container& xs) { return maximum_idx_by(is_less, xs); } // API search type: maximum_idx_maybe : [a] -> Maybe Int // fwd bind count: 0 // Return the index of the first maximum element if sequence is not empty. // maximum_idx_maybe([3, 1, 4, 2]) == Just 2 // maximum_imaximum_idx_maybedx([]) == Nothing template maybe maximum_idx_maybe(const Container& xs) { if (is_empty(xs)) return {}; else return maximum_idx(xs); } // API search type: minimum_idx_on : ((a -> b), [a]) -> Int // fwd bind count: 1 // Return the index of the first minimum element using a transformer. // minimum_idx_on(length, ["123", "12", "1234", "123"]) -> "1" // Unsafe! Crashes on an empty sequence. template std::size_t minimum_idx_on(F f, const Container& xs) { using Result = internal::invoke_result_t; auto transformed = transform_convert>>(f, xs); return minimum_idx(transformed); } // API search type: minimum_idx_on_maybe : ((a -> b), [a]) -> Just Int // fwd bind count: 1 // Return the index of the first minimum element using a transformer // if sequence is not empty. // minimum_idx_on_maybe(length, ["123", "12", "1234", "123"]) -> Just "1" // minimum_idx_on_maybe(length, []) -> Nothing" template maybe minimum_idx_on_maybe(F f, const Container& xs) { if (is_empty(xs)) return {}; else return minimum_idx_on(f, xs); } // API search type: maximum_idx_on : ((a -> b), [a]) -> Int // fwd bind count: 1 // Return the index of the first maximum element using a transformer. // maximum_idx_on(length, ["123", "12", "1234", "123"]) == "2" // Unsafe! Crashes on an empty sequence. template std::size_t maximum_idx_on(F f, const Container& xs) { using Result = internal::invoke_result_t; auto transformed = transform_convert>>(f, xs); return maximum_idx(transformed); } // API search type: maximum_idx_on_maybe : ((a -> b), [a]) -> Maybe Int // fwd bind count: 1 // Return the index of the first maximum element using a transformer // if sequence is not empty. // maximum_idx_on_maybe(length, ["123", "12", "1234", "123"]) == Just "2" // maximum_idx_on_maybe(length, []) == Nothing template maybe maximum_idx_on_maybe(F f, const Container& xs) { if (is_empty(xs)) return {}; else return maximum_idx_on(f, xs); } // API search type: minimum_by : (((a, a) -> Bool), [a]) -> a // fwd bind count: 1 // Return the first minimum element using a less comparator. // minimum_by(lessLength, ["123", "12", "1234", "123"]) -> "12" // Unsafe! Crashes on an empty sequence. template typename Container::value_type minimum_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); assert(is_not_empty(xs)); return *std::min_element(std::begin(xs), std::end(xs), comp); } // API search type: minimum_by_maybe : (((a, a) -> Bool), [a]) -> a // fwd bind count: 1 // Return the first minimum element using a less comparator // if sequence is not empty. // minimum_by_maybe(lessLength, ["123", "12", "1234", "123"]) -> Just "12" // minimum_by_maybe(lessLength, []) -> Nothing template maybe minimum_by_maybe(Compare comp, const Container& xs) { if (is_empty(xs)) return {}; else return minimum_by(comp, xs); } // API search type: maximum_by : (((a, a) -> Bool), [a]) -> a // fwd bind count: 1 // Return the first maximum element using a less comparator. // maximum_by(lessLength, ["123", "12", "1234", "123"]) == "1234" // Unsafe! Crashes on an empty sequence. template typename Container::value_type maximum_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); assert(is_not_empty(xs)); return *std::max_element(std::begin(xs), std::end(xs), comp); } // API search type: maximum_by_maybe : (((a, a) -> Bool), [a]) -> Maybe a // fwd bind count: 1 // Return the first maximum element using a less comparator // if sequence is not empty. // maximum_by_maybe(lessLength, ["123", "12", "1234", "123"]) == Just "1234" // maximum_by_maybe(lessLength, []) == Nothing template maybe maximum_by_maybe(Compare comp, const Container& xs) { if (is_empty(xs)) return {}; else return maximum_by(comp, xs); } // API search type: minimum : [a] -> a // fwd bind count: 0 // Return the first minimum element. // minimum([3, 1, 4, 2]) == 1 // Unsafe! Crashes on an empty sequence. template typename Container::value_type minimum(const Container& xs) { return minimum_by(is_less, xs); } // API search type: minimum_maybe : [a] -> Maybe a // fwd bind count: 0 // Return the first minimum element if sequence is not empty // if sequence is not empty. // minimum_maybe([3, 1, 4, 2]) == Just 1 // minimum_maybe([]) == Nothing template maybe minimum_maybe(const Container& xs) { if (is_empty(xs)) return {}; else return minimum(xs); } // API search type: maximum : [a] -> a // fwd bind count: 0 // Return the first maximum element. // maximum([3, 1, 4, 2]) == 4 // Unsafe! Crashes on an empty sequence. template typename Container::value_type maximum(const Container& xs) { return maximum_by(is_less, xs); } // API search type: maximum_maybe : [a] -> Maybe a // fwd bind count: 0 // Return the first maximum element if sequence is not empty // if sequence is not empty. // maximum_maybe([3, 1, 4, 2]) == Just 4 // maximum_maybe([]) == Nothing template maybe maximum_maybe(const Container& xs) { if (is_empty(xs)) return {}; else return maximum(xs); } // API search type: minimum_on : ((a -> b), [a]) -> a // fwd bind count: 1 // Return the first minimum element using a transformer. // minimum_on(length, ["123", "12", "1234", "123"]) -> "12" // Unsafe! Crashes on an empty sequence. template typename Container::value_type minimum_on(F f, const Container& xs) { internal::trigger_static_asserts(); return elem_at_idx(minimum_idx_on(f, xs), xs); } // API search type: minimum_on_maybe : ((a -> b), [a]) -> Maybe a // fwd bind count: 1 // Return the first minimum element using a transformer // if sequence is not empty. // minimum_on_maybe(length, ["123", "12", "1234", "123"]) -> Just "12" // minimum_on_maybe(length, []) -> Nothing template maybe minimum_on_maybe( F f, const Container& xs) { if (is_empty(xs)) return {}; else return minimum_on(f, xs); } // API search type: maximum_on : ((a -> b), [a]) -> a // fwd bind count: 1 // Return the first maximum element using a transformer. // maximum_on(length, ["123", "12", "1234", "123"]) == "1234" // Unsafe! Crashes on an empty sequence. template typename Container::value_type maximum_on(F f, const Container& xs) { internal::trigger_static_asserts(); return elem_at_idx(maximum_idx_on(f, xs), xs); } // API search type: maximum_on_maybe : ((a -> b), [a]) -> Maybe a // fwd bind count: 1 // Return the first maximum element using a transformer // if sequence is not empty. // maximum_on_maybe(length, ["123", "12", "1234", "123"]) == Just "1234" // maximum_on_maybe(length, ["123", "12", "1234", "123"]) == Nothing template maybe maximum_on_maybe( F f, const Container& xs) { if (is_empty(xs)) return {}; else return maximum_on(f, xs); } // API search type: mean : [a] -> a // fwd bind count: 0 // mean([1, 4, 4]) == 3 // Also known as average. // xs must have at least one element. // Use mean_using_doubles if overflow errors for sum(xs) can occur. // Unsafe! Crashes on an empty sequence. template Result mean(const Container& xs) { assert(size_of_cont(xs) != 0); typedef typename Container::value_type T; return static_cast(sum(xs) / static_cast(size_of_cont(xs))); } // API search type: mean_obj_div_size_t : [a] -> a // fwd bind count: 0 // mean_obj_div_size_t([B 1, B 4, B 4]) == B 3 // The provided objects must support division by a std::size_t. // Unsafe! Crashes on an empty sequence. // Also Make sure sum(xs) does not overflow. template T mean_obj_div_size_t(const Container& xs) { assert(size_of_cont(xs) != 0); return sum(xs) / size_of_cont(xs); } // API search type: mean_obj_div_double : [a] -> a // fwd bind count: 0 // mean_obj_div_double([B 1, B 4, B 4]) == B 3 // The provided objects must support division by a double. // Unsafe! Crashes on an empty sequence. // Also Make sure sum(xs) does not overflow. template T mean_obj_div_double(const Container& xs) { assert(size_of_cont(xs) != 0); return sum(xs) / static_cast(size_of_cont(xs)); } // API search type: mean_using_doubles : [a] -> a // fwd bind count: 0 // mean_using_doubles([1, 4, 4]) == 3 // Converts elements to double before calculating the sum // to prevent overflows. // Unsafe! Crashes on an empty sequence. template Result mean_using_doubles(const Container& xs) { assert(size_of_cont(xs) != 0); auto xs_as_doubles = convert_elems(xs); auto result_as_double = mean(xs_as_doubles); if (!std::is_integral::value) return static_cast(result_as_double); else return round(result_as_double); } // API search type: median : [a] -> a // fwd bind count: 0 // median([5, 6, 4, 3, 2, 6, 7, 9, 3]) == 5 // Unsafe! Crashes on an empty sequence. template Result median(const Container& xs) { assert(is_not_empty(xs)); if (size_of_cont(xs) == 1) return static_cast(xs.front()); // std::nth_element (instead of sorting) // would be faster for random-access containers // but not work at all on other containers like std::list. auto xsSorted = sort(xs); if (size_of_cont(xsSorted) % 2 == 1) { auto it = std::begin(xsSorted); internal::advance_iterator(it, size_of_cont(xsSorted) / 2); return static_cast(*it); } else { auto it1 = std::begin(xsSorted); internal::advance_iterator(it1, size_of_cont(xsSorted) / 2 - 1); auto it2 = it1; ++it2; return static_cast(*it1 + *it2) / static_cast(2); } } // API search type: all_unique_by_less : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Returns true for empty containers. // O(n*log(n)) template bool all_unique_by_less(Compare comp, const Container& xs) { internal::check_compare_for_container(); if (size_of_cont(xs) < 2) return true; return size_of_cont(unique(sort_by(comp, xs))) == size_of_cont(xs); } // API search type: all_unique_less : [a] -> Bool // fwd bind count: 0 // Returns true for empty containers. // O(n*log(n)) template bool all_unique_less(const Container& xs) { typedef typename Container::value_type T; auto comp = std::less(); return all_unique_by_less(comp, xs); } // API search type: is_infix_of : ([a], [a]) -> Bool // fwd bind count: 1 // is_infix_of("ion", "FunctionalPlus") == true template bool is_infix_of(const Container& token, const Container& xs) { return is_just(find_first_instance_of_token(token, xs)); } // API search type: is_subsequence_of : ([a], [a]) -> Bool // fwd bind count: 1 // is_subsequence_of("Final", "FunctionalPlus") == true template bool is_subsequence_of(const Container& seq, const Container& xs) { if (is_empty(seq)) return true; if (size_of_cont(seq) > size_of_cont(xs)) return false; typedef typename Container::value_type T; auto remaining = convert_container_and_elems>(seq); for (const auto& x : xs) { if (x == remaining.front()) { remaining.pop_front(); if (is_empty(remaining)) return true; } } return false; } // API search type: count_if : ((a -> Bool), [a]) -> Int // fwd bind count: 1 // count_if(is_even, [1, 2, 3, 5, 7, 8]) == 2 template std::size_t count_if(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return size_of_cont(find_all_idxs_by(p, xs)); } // API search type: count : (a, [a]) -> Int // fwd bind count: 1 // count(2, [1, 2, 3, 5, 7, 2, 2]) == 3 template std::size_t count (const typename Container::value_type& x, const Container& xs) { return size_of_cont(find_all_idxs_of(x, xs)); } // API search type: is_unique_in_by : ((a -> bool), [a]) -> Bool // fwd bind count: 1 // is_unique_in_by((==2), [1, 2, 3, 5, 7, 2, 2]) == false // is_unique_in_by((==5), [1, 2, 3, 5, 7, 2, 2]) == true template bool is_unique_in_by (UnaryPredicate pred, const Container& xs) { std::size_t count = 0; for (const auto& x : xs) { if (internal::invoke(pred, x)) { ++count; if (count > 1) { return false; } } } return true; } // API search type: is_unique_in : (a, [a]) -> Bool // fwd bind count: 1 // is_unique_in(2, [1, 2, 3, 5, 7, 2, 2]) == false // is_unique_in(5, [1, 2, 3, 5, 7, 2, 2]) == true template bool is_unique_in (const typename Container::value_type& x, const Container& xs) { return is_unique_in_by(is_equal_to(x), xs); } // API search type: is_permutation_of : ([a], [a]) -> Bool // fwd bind count: 1 // Checks if one container is a permuation of the other one. // is_permutation_of([2,3,1], [1,2,3]) == true // O(log(n)) template bool is_permutation_of(const Container& xs, const Container& ys) { return size_of_cont(xs) == size_of_cont(ys) && sort(xs) == sort(ys); } // API search type: fill_pigeonholes_to : (Int, [Int]) -> [Int] // fwd bind count: 1 // Returns a list containing the count for every element in xs // with the value corresponding to the index in the result list. // fill_pigeonholes_to(5, [0,1,3,1]) == [1,2,0,1,0] // fill_pigeonholes_to(3, [0,1,3,1]) == [1,2,0] template , typename T = typename ContainerIn::value_type> ContainerOut fill_pigeonholes_to(std::size_t idx_end, const ContainerIn& xs) { static_assert(std::is_integral::value, "Type must be integral."); if (is_empty(xs)) return {}; ContainerOut result(idx_end, 0); for (const auto& x : xs) { if (x >= 0) { const auto idx = static_cast(x); if (idx < result.size()) { ++result[idx]; } } } return result; } // API search type: fill_pigeonholes : [Int] -> [Int] // fwd bind count: 0 // Returns a list containing the count for every element in xs // with the value corresponding to the index in the result list. // fill_pigeonholes([0,1,3,1]) == [1,2,0,1] template , typename T = typename ContainerIn::value_type> ContainerOut fill_pigeonholes(const ContainerIn& xs) { static_assert(std::is_integral::value, "Type must be integral."); if (is_empty(xs)) return {}; return(fill_pigeonholes_to( maximum(xs) + 1, xs)); } // API search type: fill_pigeonholes_bool_to : (Int, [Int]) -> [Int] // fwd bind count: 1 // Returns a list telling if the element corresponding to the index // is present in xs. // fill_pigeonholes_bool_to(5, [0,1,3,1]) == [1,1,0,1,0] // fill_pigeonholes_bool_to(3, [0,1,3,1]) == [1,1,0] template , typename T = typename ContainerIn::value_type> ContainerOut fill_pigeonholes_bool_to(std::size_t idx_end, const ContainerIn& xs) { static_assert(std::is_integral::value, "Type must be integral."); if (is_empty(xs)) return {}; ContainerOut result(idx_end, 0); for (const auto& x : xs) { if (x >= 0) { const auto idx = static_cast(x); if (idx < result.size()) { result[idx] = 1; } } } return result; } // API search type: fill_pigeonholes_bool : [Int] -> [Int] // fwd bind count: 0 // Returns a list telling if the element corresponding to the index // is present in xs. // fill_pigeonholes_bool([0,1,3,1]) == [1,2,0,1] template , typename T = typename ContainerIn::value_type> ContainerOut fill_pigeonholes_bool(const ContainerIn& xs) { static_assert(std::is_integral::value, "Type must be integral."); if (is_empty(xs)) return {}; return(fill_pigeonholes_bool_to( maximum(xs) + 1, xs)); } // API search type: present_in_all : [[a]] -> [a] // fwd bind count: 0 // Returns a list containing only the elements present in all sublists of xs. // Also known as gemstones. // present_in_all([[4,1,2], [5,2,1], [2,4,1]]) == [1,2] template > ContainerOut present_in_all(const ContainerIn& xs) { return convert_container( fplus::sets_intersection( transform( convert_container, SubContainerIn>, xs))); } } // namespace fplus libfplus-0.2.13/include/fplus/container_traits.hpp000066400000000000000000000155611376322245400222720ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fplus { namespace internal { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #endif template struct has_order : public std::false_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::false_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::false_type {}; template struct has_order> : public std::true_type {}; // http://stackoverflow.com/a/33828321/1866775 template::lowest()> struct same_cont_new_t : public std::false_type{}; template struct same_cont_new_t, NewT, SizeOffset> { static_assert(SizeOffset != std::numeric_limits::lowest(), "Size of std::array must be known at compile-time."); typedef typename std::array(static_cast(N) + SizeOffset)> type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::vector type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::deque type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::forward_list type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::list type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::set type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::stack type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::queue type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::priority_queue type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::basic_string type; }; template struct SameMapTypeNewTypes : public std::false_type {}; template struct SameMapTypeNewTypes, NewKey, NewVal> { typedef typename std::map type; }; template struct SameMapTypeNewTypes, NewKey, NewVal> { typedef typename std::unordered_map type; }; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif template< typename ContIn, typename F, int SizeOffset = std::numeric_limits::lowest(), typename T = typename ContIn::value_type, typename ContOut = typename same_cont_new_t>, SizeOffset>::type> struct same_cont_new_t_from_unary_f { typedef ContOut type; }; template< typename ContIn, typename F, typename T1, typename T2, int SizeOffset = std::numeric_limits::lowest(), typename ContOut = typename same_cont_new_t>, SizeOffset>::type> struct same_cont_new_t_from_binary_f { typedef ContOut type; }; // https://stackoverflow.com/a/44549820/1866775 template struct can_self_assign { using type = std::is_assignable; }; template using can_self_assign_t = typename can_self_assign::type; template struct can_self_assign> { enum { t0 = can_self_assign_t::value, t1 = can_self_assign_t::value, x = t0&&t1 }; using type = std::integral_constant; }; template<> struct can_self_assign> { using type = std::integral_constant; }; template struct can_self_assign> { using type = std::integral_constant::value && can_self_assign_t>::value >; }; template struct reuse_container_bool_t { }; using create_new_container_t = reuse_container_bool_t; using reuse_container_t = reuse_container_bool_t; template struct can_reuse { using dContainer = typename std::decay::type; using can_assign = can_self_assign_t; using cannot_reuse = std::is_lvalue_reference; using value = reuse_container_bool_t; }; template using can_reuse_v = typename can_reuse::value; template struct remove_const_and_ref { using type = typename std::remove_const::type>::type; }; template using remove_const_and_ref_t = typename remove_const_and_ref::type; } // namespace internal } // namespace fplus libfplus-0.2.13/include/fplus/curry.hpp000066400000000000000000000053401376322245400200600ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once namespace fplus { namespace curry { // Currying. // Allow to generically bind parameters one by one. #define fplus_curry_define_fn_0(fplus_curry_define_fn_0_name) \ inline auto fplus_curry_define_fn_0_name() \ { \ return [](auto&& fplus_curry_p1) \ { \ return fplus::fplus_curry_define_fn_0_name(std::forward(fplus_curry_p1)); \ }; \ } #define fplus_curry_define_fn_1(fplus_curry_define_fn_1_name) \ template \ auto fplus_curry_define_fn_1_name(P1 p1) \ { \ return [p1](auto&& fplus_curry_p2) \ { \ return fplus::fplus_curry_define_fn_1_name(p1, std::forward(fplus_curry_p2)); \ }; \ } #define fplus_curry_define_fn_2(fplus_curry_define_fn_2_name) \ template \ auto fplus_curry_define_fn_2_name(P1 p1) \ { \ return [p1](const auto& fplus_curry_p2) \ { \ return [p1, fplus_curry_p2](auto&& fplus_curry_p3) \ { \ return fplus::fplus_curry_define_fn_2_name(p1, fplus_curry_p2, std::forward(fplus_curry_p3)); \ }; \ }; \ } #define fplus_curry_define_fn_3(fplus_curry_define_fn_3_name) \ template \ auto fplus_curry_define_fn_3_name(P1 p1) \ { \ return [p1](const auto& fplus_curry_p2) \ { \ return [p1, fplus_curry_p2](const auto& fplus_curry_p3) \ { \ return [p1, fplus_curry_p2, fplus_curry_p3](auto&& fplus_curry_p4) \ { \ return fplus::fplus_curry_define_fn_3_name(p1, fplus_curry_p2, fplus_curry_p3, std::forward(fplus_curry_p4)); \ }; \ }; \ }; \ } #define fplus_curry_define_fn_4(fplus_curry_define_fn_4_name) \ template \ auto fplus_curry_define_fn_4_name(P1 p1) \ { \ return [p1](const auto& fplus_curry_p2) \ { \ return [p1, fplus_curry_p2](const auto& fplus_curry_p3) \ { \ return [p1, fplus_curry_p2, fplus_curry_p3](const auto& fplus_curry_p4) \ { \ return [p1, fplus_curry_p2, fplus_curry_p3, fplus_curry_p4](auto&& fplus_curry_p5) \ { \ return fplus::fplus_curry_define_fn_4_name(p1, fplus_curry_p2, fplus_curry_p3, fplus_curry_p4, std::forward(fplus_curry_p5)); \ }; \ }; \ }; \ }; \ } #include "curry_instances.autogenerated_defines" } // namespace curry } // namespace fplus libfplus-0.2.13/include/fplus/curry_instances.autogenerated_defines000066400000000000000000000445371376322245400256770ustar00rootroot00000000000000// THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT EDIT. fplus_curry_define_fn_0(identity) fplus_curry_define_fn_1(is_equal) fplus_curry_define_fn_1(is_not_equal) fplus_curry_define_fn_1(is_less) fplus_curry_define_fn_1(is_less_or_equal) fplus_curry_define_fn_1(is_greater) fplus_curry_define_fn_1(is_greater_or_equal) fplus_curry_define_fn_1(xor_bools) fplus_curry_define_fn_0(is_just) fplus_curry_define_fn_0(is_nothing) fplus_curry_define_fn_0(unsafe_get_just) fplus_curry_define_fn_0(just_with_default) fplus_curry_define_fn_1(throw_on_nothing) fplus_curry_define_fn_0(just) fplus_curry_define_fn_1(as_just_if) fplus_curry_define_fn_0(maybe_to_seq) fplus_curry_define_fn_0(singleton_seq_as_maybe) fplus_curry_define_fn_1(lift_maybe) fplus_curry_define_fn_2(lift_maybe_def) fplus_curry_define_fn_2(lift_maybe_2) fplus_curry_define_fn_3(lift_maybe_2_def) fplus_curry_define_fn_1(and_then_maybe) fplus_curry_define_fn_0(flatten_maybe) fplus_curry_define_fn_0(is_empty) fplus_curry_define_fn_0(is_not_empty) fplus_curry_define_fn_0(size_of_cont) fplus_curry_define_fn_0(convert) fplus_curry_define_fn_0(convert_elems) fplus_curry_define_fn_0(convert_container) fplus_curry_define_fn_0(convert_container_and_elems) fplus_curry_define_fn_2(get_segment) fplus_curry_define_fn_2(set_segment) fplus_curry_define_fn_2(remove_segment) fplus_curry_define_fn_2(insert_at) fplus_curry_define_fn_1(elem_at_idx) fplus_curry_define_fn_1(elem_at_idx_maybe) fplus_curry_define_fn_1(elems_at_idxs) fplus_curry_define_fn_1(transform) fplus_curry_define_fn_1(transform_convert) fplus_curry_define_fn_1(transform_inner) fplus_curry_define_fn_0(reverse) fplus_curry_define_fn_1(take) fplus_curry_define_fn_1(take_exact) fplus_curry_define_fn_1(take_cyclic) fplus_curry_define_fn_1(drop) fplus_curry_define_fn_1(take_last) fplus_curry_define_fn_1(drop_last) fplus_curry_define_fn_1(drop_exact) fplus_curry_define_fn_1(take_while) fplus_curry_define_fn_1(drop_while) fplus_curry_define_fn_2(fold_left) fplus_curry_define_fn_2(reduce) fplus_curry_define_fn_1(fold_left_1) fplus_curry_define_fn_1(reduce_1) fplus_curry_define_fn_2(fold_right) fplus_curry_define_fn_1(fold_right_1) fplus_curry_define_fn_2(scan_left) fplus_curry_define_fn_1(scan_left_1) fplus_curry_define_fn_2(scan_right) fplus_curry_define_fn_1(scan_right_1) fplus_curry_define_fn_0(sum) fplus_curry_define_fn_0(product) fplus_curry_define_fn_1(append_elem) fplus_curry_define_fn_1(prepend_elem) fplus_curry_define_fn_1(append) fplus_curry_define_fn_1(append_convert) fplus_curry_define_fn_0(concat) fplus_curry_define_fn_1(interweave) fplus_curry_define_fn_0(unweave) fplus_curry_define_fn_1(sort_by) fplus_curry_define_fn_1(sort_on) fplus_curry_define_fn_0(sort) fplus_curry_define_fn_1(stable_sort_by) fplus_curry_define_fn_1(stable_sort_on) fplus_curry_define_fn_0(stable_sort) fplus_curry_define_fn_2(partial_sort_by) fplus_curry_define_fn_2(partial_sort_on) fplus_curry_define_fn_1(partial_sort) fplus_curry_define_fn_2(nth_element_by) fplus_curry_define_fn_2(nth_element_on) fplus_curry_define_fn_1(nth_element) fplus_curry_define_fn_1(unique_by) fplus_curry_define_fn_1(unique_on) fplus_curry_define_fn_0(unique) fplus_curry_define_fn_1(intersperse) fplus_curry_define_fn_1(join) fplus_curry_define_fn_1(join_elem) fplus_curry_define_fn_1(is_elem_of_by) fplus_curry_define_fn_1(is_elem_of) fplus_curry_define_fn_1(nub_by) fplus_curry_define_fn_1(nub_on) fplus_curry_define_fn_0(nub) fplus_curry_define_fn_1(all_unique_by_eq) fplus_curry_define_fn_1(all_unique_on) fplus_curry_define_fn_0(all_unique) fplus_curry_define_fn_1(is_strictly_sorted_by) fplus_curry_define_fn_1(is_strictly_sorted_on) fplus_curry_define_fn_0(is_strictly_sorted) fplus_curry_define_fn_1(is_sorted_by) fplus_curry_define_fn_1(is_sorted_on) fplus_curry_define_fn_0(is_sorted) fplus_curry_define_fn_1(is_prefix_of) fplus_curry_define_fn_1(is_suffix_of) fplus_curry_define_fn_1(all_by) fplus_curry_define_fn_0(all) fplus_curry_define_fn_1(all_the_same_by) fplus_curry_define_fn_1(all_the_same_on) fplus_curry_define_fn_0(all_the_same) fplus_curry_define_fn_2(numbers_step) fplus_curry_define_fn_1(numbers) fplus_curry_define_fn_0(singleton_seq) fplus_curry_define_fn_0(all_idxs) fplus_curry_define_fn_0(init) fplus_curry_define_fn_0(tail) fplus_curry_define_fn_0(head) fplus_curry_define_fn_0(last) fplus_curry_define_fn_0(mean_stddev) fplus_curry_define_fn_1(count_occurrences_by) fplus_curry_define_fn_0(count_occurrences) fplus_curry_define_fn_2(lexicographical_less_by) fplus_curry_define_fn_1(lexicographical_less) fplus_curry_define_fn_0(lexicographical_sort) fplus_curry_define_fn_1(replicate) fplus_curry_define_fn_2(instead_of_if) fplus_curry_define_fn_2(instead_of_if_empty) fplus_curry_define_fn_0(is_ok) fplus_curry_define_fn_0(is_error) fplus_curry_define_fn_0(unsafe_get_ok) fplus_curry_define_fn_0(unsafe_get_error) fplus_curry_define_fn_1(ok_with_default) fplus_curry_define_fn_0(ok) fplus_curry_define_fn_0(error) fplus_curry_define_fn_0(to_maybe) fplus_curry_define_fn_1(from_maybe) fplus_curry_define_fn_1(throw_on_error) fplus_curry_define_fn_1(lift_result) fplus_curry_define_fn_2(lift_result_both) fplus_curry_define_fn_2(unify_result) fplus_curry_define_fn_1(and_then_result) fplus_curry_define_fn_1(keep_if) fplus_curry_define_fn_1(drop_if) fplus_curry_define_fn_1(without) fplus_curry_define_fn_1(without_any) fplus_curry_define_fn_1(keep_if_with_idx) fplus_curry_define_fn_1(drop_if_with_idx) fplus_curry_define_fn_1(keep_by_idx) fplus_curry_define_fn_1(drop_by_idx) fplus_curry_define_fn_1(keep_idxs) fplus_curry_define_fn_1(drop_idxs) fplus_curry_define_fn_1(drop_idx) fplus_curry_define_fn_0(justs) fplus_curry_define_fn_0(oks) fplus_curry_define_fn_0(errors) fplus_curry_define_fn_1(trim_left) fplus_curry_define_fn_1(trim_token_left) fplus_curry_define_fn_1(trim_right_by) fplus_curry_define_fn_1(trim_right) fplus_curry_define_fn_1(trim_token_right) fplus_curry_define_fn_1(trim_by) fplus_curry_define_fn_1(trim) fplus_curry_define_fn_1(trim_token) fplus_curry_define_fn_1(adjacent_keep_snd_if) fplus_curry_define_fn_1(adjacent_drop_fst_if) fplus_curry_define_fn_1(adjacent_drop_snd_if) fplus_curry_define_fn_1(adjacent_keep_fst_if) fplus_curry_define_fn_1(apply_to_pair) fplus_curry_define_fn_2(zip_with) fplus_curry_define_fn_3(zip_with_3) fplus_curry_define_fn_4(zip_with_defaults) fplus_curry_define_fn_1(zip) fplus_curry_define_fn_0(unzip) fplus_curry_define_fn_0(fst) fplus_curry_define_fn_0(snd) fplus_curry_define_fn_1(transform_fst) fplus_curry_define_fn_1(transform_snd) fplus_curry_define_fn_2(transform_pair) fplus_curry_define_fn_0(swap_pair_elems) fplus_curry_define_fn_0(swap_pairs_elems) fplus_curry_define_fn_0(adjacent_pairs) fplus_curry_define_fn_0(overlapping_pairs) fplus_curry_define_fn_0(overlapping_pairs_cyclic) fplus_curry_define_fn_0(enumerate) fplus_curry_define_fn_4(inner_product_with) fplus_curry_define_fn_2(inner_product) fplus_curry_define_fn_2(first_mismatch_idx_by) fplus_curry_define_fn_2(first_mismatch_by) fplus_curry_define_fn_2(first_mismatch_idx_on) fplus_curry_define_fn_2(first_mismatch_on) fplus_curry_define_fn_2(first_mismatch_idx) fplus_curry_define_fn_2(first_mismatch) fplus_curry_define_fn_2(first_match_idx_by) fplus_curry_define_fn_2(first_match_by) fplus_curry_define_fn_2(first_match_idx_on) fplus_curry_define_fn_2(first_match_on) fplus_curry_define_fn_2(first_match_idx) fplus_curry_define_fn_2(first_match) fplus_curry_define_fn_2(is_in_interval) fplus_curry_define_fn_2(is_in_interval_around) fplus_curry_define_fn_2(is_in_open_interval) fplus_curry_define_fn_2(is_in_open_interval_around) fplus_curry_define_fn_2(is_in_closed_interval) fplus_curry_define_fn_4(reference_interval) fplus_curry_define_fn_2(clamp) fplus_curry_define_fn_0(is_negative) fplus_curry_define_fn_0(is_positive) fplus_curry_define_fn_0(is_even) fplus_curry_define_fn_0(is_odd) fplus_curry_define_fn_0(abs) fplus_curry_define_fn_1(abs_diff) fplus_curry_define_fn_0(square) fplus_curry_define_fn_0(cube) fplus_curry_define_fn_0(sign) fplus_curry_define_fn_0(sign_with_zero) fplus_curry_define_fn_0(integral_cast_throw) fplus_curry_define_fn_0(integral_cast_clamp) fplus_curry_define_fn_0(round) fplus_curry_define_fn_0(floor) fplus_curry_define_fn_1(floor_to_int_mult) fplus_curry_define_fn_1(ceil_to_int_mult) fplus_curry_define_fn_0(ceil) fplus_curry_define_fn_1(int_power) fplus_curry_define_fn_2(min_2_on) fplus_curry_define_fn_2(max_2_on) fplus_curry_define_fn_1(min_2) fplus_curry_define_fn_1(max_2) fplus_curry_define_fn_0(deg_to_rad) fplus_curry_define_fn_0(rad_to_deg) fplus_curry_define_fn_2(normalize_min_max) fplus_curry_define_fn_2(normalize_mean_stddev) fplus_curry_define_fn_0(standardize) fplus_curry_define_fn_1(histogram_using_intervals) fplus_curry_define_fn_2(generate_consecutive_intervals) fplus_curry_define_fn_3(histogram) fplus_curry_define_fn_1(modulo_chain) fplus_curry_define_fn_2(line_equation) fplus_curry_define_fn_1(generate_by_idx) fplus_curry_define_fn_1(repeat) fplus_curry_define_fn_1(infixes) fplus_curry_define_fn_3(carthesian_product_with_where) fplus_curry_define_fn_2(carthesian_product_with) fplus_curry_define_fn_2(carthesian_product_where) fplus_curry_define_fn_1(carthesian_product) fplus_curry_define_fn_1(carthesian_product_n) fplus_curry_define_fn_1(permutations) fplus_curry_define_fn_1(combinations) fplus_curry_define_fn_1(combinations_with_replacement) fplus_curry_define_fn_0(power_set) fplus_curry_define_fn_2(iterate) fplus_curry_define_fn_1(iterate_maybe) fplus_curry_define_fn_1(adjacent_difference_by) fplus_curry_define_fn_0(adjacent_difference) fplus_curry_define_fn_0(rotate_left) fplus_curry_define_fn_0(rotate_right) fplus_curry_define_fn_0(rotations_left) fplus_curry_define_fn_0(rotations_right) fplus_curry_define_fn_2(fill_left) fplus_curry_define_fn_2(fill_right) fplus_curry_define_fn_0(inits) fplus_curry_define_fn_0(tails) fplus_curry_define_fn_1(find_first_by) fplus_curry_define_fn_1(find_last_by) fplus_curry_define_fn_1(find_first_idx_by) fplus_curry_define_fn_1(find_last_idx_by) fplus_curry_define_fn_1(find_first_idx) fplus_curry_define_fn_1(find_last_idx) fplus_curry_define_fn_1(find_all_idxs_by) fplus_curry_define_fn_1(find_all_idxs_of) fplus_curry_define_fn_1(find_all_instances_of_token) fplus_curry_define_fn_1(find_all_instances_of_token_non_overlapping) fplus_curry_define_fn_1(find_first_instance_of_token) fplus_curry_define_fn_1(set_includes) fplus_curry_define_fn_1(unordered_set_includes) fplus_curry_define_fn_1(set_merge) fplus_curry_define_fn_1(unordered_set_merge) fplus_curry_define_fn_1(set_intersection) fplus_curry_define_fn_1(unordered_set_intersection) fplus_curry_define_fn_1(set_is_disjoint) fplus_curry_define_fn_1(unordered_set_is_disjoint) fplus_curry_define_fn_1(set_difference) fplus_curry_define_fn_1(unordered_set_difference) fplus_curry_define_fn_1(set_symmetric_difference) fplus_curry_define_fn_1(unordered_set_symmetric_difference) fplus_curry_define_fn_0(sets_intersection) fplus_curry_define_fn_0(unordered_sets_intersection) fplus_curry_define_fn_1(any_by) fplus_curry_define_fn_0(any) fplus_curry_define_fn_1(none_by) fplus_curry_define_fn_0(none) fplus_curry_define_fn_1(minimum_idx_by) fplus_curry_define_fn_1(minimum_idx_by_maybe) fplus_curry_define_fn_1(maximum_idx_by) fplus_curry_define_fn_1(maximum_idx_by_maybe) fplus_curry_define_fn_0(minimum_idx) fplus_curry_define_fn_0(minimum_idx_maybe) fplus_curry_define_fn_0(maximum_idx) fplus_curry_define_fn_0(maximum_idx_maybe) fplus_curry_define_fn_1(minimum_idx_on) fplus_curry_define_fn_1(minimum_idx_on_maybe) fplus_curry_define_fn_1(maximum_idx_on) fplus_curry_define_fn_1(maximum_idx_on_maybe) fplus_curry_define_fn_1(minimum_by) fplus_curry_define_fn_1(minimum_by_maybe) fplus_curry_define_fn_1(maximum_by) fplus_curry_define_fn_1(maximum_by_maybe) fplus_curry_define_fn_0(minimum) fplus_curry_define_fn_0(minimum_maybe) fplus_curry_define_fn_0(maximum) fplus_curry_define_fn_0(maximum_maybe) fplus_curry_define_fn_1(minimum_on) fplus_curry_define_fn_1(minimum_on_maybe) fplus_curry_define_fn_1(maximum_on) fplus_curry_define_fn_1(maximum_on_maybe) fplus_curry_define_fn_0(mean) fplus_curry_define_fn_0(mean_obj_div_size_t) fplus_curry_define_fn_0(mean_obj_div_double) fplus_curry_define_fn_0(mean_using_doubles) fplus_curry_define_fn_0(median) fplus_curry_define_fn_1(all_unique_by_less) fplus_curry_define_fn_0(all_unique_less) fplus_curry_define_fn_1(is_infix_of) fplus_curry_define_fn_1(is_subsequence_of) fplus_curry_define_fn_1(count_if) fplus_curry_define_fn_1(count) fplus_curry_define_fn_1(is_unique_in_by) fplus_curry_define_fn_1(is_unique_in) fplus_curry_define_fn_1(is_permutation_of) fplus_curry_define_fn_1(fill_pigeonholes_to) fplus_curry_define_fn_0(fill_pigeonholes) fplus_curry_define_fn_1(fill_pigeonholes_bool_to) fplus_curry_define_fn_0(fill_pigeonholes_bool) fplus_curry_define_fn_0(present_in_all) fplus_curry_define_fn_1(elem_at_idx_or_nothing) fplus_curry_define_fn_2(elem_at_idx_or_constant) fplus_curry_define_fn_1(elem_at_idx_or_replicate) fplus_curry_define_fn_1(elem_at_idx_or_wrap) fplus_curry_define_fn_2(extrapolate_replicate) fplus_curry_define_fn_2(extrapolate_wrap) fplus_curry_define_fn_1(elem_at_float_idx) fplus_curry_define_fn_0(pairs_to_map) fplus_curry_define_fn_0(pairs_to_map_grouped) fplus_curry_define_fn_0(map_to_pairs) fplus_curry_define_fn_1(transform_map_values) fplus_curry_define_fn_2(map_union_with) fplus_curry_define_fn_1(map_union) fplus_curry_define_fn_0(get_map_keys) fplus_curry_define_fn_0(get_map_values) fplus_curry_define_fn_0(swap_keys_and_values) fplus_curry_define_fn_1(create_map) fplus_curry_define_fn_1(create_map_with) fplus_curry_define_fn_1(create_unordered_map) fplus_curry_define_fn_1(create_unordered_map_with) fplus_curry_define_fn_1(get_from_map) fplus_curry_define_fn_1(get_from_map_unsafe) fplus_curry_define_fn_2(get_from_map_with_def) fplus_curry_define_fn_1(get_first_from_map) fplus_curry_define_fn_1(get_first_from_map_unsafe) fplus_curry_define_fn_2(get_first_from_map_with_def) fplus_curry_define_fn_1(map_contains) fplus_curry_define_fn_1(map_keep_if) fplus_curry_define_fn_1(map_drop_if) fplus_curry_define_fn_1(map_keep) fplus_curry_define_fn_1(map_drop) fplus_curry_define_fn_1(map_keep_if_value) fplus_curry_define_fn_1(map_drop_if_value) fplus_curry_define_fn_1(map_keep_values) fplus_curry_define_fn_1(map_drop_values) fplus_curry_define_fn_1(map_pluck) fplus_curry_define_fn_1(choose) fplus_curry_define_fn_2(choose_by) fplus_curry_define_fn_1(choose_lazy) fplus_curry_define_fn_2(choose_by_lazy) fplus_curry_define_fn_1(choose_def) fplus_curry_define_fn_2(choose_by_def) fplus_curry_define_fn_1(choose_def_lazy) fplus_curry_define_fn_2(choose_by_def_lazy) fplus_curry_define_fn_1(group_by) fplus_curry_define_fn_1(group_on) fplus_curry_define_fn_1(group_on_labeled) fplus_curry_define_fn_0(group) fplus_curry_define_fn_1(group_globally_by) fplus_curry_define_fn_1(group_globally_on) fplus_curry_define_fn_1(group_globally_on_labeled) fplus_curry_define_fn_0(group_globally) fplus_curry_define_fn_1(cluster_by) fplus_curry_define_fn_2(split_by) fplus_curry_define_fn_1(split_by_keep_separators) fplus_curry_define_fn_2(split) fplus_curry_define_fn_2(split_one_of) fplus_curry_define_fn_1(split_keep_separators) fplus_curry_define_fn_1(split_at_idx) fplus_curry_define_fn_2(insert_at_idx) fplus_curry_define_fn_1(partition) fplus_curry_define_fn_1(split_at_idxs) fplus_curry_define_fn_1(split_every) fplus_curry_define_fn_2(split_by_token) fplus_curry_define_fn_1(run_length_encode_by) fplus_curry_define_fn_0(run_length_encode) fplus_curry_define_fn_0(run_length_decode) fplus_curry_define_fn_1(span) fplus_curry_define_fn_2(divvy) fplus_curry_define_fn_1(aperture) fplus_curry_define_fn_1(stride) fplus_curry_define_fn_1(winsorize) fplus_curry_define_fn_1(separate_on) fplus_curry_define_fn_0(separate) fplus_curry_define_fn_1(transform_with_idx) fplus_curry_define_fn_1(transform_and_keep_justs) fplus_curry_define_fn_1(transform_and_keep_oks) fplus_curry_define_fn_1(transform_and_concat) fplus_curry_define_fn_1(replicate_elems) fplus_curry_define_fn_0(interleave) fplus_curry_define_fn_0(transpose) fplus_curry_define_fn_1(shuffle) fplus_curry_define_fn_2(sample) fplus_curry_define_fn_1(random_element) fplus_curry_define_fn_2(random_elements) fplus_curry_define_fn_1(apply_functions) fplus_curry_define_fn_2(apply_function_n_times) fplus_curry_define_fn_1(transform_parallelly) fplus_curry_define_fn_2(transform_parallelly_n_threads) fplus_curry_define_fn_2(reduce_parallelly) fplus_curry_define_fn_3(reduce_parallelly_n_threads) fplus_curry_define_fn_1(reduce_1_parallelly) fplus_curry_define_fn_2(reduce_1_parallelly_n_threads) fplus_curry_define_fn_1(keep_if_parallelly) fplus_curry_define_fn_2(keep_if_parallelly_n_threads) fplus_curry_define_fn_3(transform_reduce) fplus_curry_define_fn_2(transform_reduce_1) fplus_curry_define_fn_3(transform_reduce_parallelly) fplus_curry_define_fn_4(transform_reduce_parallelly_n_threads) fplus_curry_define_fn_2(transform_reduce_1_parallelly) fplus_curry_define_fn_3(transform_reduce_1_parallelly_n_threads) fplus_curry_define_fn_1(read_value_with_default) fplus_curry_define_fn_2(replace_if) fplus_curry_define_fn_2(replace_elem_at_idx) fplus_curry_define_fn_2(replace_elems) fplus_curry_define_fn_2(replace_tokens) fplus_curry_define_fn_0(show) fplus_curry_define_fn_3(show_cont_with_frame_and_newlines) fplus_curry_define_fn_3(show_cont_with_frame) fplus_curry_define_fn_1(show_cont_with) fplus_curry_define_fn_0(show_cont) fplus_curry_define_fn_0(show_maybe) fplus_curry_define_fn_0(show_result) fplus_curry_define_fn_2(show_float) fplus_curry_define_fn_3(show_float_fill_left) fplus_curry_define_fn_2(show_fill_left) fplus_curry_define_fn_2(show_fill_right) fplus_curry_define_fn_0(is_letter_or_digit) fplus_curry_define_fn_0(is_whitespace) fplus_curry_define_fn_0(is_line_break) fplus_curry_define_fn_0(clean_newlines) fplus_curry_define_fn_1(split_words) fplus_curry_define_fn_1(split_lines) fplus_curry_define_fn_0(trim_whitespace_left) fplus_curry_define_fn_0(trim_whitespace_right) fplus_curry_define_fn_0(trim_whitespace) fplus_curry_define_fn_0(to_lower_case) fplus_curry_define_fn_1(to_lower_case_loc) fplus_curry_define_fn_0(to_upper_case) fplus_curry_define_fn_1(to_upper_case_loc) fplus_curry_define_fn_2(to_string_fill_left) fplus_curry_define_fn_2(to_string_fill_right) fplus_curry_define_fn_1(trees_from_sequence) fplus_curry_define_fn_1(are_trees_equal) fplus_curry_define_fn_0(tree_size) fplus_curry_define_fn_0(tree_depth) fplus_curry_define_fn_0(flatten_tree_depth_first) fplus_curry_define_fn_0(flatten_tree_breadth_first) fplus_curry_define_fn_0(show_timed) fplus_curry_define_fn_0(make_timed_function) fplus_curry_define_fn_0(make_timed_void_function) libfplus-0.2.13/include/fplus/extrapolate.hpp000066400000000000000000000115741376322245400212520ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include namespace fplus { // API search type: elem_at_idx_or_nothing : (Int, [a]) -> Maybe a // fwd bind count: 1 // Return nth element of a sequence. // Returns nothing if index is outside of xs. template maybe elem_at_idx_or_nothing(signed int idx, const Container& xs) { if (idx < 0 || idx >= static_cast(size_of_cont(xs))) { return {}; } auto it = std::begin(xs); internal::advance_iterator(it, static_cast(idx)); return *it; } // API search type: elem_at_idx_or_constant : (a, Int, [a]) -> a // fwd bind count: 2 // Return nth element of a sequence. // Interpolate outside of sequence with a constant value. // iiiiii|abcdefgh|iiiiiii template T elem_at_idx_or_constant(const T& c, signed int idx, const Container& xs) { if (idx < 0 || idx >= static_cast(size_of_cont(xs))) { return c; } auto it = std::begin(xs); internal::advance_iterator(it, static_cast(idx)); return *it; } // API search type: elem_at_idx_or_replicate : (Int, [a]) -> a // fwd bind count: 1 // Return nth element of a sequence. // Interpolate outside of sequence by replicating the nearest inside value. // aaaaaa|abcdefgh|hhhhhhh // xs must be non-empty. template T elem_at_idx_or_replicate(signed int idx, const Container& xs) { assert(is_not_empty(xs)); if (idx < 0) { return xs.front(); } if (idx >= static_cast(size_of_cont(xs))) { return xs.back(); } auto it = std::begin(xs); internal::advance_iterator(it, static_cast(idx)); return *it; } // API search type: elem_at_idx_or_wrap : (Int, [a]) -> a // fwd bind count: 1 // Return nth element of a sequence. // Interpolate outside of sequence by replicating the sequence. // For cyclic element access. // cdefgh|abcdefgh|abcdefg // xs must be non-empty. template T elem_at_idx_or_wrap(signed int idx, const Container& xs) { assert(is_not_empty(xs)); const signed int cont_size = static_cast(size_of_cont(xs)); if (idx < 0) idx = cont_size - (std::abs(idx) % cont_size); else idx = idx % cont_size; auto it = std::begin(xs); internal::advance_iterator(it, static_cast(idx)); return *it; } // API search type: extrapolate_replicate : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Extrapolate a sequence by replicating the border values. // count_begin determines the number of elements to be prepended. // count_end determines the number of elements to be appended. // aaaaaa|abcdefgh|hhhhhhh // xs must be non-empty. template Container extrapolate_replicate(std::size_t count_begin, std::size_t count_end, const Container& xs) { assert(is_not_empty(xs)); Container ys; const auto xs_size = size_of_cont(xs); internal::prepare_container(ys, xs_size + count_begin + count_end); auto it = internal::get_back_inserter(ys); const signed int idx_end = static_cast(xs_size + count_end); const signed int idx_start = -static_cast(count_begin); for (signed int idx = idx_start; idx < idx_end; ++idx) { *it = elem_at_idx_or_replicate(idx, xs); } return ys; } // API search type: extrapolate_wrap : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Extrapolate a sequence by accessing the elements in cyclic fashion. // count_begin determines the number of elements to be prepended. // count_end determines the number of elements to be appended. // cdefgh|abcdefgh|abcdefg // xs must be non-empty. template Container extrapolate_wrap(std::size_t count_begin, std::size_t count_end, const Container& xs) { assert(is_not_empty(xs)); Container ys; const auto xs_size = size_of_cont(xs); internal::prepare_container(ys, xs_size + count_begin + count_end); auto it = internal::get_back_inserter(ys); const signed int idx_end = static_cast(xs_size + count_end); const signed int idx_start = -static_cast(count_begin); for (signed int idx = idx_start; idx < idx_end; ++idx) { *it = elem_at_idx_or_wrap(idx, xs); } return ys; } } // namespace fplus libfplus-0.2.13/include/fplus/filter.hpp000066400000000000000000000435131376322245400202050ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include namespace fplus { namespace internal { template Container keep_if(internal::reuse_container_t, Pred pred, Container&& xs) { internal::check_unary_predicate_for_container(); xs.erase(std::remove_if( std::begin(xs), std::end(xs), logical_not(pred)), std::end(xs)); return std::forward(xs); } template Container keep_if(internal::create_new_container_t, Pred pred, const Container& xs) { internal::check_unary_predicate_for_container(); Container result; auto it = internal::get_back_inserter(result); std::copy_if(std::begin(xs), std::end(xs), it, pred); return result; } } // namespace internal // API search type: keep_if : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Keep the elements of a sequence fulfilling a predicate. // keep_if(is_even, [1, 2, 3, 2, 4, 5]) == [2, 2, 4] // Also known as filter. template > ContainerOut keep_if(Pred pred, Container&& xs) { return internal::keep_if(internal::can_reuse_v{}, pred, std::forward(xs)); } // API search type: drop_if : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Drop the elements of a sequence fulfilling a predicate. // drop_if(is_even, [1, 2, 3, 2, 4, 5]) == [1, 3, 5] // Also known as reject. template > ContainerOut drop_if(Pred pred, Container&& xs) { return keep_if(logical_not(pred), std::forward(xs)); } // API search type: without : (a, [a]) -> [a] // fwd bind count: 1 // Keep all elements a sequence not equal to elem. // without(0, [1, 0, 0, 5, 3, 0, 1]) == [1, 5, 3, 1] template > ContainerOut without(T elem, Container&& xs) { return drop_if(is_equal_to(elem), std::forward(xs)); } // API search type: without_any : (a, [a]) -> [a] // fwd bind count: 1 // Keep all elements a sequence not present in elems. // without([0, 1], [1, 0, 0, 5, 3, 0, 1]) == [5, 3] template > ContainerOut without_any(const ContainerElems& elems, Container&& xs) { static_assert(std::is_same< typename ContainerElems::value_type, typename std::remove_reference::type::value_type>::value, "Container values must be of the same type."); const auto pred = bind_2nd_of_2(is_elem_of, elems); return drop_if(pred, std::forward(xs)); } // API search type: keep_if_with_idx : (((Int, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Keep the elements of a sequence fulfilling a predicate. // Predicate takes index and value. // All elements fulfilling the predicate are kept. template Container keep_if_with_idx(Pred pred, const Container& xs) { internal::check_index_with_type_predicate_for_container(); Container ys; auto it = internal::get_back_inserter(ys); std::size_t idx = 0; for (const auto& x : xs) { if (internal::invoke(pred, idx++, x)) *it = x; } return ys; } // API search type: drop_if_with_idx : (((Int, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Drop the elements of a sequence fulfilling a predicate. // Predicate takes index and value. // All elements fulfilling the predicate are skipped. template Container drop_if_with_idx(Pred pred, const Container& xs) { internal::check_index_with_type_predicate_for_container(); const auto inverse_pred = [pred](auto idx, const auto& x) { return !internal::invoke(pred, idx, x); }; return keep_if_with_idx(inverse_pred, xs); } namespace internal { template Container keep_by_idx(internal::reuse_container_t, UnaryPredicate pred, Container&& xs) { auto itOut = std::begin(xs); std::size_t i = 0; for (auto it = std::begin(xs); it != std::end(xs); ++it) { if (internal::invoke(pred, i++)) *itOut++ = std::move(*it); } xs.erase(itOut, std::end(xs)); return std::forward(xs); } template Container keep_by_idx(internal::create_new_container_t, UnaryPredicate pred, const Container& xs) { Container ys = xs; return internal::keep_by_idx(internal::reuse_container_t(), pred, std::move(ys)); } } // namespace internal // API search type: keep_by_idx : ((Int -> Bool), [a]) -> [a] // fwd bind count: 1 // Keep the elements of a sequence with an index fulfilling a predicate. // Predicate takes an index and decides if an element is kept. template > ContainerOut keep_by_idx(UnaryPredicate pred, Container&& xs) { internal::check_unary_predicate_for_type(); return internal::keep_by_idx(internal::can_reuse_v{}, pred, std::forward(xs)); } // API search type: drop_by_idx : ((Int -> Bool), [a]) -> [a] // fwd bind count: 1 // Drop the elements of a sequence with an index fulfilling a predicate. // Predicate takes an index and decides if an element is dropped. template > ContainerOut drop_by_idx(UnaryPredicate pred, Container&& xs) { internal::check_unary_predicate_for_type(); return keep_by_idx(logical_not(pred), std::forward(xs)); } // API search type: keep_idxs : ([Int], [a]) -> [a] // fwd bind count: 1 // Keep the elements of a sequence with an index present in idxs_to_keep. // keep_idxs([2,5], [1,2,3,4,5,6,7]) == [3,6] template Container keep_idxs(const ContainerIdxs& idxs_to_keep, const Container& xs) { static_assert(std::is_same::value, "Indices must be std::size_t"); auto idxs_left = convert_container>( unique(sort(idxs_to_keep))); Container ys; auto it = internal::get_back_inserter(ys); std::size_t idx = 0; for (const auto& x : xs) { if (!idxs_left.empty() && idxs_left.front() == idx) { idxs_left.pop_front(); *it = x; } ++idx; } return ys; } // API search type: drop_idxs : ([Int], [a]) -> [a] // fwd bind count: 1 // Drop the elements of a sequence with an index present in idxs_to_keep. // drop_idxs([2,5], [1,2,3,4,5,6,7]) == [1,2,4,5,7] template Container drop_idxs(const ContainerIdxs& idxs_to_drop, const Container& xs) { static_assert(std::is_same::value, "Indices must be std::size_t"); auto idxs_left = convert_container>( unique(sort(idxs_to_drop))); Container ys; auto it = internal::get_back_inserter(ys); std::size_t idx = 0; for (const auto& x : xs) { if (idxs_left.empty() || idxs_left.front() != idx) { *it = x; } else { if (!idxs_left.empty()) { idxs_left.pop_front(); } } ++idx; } return ys; } // API search type: drop_idx : (Int, [a]) -> [a] // fwd bind count: 1 // Remove the element at a specific index from a sequence. // drop_idx(2, [1,2,3,4,5,6,7]) == [1,2,4,5,6,7] template Container drop_idx(std::size_t idx, const Container& xs) { return drop_by_idx(is_equal_to(idx), xs); } // API search type: justs : [Maybe a] -> [a] // fwd bind count: 0 // From a Container filled with Maybe the nothings are dropped // and the values inside the justs are returned in a new container. template ::type> ContainerOut justs(const ContainerIn& xs) { typedef typename ContainerIn::value_type::type T; auto justsInMaybes = keep_if(is_just, xs); ContainerOut ys; internal::prepare_container(ys, fplus::size_of_cont(justsInMaybes)); auto itOut = internal::get_back_inserter(ys); std::transform(std::begin(justsInMaybes), std::end(justsInMaybes), itOut, unsafe_get_just); return ys; } // API search type: oks : [Result a b] -> [a] // fwd bind count: 0 // From a Container filled with Result the errors are dropped // and the values inside the ok are returned in a new container. template ::type> ContainerOut oks(const ContainerIn& xs) { typedef typename ContainerIn::value_type::ok_t Ok; typedef typename ContainerIn::value_type::error_t Error; auto oksInResults = keep_if(is_ok, xs); ContainerOut ys; internal::prepare_container(ys, fplus::size_of_cont(oksInResults)); auto itOut = internal::get_back_inserter(ys); std::transform(std::begin(oksInResults), std::end(oksInResults), itOut, unsafe_get_ok); return ys; } // API search type: errors : [Result a b] -> [b] // fwd bind count: 0 // From a Container filled with Result the oks are dropped // and the values inside the errors are returned in a new container. template ::type> ContainerOut errors(const ContainerIn& xs) { typedef typename ContainerIn::value_type::ok_t Ok; typedef typename ContainerIn::value_type::error_t Error; auto errorsInResults = keep_if(is_error, xs); ContainerOut ys; internal::prepare_container(ys, fplus::size_of_cont(errorsInResults)); auto itOut = internal::get_back_inserter(ys); std::transform(std::begin(errorsInResults), std::end(errorsInResults), itOut, unsafe_get_error); return ys; } // API search type: trim_left : (a, [a]) -> [a] // fwd bind count: 1 // Remove elements from the left as long as they equal x. // trim_left('_', "___abc__") == "abc__" // trim_left(0, [0,0,0,5,6,7,8,6,4]) == [5,6,7,8,6,4] template Container trim_left(const T& x, const Container& xs) { return drop_while(is_equal_to(x), xs); } // API search type: trim_token_left : ([a], [a]) -> [a] // fwd bind count: 1 // Remove elements from the left as long as they match token. // trim_token_left([0,1,2], [0,1,2,0,1,2,7,5,9]) == [7,5,9] template Container trim_token_left(const Container& token, const Container& xs) { auto result = xs; while (is_prefix_of(token, result)) { result = get_segment(size_of_cont(token), size_of_cont(result), result); } return result; } // API search type: trim_right_by : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Remove elements from the left as long as p is fulfilled. // trim_right_by(is_even, [0,2,4,5,6,7,8,6,4]) == [0,2,4,5,6,7] template Container trim_right_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return reverse(drop_while(p, reverse(xs))); } // API search type: trim_right : (a, [a]) -> [a] // fwd bind count: 1 // Remove elements from the left as long as they equal x. // trim_right('_', "___abc__") == "___abc" // trim_right(4, [0,2,4,5,6,7,8,4,4]) == [0,2,4,5,6,7,8] template Container trim_right(const T& x, const Container& xs) { return trim_right_by(is_equal_to(x), xs); } // API search type: trim_token_right : ([a], [a]) -> [a] // fwd bind count: 1 // Remove elements from the right as long as they match token. // trim_token_right([0,1,2], [7,5,9,0,1,2,0,1,2]) == [7,5,9] template Container trim_token_right(const Container& token, const Container& xs) { return reverse(trim_token_left(reverse(token), reverse(xs))); } // API search type: trim_by : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Remove elements from the left and right as long as p is fulfilled. // trim_by(is_even, [0,2,4,5,6,7,8,6,4]) == [5,6,7] template Container trim_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return trim_right_by(p, drop_while(p, xs)); } // API search type: trim : (a, [a]) -> [a] // fwd bind count: 1 // Remove elements from the left and right as long as they equal x. // trim('_', "___abc__") == "abc" // trim(0, [0,2,4,5,6,7,8,0,0]) == [2,4,5,6,7,8] template Container trim(const T& x, const Container& xs) { return trim_right(x, trim_left(x, xs)); } // API search type: trim_token : ([a], [a]) -> [a] // fwd bind count: 1 // Remove elements from the left and right as long as they match token. // trim_token([0,1], [0,1,7,8,9,0,1]) == [7,8,9] template Container trim_token(const Container& token, const Container& xs) { return trim_token_right(token, trim_token_left(token, xs)); } // API search type: adjacent_keep_snd_if : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // For each pair of adjacent elements in a source sequence, // evaluate the specified binary predicate. // If the predicate evaluates to false, // the second element of the pair is removed from the result sequence; // otherwise, it is included. // The first element in the source sequence is always included. // Also known as adjacent_filter. template Container adjacent_keep_snd_if(BinaryPredicate p, const Container& xs) { if (is_empty(xs)) { return {}; } internal::check_binary_predicate_for_container(); Container result; auto it = internal::get_back_inserter(result); auto it_in = std::begin(xs); *it = *it_in; while (internal::add_to_iterator(it_in) != std::end(xs)) { if (p(*it_in, *internal::add_to_iterator(it_in))) { *it = *internal::add_to_iterator(it_in); } internal::advance_iterator(it_in, 1); } return result; } // API search type: adjacent_drop_fst_if : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // For each pair of adjacent elements in a source sequence, // evaluate the specified binary predicate. // If the predicate evaluates to true, // the first element of the pair is removed from the result sequence; // otherwise, it is included. // The last element in the source sequence is always included. // Also known as adjacent_remove_if. template Container adjacent_drop_fst_if(BinaryPredicate p, const Container& xs) { if (is_empty(xs)) { return {}; } internal::check_binary_predicate_for_container(); Container result; auto it = internal::get_back_inserter(result); auto it_in = std::begin(xs); while (internal::add_to_iterator(it_in) != std::end(xs)) { if (!internal::invoke(p, *it_in, *internal::add_to_iterator(it_in))) { *it = *it_in; } internal::advance_iterator(it_in, 1); } *it = *it_in; return result; } // API search type: adjacent_drop_snd_if : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // For each pair of adjacent elements in a source sequence, // evaluate the specified binary predicate. // If the predicate evaluates to true, // the second element of the pair is removed from the result sequence; // otherwise, it is included. // The first element in the source sequence is always included. template Container adjacent_drop_snd_if(BinaryPredicate p, const Container& xs) { typedef typename Container::value_type T; const auto not_p = [&p](const T& x, const T& y) -> bool { return !internal::invoke(p, x, y); }; return adjacent_keep_snd_if(not_p, xs); } // API search type: adjacent_keep_fst_if : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // For each pair of adjacent elements in a source sequence, // evaluate the specified binary predicate. // If the predicate evaluates to false, // the first element of the pair is removed from the result sequence; // otherwise, it is included. // The last element in the source sequence is always included. template Container adjacent_keep_fst_if(BinaryPredicate p, const Container& xs) { typedef typename Container::value_type T; const auto not_p = [&p](const T& x, const T& y) -> bool { return !internal::invoke(p, x, y); }; return adjacent_drop_fst_if(not_p, xs); } } // namespace fplus libfplus-0.2.13/include/fplus/fplus.hpp000066400000000000000000000024361376322245400200500ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include libfplus-0.2.13/include/fplus/function_traits.hpp000066400000000000000000000311121376322245400221230ustar00rootroot00000000000000//-------------------------------------- // utils/traits: Additional type traits //-------------------------------------- // // Copyright kennytm (auraHT Ltd.) 2011. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) /** ```` --- Additional type traits ================================================= This module provides additional type traits and related functions, missing from the standard library. */ #ifndef TRAITS_HPP_9ALQFEFX7TO #define TRAITS_HPP_9ALQFEFX7TO 1 #include #include #include #include #include namespace fplus { // source: https://github.com/kennytm/utils namespace utils { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #endif /** .. macro:: DECLARE_HAS_TYPE_MEMBER(member_name) This macro declares a template ``has_member_name`` which will check whether a type member ``member_name`` exists in a particular type. Example:: DECLARE_HAS_TYPE_MEMBER(result_type) ... printf("%d\n", has_result_type< std::plus >::value); // ^ prints '1' (true) printf("%d\n", has_result_type< double(*)() >::value); // ^ prints '0' (false) */ #define DECLARE_HAS_TYPE_MEMBER(member_name) \ template \ struct has_##member_name \ { enum { value = false }; }; \ template \ struct has_##member_name::type> \ { enum { value = true }; }; /** .. type:: struct utils::function_traits Obtain compile-time information about a function object *F*. This template currently supports the following types: * Normal function types (``R(T...)``), function pointers (``R(*)(T...)``) and function references (``R(&)(T...)`` and ``R(&&)(T...)``). * Member functions (``R(C::*)(T...)``) * ``std::function`` * Type of lambda functions, and any other types that has a unique ``operator()``. * Type of ``std::mem_fn`` (only for GCC's libstdc++ and LLVM's libc++). Following the C++ spec, the first argument will be a raw pointer. */ template struct function_traits : public function_traits {}; namespace xx_impl { template struct memfn_type { typedef typename std::conditional< std::is_const::value, typename std::conditional< std::is_volatile::value, R (C::*)(A...) const volatile, R (C::*)(A...) const >::type, typename std::conditional< std::is_volatile::value, R (C::*)(A...) volatile, R (C::*)(A...) >::type >::type type; }; } template struct function_traits { /** .. type:: type result_type The type returned by calling an instance of the function object type *F*. */ typedef ReturnType result_type; /** .. type:: type function_type The function type (``R(T...)``). */ typedef ReturnType function_type(Args...); /** .. type:: type member_function_type The member function type for an *OwnerType* (``R(OwnerType::*)(T...)``). */ template using member_function_type = typename xx_impl::memfn_type< typename std::remove_pointer::type>::type, ReturnType, Args... >::type; /** .. data:: static const size_t arity Number of arguments the function object will take. */ enum { arity = sizeof...(Args) }; /** .. type:: type arg::type The type of the *n*-th argument. */ template struct arg { typedef typename std::tuple_element>::type type; }; }; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits { typedef ClassType& owner_type; }; template struct function_traits : public function_traits { typedef const ClassType& owner_type; }; template struct function_traits : public function_traits { typedef volatile ClassType& owner_type; }; template struct function_traits : public function_traits { typedef const volatile ClassType& owner_type; }; template struct function_traits> : public function_traits {}; #if defined(_GLIBCXX_FUNCTIONAL) #define MEM_FN_SYMBOL_XX0SL7G4Z0J std::_Mem_fn #elif defined(_LIBCPP_FUNCTIONAL) #define MEM_FN_SYMBOL_XX0SL7G4Z0J std::__mem_fn #endif #ifdef MEM_FN_SYMBOL_XX0SL7G4Z0J template struct function_traits> : public function_traits {}; template struct function_traits> : public function_traits {}; template struct function_traits> : public function_traits {}; template struct function_traits> : public function_traits {}; template struct function_traits> : public function_traits {}; #undef MEM_FN_SYMBOL_XX0SL7G4Z0J #endif template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; #define FORWARD_RES_8QR485JMSBT \ typename std::conditional< \ std::is_lvalue_reference::value, \ T&, \ typename std::remove_reference::type&& \ >::type /** .. function:: auto utils::forward_like(T&& t) noexcept Forward the reference *t* like the type of *Like*. That means, if *Like* is an lvalue (reference), this function will return an lvalue reference of *t*. Otherwise, if *Like* is an rvalue, this function will return an rvalue reference of *t*. This is mainly used to propagate the expression category (lvalue/rvalue) of a member of *Like*, generalizing ``std::forward``. */ template FORWARD_RES_8QR485JMSBT forward_like(T&& input) noexcept { return static_cast(input); } #undef FORWARD_RES_8QR485JMSBT /** .. type:: struct utils::copy_cv Copy the CV qualifier between the two types. For example, ``utils::copy_cv::type`` will become ``const double``. */ template struct copy_cv { private: typedef typename std::remove_cv::type raw_To; typedef typename std::conditional::value, const raw_To, raw_To>::type const_raw_To; public: /** .. type:: type type Result of cv-copying. */ typedef typename std::conditional::value, volatile const_raw_To, const_raw_To>::type type; }; /** .. type:: struct utils::pointee Returns the type by derefering an instance of *T*. This is a generalization of ``std::remove_pointer``, that it also works with iterators. */ template struct pointee { /** .. type:: type type Result of dereferencing. */ typedef typename std::remove_reference())>::type type; }; /** .. function:: std::add_rvalue_reference::type utils::rt_val() noexcept Returns a value of type *T*. It is guaranteed to do nothing and will not throw a compile-time error, but using the returned result will cause undefined behavior. */ template typename std::add_rvalue_reference::type rt_val() noexcept { return std::move(*static_cast(nullptr)); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif } } namespace fplus { namespace internal { template struct is_std_function : std::false_type { }; template struct is_std_function> : std::true_type { }; // Those traits are needed to not perform arity checks on a generic-lambd // or a templated/overloaded operator() template struct has_function_traits : std::false_type { }; // There is a bug with GCC 7 when a std::function is passed as T. // It produces an ambiguous call between this one and the std::function overload // It's related to our void_t implementation, the C++14 compatible version does not // work, whereas the C++17 one does... // // So, help GCC a bit with is_std_function template struct has_function_traits::value, void_t>> : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits> : std::true_type { }; } } #endif libfplus-0.2.13/include/fplus/fwd.hpp000066400000000000000000000067441376322245400175050ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once namespace fplus { namespace fwd { // Partial currying. // Allow to generically bind all but parameters except the last one. // The lambda paramter ist named fplus_fwd_x instead of x // because gcc can produce unjustified shadow warnings. see: // http://stackoverflow.com/questions/41208811/parameter-of-returned-generic-lambda-allegedly-shadows-parameter-of-free-functio #define fplus_fwd_define_fn_0(fplus_fwd_define_fn_0_name) \ inline auto fplus_fwd_define_fn_0_name() \ { \ return [](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_0_name(std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_define_fn_1(fplus_fwd_define_fn_1_name) \ template \ auto fplus_fwd_define_fn_1_name(P1 p1) \ { \ return [p1](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_1_name(p1, std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_define_fn_2(fplus_fwd_define_fn_2_name) \ template \ auto fplus_fwd_define_fn_2_name(P1 p1, P2 p2) \ { \ return [p1, p2](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_2_name(p1, p2, std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_define_fn_3(fplus_fwd_define_fn_3_name) \ template \ auto fplus_fwd_define_fn_3_name(P1 p1, P2 p2, P3 p3) \ { \ return [p1, p2, p3](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_3_name(p1, p2, p3, std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_define_fn_4(fplus_fwd_define_fn_4_name) \ template \ auto fplus_fwd_define_fn_4_name(P1 p1, P2 p2, P3 p3, P4 p4) \ { \ return [p1, p2, p3, p4](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_4_name(p1, p2, p3, p4, std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_flip_define_fn_1(fplus_fwd_flip_define_fn_1_name) \ namespace flip \ { \ template \ auto fplus_fwd_flip_define_fn_1_name(P2 p2) \ { \ return [p2](auto&& fplus_fwd_flip_x) \ { \ return fplus::fplus_fwd_flip_define_fn_1_name(std::forward(fplus_fwd_flip_x), p2); \ }; \ } \ } // namespace flip namespace internal { template struct compose_helper{ compose_helper(F f, G g) : f_(f), g_(g) {} template decltype(auto) operator()(X&& x) const { return g_(f_(std::forward(x))); } private: F f_; G g_; }; } // namespace internal template auto compose(F f, G g) { return internal::compose_helper {f, g}; } template auto compose(F1 f, Fs ... args) { return compose(f, compose(args...)); } template auto apply(X&& x, Fs ... args) { return compose(args...)(std::forward(x)); } template auto apply(X&& x, F f) { return f(std::forward(x)); } #include "fwd_instances.autogenerated_defines" } // namespace fwd } // namespace fplus libfplus-0.2.13/include/fplus/fwd_instances.autogenerated_defines000066400000000000000000000662561376322245400253150ustar00rootroot00000000000000// THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT EDIT. fplus_fwd_define_fn_0(identity) fplus_fwd_define_fn_1(is_equal) fplus_fwd_define_fn_1(is_not_equal) fplus_fwd_define_fn_1(is_less) fplus_fwd_define_fn_1(is_less_or_equal) fplus_fwd_define_fn_1(is_greater) fplus_fwd_define_fn_1(is_greater_or_equal) fplus_fwd_define_fn_1(xor_bools) fplus_fwd_define_fn_0(is_just) fplus_fwd_define_fn_0(is_nothing) fplus_fwd_define_fn_0(unsafe_get_just) fplus_fwd_define_fn_0(just_with_default) fplus_fwd_define_fn_1(throw_on_nothing) fplus_fwd_define_fn_0(just) fplus_fwd_define_fn_1(as_just_if) fplus_fwd_define_fn_0(maybe_to_seq) fplus_fwd_define_fn_0(singleton_seq_as_maybe) fplus_fwd_define_fn_1(lift_maybe) fplus_fwd_define_fn_2(lift_maybe_def) fplus_fwd_define_fn_2(lift_maybe_2) fplus_fwd_define_fn_3(lift_maybe_2_def) fplus_fwd_define_fn_1(and_then_maybe) fplus_fwd_define_fn_0(flatten_maybe) fplus_fwd_define_fn_0(is_empty) fplus_fwd_define_fn_0(is_not_empty) fplus_fwd_define_fn_0(size_of_cont) fplus_fwd_define_fn_0(convert) fplus_fwd_define_fn_0(convert_elems) fplus_fwd_define_fn_0(convert_container) fplus_fwd_define_fn_0(convert_container_and_elems) fplus_fwd_define_fn_2(get_segment) fplus_fwd_define_fn_2(set_segment) fplus_fwd_define_fn_2(remove_segment) fplus_fwd_define_fn_2(insert_at) fplus_fwd_define_fn_1(elem_at_idx) fplus_fwd_define_fn_1(elem_at_idx_maybe) fplus_fwd_define_fn_1(elems_at_idxs) fplus_fwd_define_fn_1(transform) fplus_fwd_define_fn_1(transform_convert) fplus_fwd_define_fn_1(transform_inner) fplus_fwd_define_fn_0(reverse) fplus_fwd_define_fn_1(take) fplus_fwd_define_fn_1(take_exact) fplus_fwd_define_fn_1(take_cyclic) fplus_fwd_define_fn_1(drop) fplus_fwd_define_fn_1(take_last) fplus_fwd_define_fn_1(drop_last) fplus_fwd_define_fn_1(drop_exact) fplus_fwd_define_fn_1(take_while) fplus_fwd_define_fn_1(drop_while) fplus_fwd_define_fn_2(fold_left) fplus_fwd_define_fn_2(reduce) fplus_fwd_define_fn_1(fold_left_1) fplus_fwd_define_fn_1(reduce_1) fplus_fwd_define_fn_2(fold_right) fplus_fwd_define_fn_1(fold_right_1) fplus_fwd_define_fn_2(scan_left) fplus_fwd_define_fn_1(scan_left_1) fplus_fwd_define_fn_2(scan_right) fplus_fwd_define_fn_1(scan_right_1) fplus_fwd_define_fn_0(sum) fplus_fwd_define_fn_0(product) fplus_fwd_define_fn_1(append_elem) fplus_fwd_define_fn_1(prepend_elem) fplus_fwd_define_fn_1(append) fplus_fwd_define_fn_1(append_convert) fplus_fwd_define_fn_0(concat) fplus_fwd_define_fn_1(interweave) fplus_fwd_define_fn_0(unweave) fplus_fwd_define_fn_1(sort_by) fplus_fwd_define_fn_1(sort_on) fplus_fwd_define_fn_0(sort) fplus_fwd_define_fn_1(stable_sort_by) fplus_fwd_define_fn_1(stable_sort_on) fplus_fwd_define_fn_0(stable_sort) fplus_fwd_define_fn_2(partial_sort_by) fplus_fwd_define_fn_2(partial_sort_on) fplus_fwd_define_fn_1(partial_sort) fplus_fwd_define_fn_2(nth_element_by) fplus_fwd_define_fn_2(nth_element_on) fplus_fwd_define_fn_1(nth_element) fplus_fwd_define_fn_1(unique_by) fplus_fwd_define_fn_1(unique_on) fplus_fwd_define_fn_0(unique) fplus_fwd_define_fn_1(intersperse) fplus_fwd_define_fn_1(join) fplus_fwd_define_fn_1(join_elem) fplus_fwd_define_fn_1(is_elem_of_by) fplus_fwd_define_fn_1(is_elem_of) fplus_fwd_define_fn_1(nub_by) fplus_fwd_define_fn_1(nub_on) fplus_fwd_define_fn_0(nub) fplus_fwd_define_fn_1(all_unique_by_eq) fplus_fwd_define_fn_1(all_unique_on) fplus_fwd_define_fn_0(all_unique) fplus_fwd_define_fn_1(is_strictly_sorted_by) fplus_fwd_define_fn_1(is_strictly_sorted_on) fplus_fwd_define_fn_0(is_strictly_sorted) fplus_fwd_define_fn_1(is_sorted_by) fplus_fwd_define_fn_1(is_sorted_on) fplus_fwd_define_fn_0(is_sorted) fplus_fwd_define_fn_1(is_prefix_of) fplus_fwd_define_fn_1(is_suffix_of) fplus_fwd_define_fn_1(all_by) fplus_fwd_define_fn_0(all) fplus_fwd_define_fn_1(all_the_same_by) fplus_fwd_define_fn_1(all_the_same_on) fplus_fwd_define_fn_0(all_the_same) fplus_fwd_define_fn_2(numbers_step) fplus_fwd_define_fn_1(numbers) fplus_fwd_define_fn_0(singleton_seq) fplus_fwd_define_fn_0(all_idxs) fplus_fwd_define_fn_0(init) fplus_fwd_define_fn_0(tail) fplus_fwd_define_fn_0(head) fplus_fwd_define_fn_0(last) fplus_fwd_define_fn_0(mean_stddev) fplus_fwd_define_fn_1(count_occurrences_by) fplus_fwd_define_fn_0(count_occurrences) fplus_fwd_define_fn_2(lexicographical_less_by) fplus_fwd_define_fn_1(lexicographical_less) fplus_fwd_define_fn_0(lexicographical_sort) fplus_fwd_define_fn_1(replicate) fplus_fwd_define_fn_2(instead_of_if) fplus_fwd_define_fn_2(instead_of_if_empty) fplus_fwd_define_fn_0(is_ok) fplus_fwd_define_fn_0(is_error) fplus_fwd_define_fn_0(unsafe_get_ok) fplus_fwd_define_fn_0(unsafe_get_error) fplus_fwd_define_fn_1(ok_with_default) fplus_fwd_define_fn_0(ok) fplus_fwd_define_fn_0(error) fplus_fwd_define_fn_0(to_maybe) fplus_fwd_define_fn_1(from_maybe) fplus_fwd_define_fn_1(throw_on_error) fplus_fwd_define_fn_1(lift_result) fplus_fwd_define_fn_2(lift_result_both) fplus_fwd_define_fn_2(unify_result) fplus_fwd_define_fn_1(and_then_result) fplus_fwd_define_fn_1(keep_if) fplus_fwd_define_fn_1(drop_if) fplus_fwd_define_fn_1(without) fplus_fwd_define_fn_1(without_any) fplus_fwd_define_fn_1(keep_if_with_idx) fplus_fwd_define_fn_1(drop_if_with_idx) fplus_fwd_define_fn_1(keep_by_idx) fplus_fwd_define_fn_1(drop_by_idx) fplus_fwd_define_fn_1(keep_idxs) fplus_fwd_define_fn_1(drop_idxs) fplus_fwd_define_fn_1(drop_idx) fplus_fwd_define_fn_0(justs) fplus_fwd_define_fn_0(oks) fplus_fwd_define_fn_0(errors) fplus_fwd_define_fn_1(trim_left) fplus_fwd_define_fn_1(trim_token_left) fplus_fwd_define_fn_1(trim_right_by) fplus_fwd_define_fn_1(trim_right) fplus_fwd_define_fn_1(trim_token_right) fplus_fwd_define_fn_1(trim_by) fplus_fwd_define_fn_1(trim) fplus_fwd_define_fn_1(trim_token) fplus_fwd_define_fn_1(adjacent_keep_snd_if) fplus_fwd_define_fn_1(adjacent_drop_fst_if) fplus_fwd_define_fn_1(adjacent_drop_snd_if) fplus_fwd_define_fn_1(adjacent_keep_fst_if) fplus_fwd_define_fn_1(apply_to_pair) fplus_fwd_define_fn_2(zip_with) fplus_fwd_define_fn_3(zip_with_3) fplus_fwd_define_fn_4(zip_with_defaults) fplus_fwd_define_fn_1(zip) fplus_fwd_define_fn_0(unzip) fplus_fwd_define_fn_0(fst) fplus_fwd_define_fn_0(snd) fplus_fwd_define_fn_1(transform_fst) fplus_fwd_define_fn_1(transform_snd) fplus_fwd_define_fn_2(transform_pair) fplus_fwd_define_fn_0(swap_pair_elems) fplus_fwd_define_fn_0(swap_pairs_elems) fplus_fwd_define_fn_0(adjacent_pairs) fplus_fwd_define_fn_0(overlapping_pairs) fplus_fwd_define_fn_0(overlapping_pairs_cyclic) fplus_fwd_define_fn_0(enumerate) fplus_fwd_define_fn_4(inner_product_with) fplus_fwd_define_fn_2(inner_product) fplus_fwd_define_fn_2(first_mismatch_idx_by) fplus_fwd_define_fn_2(first_mismatch_by) fplus_fwd_define_fn_2(first_mismatch_idx_on) fplus_fwd_define_fn_2(first_mismatch_on) fplus_fwd_define_fn_2(first_mismatch_idx) fplus_fwd_define_fn_2(first_mismatch) fplus_fwd_define_fn_2(first_match_idx_by) fplus_fwd_define_fn_2(first_match_by) fplus_fwd_define_fn_2(first_match_idx_on) fplus_fwd_define_fn_2(first_match_on) fplus_fwd_define_fn_2(first_match_idx) fplus_fwd_define_fn_2(first_match) fplus_fwd_define_fn_2(is_in_interval) fplus_fwd_define_fn_2(is_in_interval_around) fplus_fwd_define_fn_2(is_in_open_interval) fplus_fwd_define_fn_2(is_in_open_interval_around) fplus_fwd_define_fn_2(is_in_closed_interval) fplus_fwd_define_fn_4(reference_interval) fplus_fwd_define_fn_2(clamp) fplus_fwd_define_fn_0(is_negative) fplus_fwd_define_fn_0(is_positive) fplus_fwd_define_fn_0(is_even) fplus_fwd_define_fn_0(is_odd) fplus_fwd_define_fn_0(abs) fplus_fwd_define_fn_1(abs_diff) fplus_fwd_define_fn_0(square) fplus_fwd_define_fn_0(cube) fplus_fwd_define_fn_0(sign) fplus_fwd_define_fn_0(sign_with_zero) fplus_fwd_define_fn_0(integral_cast_throw) fplus_fwd_define_fn_0(integral_cast_clamp) fplus_fwd_define_fn_0(round) fplus_fwd_define_fn_0(floor) fplus_fwd_define_fn_1(floor_to_int_mult) fplus_fwd_define_fn_1(ceil_to_int_mult) fplus_fwd_define_fn_0(ceil) fplus_fwd_define_fn_1(int_power) fplus_fwd_define_fn_2(min_2_on) fplus_fwd_define_fn_2(max_2_on) fplus_fwd_define_fn_1(min_2) fplus_fwd_define_fn_1(max_2) fplus_fwd_define_fn_0(deg_to_rad) fplus_fwd_define_fn_0(rad_to_deg) fplus_fwd_define_fn_2(normalize_min_max) fplus_fwd_define_fn_2(normalize_mean_stddev) fplus_fwd_define_fn_0(standardize) fplus_fwd_define_fn_1(histogram_using_intervals) fplus_fwd_define_fn_2(generate_consecutive_intervals) fplus_fwd_define_fn_3(histogram) fplus_fwd_define_fn_1(modulo_chain) fplus_fwd_define_fn_2(line_equation) fplus_fwd_define_fn_1(generate_by_idx) fplus_fwd_define_fn_1(repeat) fplus_fwd_define_fn_1(infixes) fplus_fwd_define_fn_3(carthesian_product_with_where) fplus_fwd_define_fn_2(carthesian_product_with) fplus_fwd_define_fn_2(carthesian_product_where) fplus_fwd_define_fn_1(carthesian_product) fplus_fwd_define_fn_1(carthesian_product_n) fplus_fwd_define_fn_1(permutations) fplus_fwd_define_fn_1(combinations) fplus_fwd_define_fn_1(combinations_with_replacement) fplus_fwd_define_fn_0(power_set) fplus_fwd_define_fn_2(iterate) fplus_fwd_define_fn_1(iterate_maybe) fplus_fwd_define_fn_1(adjacent_difference_by) fplus_fwd_define_fn_0(adjacent_difference) fplus_fwd_define_fn_0(rotate_left) fplus_fwd_define_fn_0(rotate_right) fplus_fwd_define_fn_0(rotations_left) fplus_fwd_define_fn_0(rotations_right) fplus_fwd_define_fn_2(fill_left) fplus_fwd_define_fn_2(fill_right) fplus_fwd_define_fn_0(inits) fplus_fwd_define_fn_0(tails) fplus_fwd_define_fn_1(find_first_by) fplus_fwd_define_fn_1(find_last_by) fplus_fwd_define_fn_1(find_first_idx_by) fplus_fwd_define_fn_1(find_last_idx_by) fplus_fwd_define_fn_1(find_first_idx) fplus_fwd_define_fn_1(find_last_idx) fplus_fwd_define_fn_1(find_all_idxs_by) fplus_fwd_define_fn_1(find_all_idxs_of) fplus_fwd_define_fn_1(find_all_instances_of_token) fplus_fwd_define_fn_1(find_all_instances_of_token_non_overlapping) fplus_fwd_define_fn_1(find_first_instance_of_token) fplus_fwd_define_fn_1(set_includes) fplus_fwd_define_fn_1(unordered_set_includes) fplus_fwd_define_fn_1(set_merge) fplus_fwd_define_fn_1(unordered_set_merge) fplus_fwd_define_fn_1(set_intersection) fplus_fwd_define_fn_1(unordered_set_intersection) fplus_fwd_define_fn_1(set_is_disjoint) fplus_fwd_define_fn_1(unordered_set_is_disjoint) fplus_fwd_define_fn_1(set_difference) fplus_fwd_define_fn_1(unordered_set_difference) fplus_fwd_define_fn_1(set_symmetric_difference) fplus_fwd_define_fn_1(unordered_set_symmetric_difference) fplus_fwd_define_fn_0(sets_intersection) fplus_fwd_define_fn_0(unordered_sets_intersection) fplus_fwd_define_fn_1(any_by) fplus_fwd_define_fn_0(any) fplus_fwd_define_fn_1(none_by) fplus_fwd_define_fn_0(none) fplus_fwd_define_fn_1(minimum_idx_by) fplus_fwd_define_fn_1(minimum_idx_by_maybe) fplus_fwd_define_fn_1(maximum_idx_by) fplus_fwd_define_fn_1(maximum_idx_by_maybe) fplus_fwd_define_fn_0(minimum_idx) fplus_fwd_define_fn_0(minimum_idx_maybe) fplus_fwd_define_fn_0(maximum_idx) fplus_fwd_define_fn_0(maximum_idx_maybe) fplus_fwd_define_fn_1(minimum_idx_on) fplus_fwd_define_fn_1(minimum_idx_on_maybe) fplus_fwd_define_fn_1(maximum_idx_on) fplus_fwd_define_fn_1(maximum_idx_on_maybe) fplus_fwd_define_fn_1(minimum_by) fplus_fwd_define_fn_1(minimum_by_maybe) fplus_fwd_define_fn_1(maximum_by) fplus_fwd_define_fn_1(maximum_by_maybe) fplus_fwd_define_fn_0(minimum) fplus_fwd_define_fn_0(minimum_maybe) fplus_fwd_define_fn_0(maximum) fplus_fwd_define_fn_0(maximum_maybe) fplus_fwd_define_fn_1(minimum_on) fplus_fwd_define_fn_1(minimum_on_maybe) fplus_fwd_define_fn_1(maximum_on) fplus_fwd_define_fn_1(maximum_on_maybe) fplus_fwd_define_fn_0(mean) fplus_fwd_define_fn_0(mean_obj_div_size_t) fplus_fwd_define_fn_0(mean_obj_div_double) fplus_fwd_define_fn_0(mean_using_doubles) fplus_fwd_define_fn_0(median) fplus_fwd_define_fn_1(all_unique_by_less) fplus_fwd_define_fn_0(all_unique_less) fplus_fwd_define_fn_1(is_infix_of) fplus_fwd_define_fn_1(is_subsequence_of) fplus_fwd_define_fn_1(count_if) fplus_fwd_define_fn_1(count) fplus_fwd_define_fn_1(is_unique_in_by) fplus_fwd_define_fn_1(is_unique_in) fplus_fwd_define_fn_1(is_permutation_of) fplus_fwd_define_fn_1(fill_pigeonholes_to) fplus_fwd_define_fn_0(fill_pigeonholes) fplus_fwd_define_fn_1(fill_pigeonholes_bool_to) fplus_fwd_define_fn_0(fill_pigeonholes_bool) fplus_fwd_define_fn_0(present_in_all) fplus_fwd_define_fn_1(elem_at_idx_or_nothing) fplus_fwd_define_fn_2(elem_at_idx_or_constant) fplus_fwd_define_fn_1(elem_at_idx_or_replicate) fplus_fwd_define_fn_1(elem_at_idx_or_wrap) fplus_fwd_define_fn_2(extrapolate_replicate) fplus_fwd_define_fn_2(extrapolate_wrap) fplus_fwd_define_fn_1(elem_at_float_idx) fplus_fwd_define_fn_0(pairs_to_map) fplus_fwd_define_fn_0(pairs_to_map_grouped) fplus_fwd_define_fn_0(map_to_pairs) fplus_fwd_define_fn_1(transform_map_values) fplus_fwd_define_fn_2(map_union_with) fplus_fwd_define_fn_1(map_union) fplus_fwd_define_fn_0(get_map_keys) fplus_fwd_define_fn_0(get_map_values) fplus_fwd_define_fn_0(swap_keys_and_values) fplus_fwd_define_fn_1(create_map) fplus_fwd_define_fn_1(create_map_with) fplus_fwd_define_fn_1(create_unordered_map) fplus_fwd_define_fn_1(create_unordered_map_with) fplus_fwd_define_fn_1(get_from_map) fplus_fwd_define_fn_1(get_from_map_unsafe) fplus_fwd_define_fn_2(get_from_map_with_def) fplus_fwd_define_fn_1(get_first_from_map) fplus_fwd_define_fn_1(get_first_from_map_unsafe) fplus_fwd_define_fn_2(get_first_from_map_with_def) fplus_fwd_define_fn_1(map_contains) fplus_fwd_define_fn_1(map_keep_if) fplus_fwd_define_fn_1(map_drop_if) fplus_fwd_define_fn_1(map_keep) fplus_fwd_define_fn_1(map_drop) fplus_fwd_define_fn_1(map_keep_if_value) fplus_fwd_define_fn_1(map_drop_if_value) fplus_fwd_define_fn_1(map_keep_values) fplus_fwd_define_fn_1(map_drop_values) fplus_fwd_define_fn_1(map_pluck) fplus_fwd_define_fn_1(choose) fplus_fwd_define_fn_2(choose_by) fplus_fwd_define_fn_1(choose_lazy) fplus_fwd_define_fn_2(choose_by_lazy) fplus_fwd_define_fn_1(choose_def) fplus_fwd_define_fn_2(choose_by_def) fplus_fwd_define_fn_1(choose_def_lazy) fplus_fwd_define_fn_2(choose_by_def_lazy) fplus_fwd_define_fn_1(group_by) fplus_fwd_define_fn_1(group_on) fplus_fwd_define_fn_1(group_on_labeled) fplus_fwd_define_fn_0(group) fplus_fwd_define_fn_1(group_globally_by) fplus_fwd_define_fn_1(group_globally_on) fplus_fwd_define_fn_1(group_globally_on_labeled) fplus_fwd_define_fn_0(group_globally) fplus_fwd_define_fn_1(cluster_by) fplus_fwd_define_fn_2(split_by) fplus_fwd_define_fn_1(split_by_keep_separators) fplus_fwd_define_fn_2(split) fplus_fwd_define_fn_2(split_one_of) fplus_fwd_define_fn_1(split_keep_separators) fplus_fwd_define_fn_1(split_at_idx) fplus_fwd_define_fn_2(insert_at_idx) fplus_fwd_define_fn_1(partition) fplus_fwd_define_fn_1(split_at_idxs) fplus_fwd_define_fn_1(split_every) fplus_fwd_define_fn_2(split_by_token) fplus_fwd_define_fn_1(run_length_encode_by) fplus_fwd_define_fn_0(run_length_encode) fplus_fwd_define_fn_0(run_length_decode) fplus_fwd_define_fn_1(span) fplus_fwd_define_fn_2(divvy) fplus_fwd_define_fn_1(aperture) fplus_fwd_define_fn_1(stride) fplus_fwd_define_fn_1(winsorize) fplus_fwd_define_fn_1(separate_on) fplus_fwd_define_fn_0(separate) fplus_fwd_define_fn_1(transform_with_idx) fplus_fwd_define_fn_1(transform_and_keep_justs) fplus_fwd_define_fn_1(transform_and_keep_oks) fplus_fwd_define_fn_1(transform_and_concat) fplus_fwd_define_fn_1(replicate_elems) fplus_fwd_define_fn_0(interleave) fplus_fwd_define_fn_0(transpose) fplus_fwd_define_fn_1(shuffle) fplus_fwd_define_fn_2(sample) fplus_fwd_define_fn_1(random_element) fplus_fwd_define_fn_2(random_elements) fplus_fwd_define_fn_1(apply_functions) fplus_fwd_define_fn_2(apply_function_n_times) fplus_fwd_define_fn_1(transform_parallelly) fplus_fwd_define_fn_2(transform_parallelly_n_threads) fplus_fwd_define_fn_2(reduce_parallelly) fplus_fwd_define_fn_3(reduce_parallelly_n_threads) fplus_fwd_define_fn_1(reduce_1_parallelly) fplus_fwd_define_fn_2(reduce_1_parallelly_n_threads) fplus_fwd_define_fn_1(keep_if_parallelly) fplus_fwd_define_fn_2(keep_if_parallelly_n_threads) fplus_fwd_define_fn_3(transform_reduce) fplus_fwd_define_fn_2(transform_reduce_1) fplus_fwd_define_fn_3(transform_reduce_parallelly) fplus_fwd_define_fn_4(transform_reduce_parallelly_n_threads) fplus_fwd_define_fn_2(transform_reduce_1_parallelly) fplus_fwd_define_fn_3(transform_reduce_1_parallelly_n_threads) fplus_fwd_define_fn_1(read_value_with_default) fplus_fwd_define_fn_2(replace_if) fplus_fwd_define_fn_2(replace_elem_at_idx) fplus_fwd_define_fn_2(replace_elems) fplus_fwd_define_fn_2(replace_tokens) fplus_fwd_define_fn_0(show) fplus_fwd_define_fn_3(show_cont_with_frame_and_newlines) fplus_fwd_define_fn_3(show_cont_with_frame) fplus_fwd_define_fn_1(show_cont_with) fplus_fwd_define_fn_0(show_cont) fplus_fwd_define_fn_0(show_maybe) fplus_fwd_define_fn_0(show_result) fplus_fwd_define_fn_2(show_float) fplus_fwd_define_fn_3(show_float_fill_left) fplus_fwd_define_fn_2(show_fill_left) fplus_fwd_define_fn_2(show_fill_right) fplus_fwd_define_fn_0(is_letter_or_digit) fplus_fwd_define_fn_0(is_whitespace) fplus_fwd_define_fn_0(is_line_break) fplus_fwd_define_fn_0(clean_newlines) fplus_fwd_define_fn_1(split_words) fplus_fwd_define_fn_1(split_lines) fplus_fwd_define_fn_0(trim_whitespace_left) fplus_fwd_define_fn_0(trim_whitespace_right) fplus_fwd_define_fn_0(trim_whitespace) fplus_fwd_define_fn_0(to_lower_case) fplus_fwd_define_fn_1(to_lower_case_loc) fplus_fwd_define_fn_0(to_upper_case) fplus_fwd_define_fn_1(to_upper_case_loc) fplus_fwd_define_fn_2(to_string_fill_left) fplus_fwd_define_fn_2(to_string_fill_right) fplus_fwd_define_fn_1(trees_from_sequence) fplus_fwd_define_fn_1(are_trees_equal) fplus_fwd_define_fn_0(tree_size) fplus_fwd_define_fn_0(tree_depth) fplus_fwd_define_fn_0(flatten_tree_depth_first) fplus_fwd_define_fn_0(flatten_tree_breadth_first) fplus_fwd_define_fn_0(show_timed) fplus_fwd_define_fn_0(make_timed_function) fplus_fwd_define_fn_0(make_timed_void_function) fplus_fwd_flip_define_fn_1(is_equal) fplus_fwd_flip_define_fn_1(is_not_equal) fplus_fwd_flip_define_fn_1(is_less) fplus_fwd_flip_define_fn_1(is_less_or_equal) fplus_fwd_flip_define_fn_1(is_greater) fplus_fwd_flip_define_fn_1(is_greater_or_equal) fplus_fwd_flip_define_fn_1(xor_bools) fplus_fwd_flip_define_fn_1(throw_on_nothing) fplus_fwd_flip_define_fn_1(as_just_if) fplus_fwd_flip_define_fn_1(lift_maybe) fplus_fwd_flip_define_fn_1(and_then_maybe) fplus_fwd_flip_define_fn_1(elem_at_idx) fplus_fwd_flip_define_fn_1(elem_at_idx_maybe) fplus_fwd_flip_define_fn_1(elems_at_idxs) fplus_fwd_flip_define_fn_1(transform) fplus_fwd_flip_define_fn_1(transform_convert) fplus_fwd_flip_define_fn_1(transform_inner) fplus_fwd_flip_define_fn_1(take) fplus_fwd_flip_define_fn_1(take_exact) fplus_fwd_flip_define_fn_1(take_cyclic) fplus_fwd_flip_define_fn_1(drop) fplus_fwd_flip_define_fn_1(take_last) fplus_fwd_flip_define_fn_1(drop_last) fplus_fwd_flip_define_fn_1(drop_exact) fplus_fwd_flip_define_fn_1(take_while) fplus_fwd_flip_define_fn_1(drop_while) fplus_fwd_flip_define_fn_1(fold_left_1) fplus_fwd_flip_define_fn_1(reduce_1) fplus_fwd_flip_define_fn_1(fold_right_1) fplus_fwd_flip_define_fn_1(scan_left_1) fplus_fwd_flip_define_fn_1(scan_right_1) fplus_fwd_flip_define_fn_1(append_elem) fplus_fwd_flip_define_fn_1(prepend_elem) fplus_fwd_flip_define_fn_1(append) fplus_fwd_flip_define_fn_1(append_convert) fplus_fwd_flip_define_fn_1(interweave) fplus_fwd_flip_define_fn_1(sort_by) fplus_fwd_flip_define_fn_1(sort_on) fplus_fwd_flip_define_fn_1(stable_sort_by) fplus_fwd_flip_define_fn_1(stable_sort_on) fplus_fwd_flip_define_fn_1(partial_sort) fplus_fwd_flip_define_fn_1(nth_element) fplus_fwd_flip_define_fn_1(unique_by) fplus_fwd_flip_define_fn_1(unique_on) fplus_fwd_flip_define_fn_1(intersperse) fplus_fwd_flip_define_fn_1(join) fplus_fwd_flip_define_fn_1(join_elem) fplus_fwd_flip_define_fn_1(is_elem_of_by) fplus_fwd_flip_define_fn_1(is_elem_of) fplus_fwd_flip_define_fn_1(nub_by) fplus_fwd_flip_define_fn_1(nub_on) fplus_fwd_flip_define_fn_1(all_unique_by_eq) fplus_fwd_flip_define_fn_1(all_unique_on) fplus_fwd_flip_define_fn_1(is_strictly_sorted_by) fplus_fwd_flip_define_fn_1(is_strictly_sorted_on) fplus_fwd_flip_define_fn_1(is_sorted_by) fplus_fwd_flip_define_fn_1(is_sorted_on) fplus_fwd_flip_define_fn_1(is_prefix_of) fplus_fwd_flip_define_fn_1(is_suffix_of) fplus_fwd_flip_define_fn_1(all_by) fplus_fwd_flip_define_fn_1(all_the_same_by) fplus_fwd_flip_define_fn_1(all_the_same_on) fplus_fwd_flip_define_fn_1(numbers) fplus_fwd_flip_define_fn_1(count_occurrences_by) fplus_fwd_flip_define_fn_1(lexicographical_less) fplus_fwd_flip_define_fn_1(replicate) fplus_fwd_flip_define_fn_1(ok_with_default) fplus_fwd_flip_define_fn_1(from_maybe) fplus_fwd_flip_define_fn_1(throw_on_error) fplus_fwd_flip_define_fn_1(lift_result) fplus_fwd_flip_define_fn_1(and_then_result) fplus_fwd_flip_define_fn_1(keep_if) fplus_fwd_flip_define_fn_1(drop_if) fplus_fwd_flip_define_fn_1(without) fplus_fwd_flip_define_fn_1(without_any) fplus_fwd_flip_define_fn_1(keep_if_with_idx) fplus_fwd_flip_define_fn_1(drop_if_with_idx) fplus_fwd_flip_define_fn_1(keep_by_idx) fplus_fwd_flip_define_fn_1(drop_by_idx) fplus_fwd_flip_define_fn_1(keep_idxs) fplus_fwd_flip_define_fn_1(drop_idxs) fplus_fwd_flip_define_fn_1(drop_idx) fplus_fwd_flip_define_fn_1(trim_left) fplus_fwd_flip_define_fn_1(trim_token_left) fplus_fwd_flip_define_fn_1(trim_right_by) fplus_fwd_flip_define_fn_1(trim_right) fplus_fwd_flip_define_fn_1(trim_token_right) fplus_fwd_flip_define_fn_1(trim_by) fplus_fwd_flip_define_fn_1(trim) fplus_fwd_flip_define_fn_1(trim_token) fplus_fwd_flip_define_fn_1(adjacent_keep_snd_if) fplus_fwd_flip_define_fn_1(adjacent_drop_fst_if) fplus_fwd_flip_define_fn_1(adjacent_drop_snd_if) fplus_fwd_flip_define_fn_1(adjacent_keep_fst_if) fplus_fwd_flip_define_fn_1(apply_to_pair) fplus_fwd_flip_define_fn_1(zip) fplus_fwd_flip_define_fn_1(transform_fst) fplus_fwd_flip_define_fn_1(transform_snd) fplus_fwd_flip_define_fn_1(abs_diff) fplus_fwd_flip_define_fn_1(floor_to_int_mult) fplus_fwd_flip_define_fn_1(ceil_to_int_mult) fplus_fwd_flip_define_fn_1(int_power) fplus_fwd_flip_define_fn_1(min_2) fplus_fwd_flip_define_fn_1(max_2) fplus_fwd_flip_define_fn_1(histogram_using_intervals) fplus_fwd_flip_define_fn_1(modulo_chain) fplus_fwd_flip_define_fn_1(generate_by_idx) fplus_fwd_flip_define_fn_1(repeat) fplus_fwd_flip_define_fn_1(infixes) fplus_fwd_flip_define_fn_1(carthesian_product) fplus_fwd_flip_define_fn_1(carthesian_product_n) fplus_fwd_flip_define_fn_1(permutations) fplus_fwd_flip_define_fn_1(combinations) fplus_fwd_flip_define_fn_1(combinations_with_replacement) fplus_fwd_flip_define_fn_1(iterate_maybe) fplus_fwd_flip_define_fn_1(adjacent_difference_by) fplus_fwd_flip_define_fn_1(find_first_by) fplus_fwd_flip_define_fn_1(find_last_by) fplus_fwd_flip_define_fn_1(find_first_idx_by) fplus_fwd_flip_define_fn_1(find_last_idx_by) fplus_fwd_flip_define_fn_1(find_first_idx) fplus_fwd_flip_define_fn_1(find_last_idx) fplus_fwd_flip_define_fn_1(find_all_idxs_by) fplus_fwd_flip_define_fn_1(find_all_idxs_of) fplus_fwd_flip_define_fn_1(find_all_instances_of_token) fplus_fwd_flip_define_fn_1(find_all_instances_of_token_non_overlapping) fplus_fwd_flip_define_fn_1(find_first_instance_of_token) fplus_fwd_flip_define_fn_1(set_includes) fplus_fwd_flip_define_fn_1(unordered_set_includes) fplus_fwd_flip_define_fn_1(set_merge) fplus_fwd_flip_define_fn_1(unordered_set_merge) fplus_fwd_flip_define_fn_1(set_intersection) fplus_fwd_flip_define_fn_1(unordered_set_intersection) fplus_fwd_flip_define_fn_1(set_is_disjoint) fplus_fwd_flip_define_fn_1(unordered_set_is_disjoint) fplus_fwd_flip_define_fn_1(set_difference) fplus_fwd_flip_define_fn_1(unordered_set_difference) fplus_fwd_flip_define_fn_1(set_symmetric_difference) fplus_fwd_flip_define_fn_1(unordered_set_symmetric_difference) fplus_fwd_flip_define_fn_1(any_by) fplus_fwd_flip_define_fn_1(none_by) fplus_fwd_flip_define_fn_1(minimum_idx_by) fplus_fwd_flip_define_fn_1(minimum_idx_by_maybe) fplus_fwd_flip_define_fn_1(maximum_idx_by) fplus_fwd_flip_define_fn_1(maximum_idx_by_maybe) fplus_fwd_flip_define_fn_1(minimum_idx_on) fplus_fwd_flip_define_fn_1(minimum_idx_on_maybe) fplus_fwd_flip_define_fn_1(maximum_idx_on) fplus_fwd_flip_define_fn_1(maximum_idx_on_maybe) fplus_fwd_flip_define_fn_1(minimum_by) fplus_fwd_flip_define_fn_1(minimum_by_maybe) fplus_fwd_flip_define_fn_1(maximum_by) fplus_fwd_flip_define_fn_1(maximum_by_maybe) fplus_fwd_flip_define_fn_1(minimum_on) fplus_fwd_flip_define_fn_1(minimum_on_maybe) fplus_fwd_flip_define_fn_1(maximum_on) fplus_fwd_flip_define_fn_1(maximum_on_maybe) fplus_fwd_flip_define_fn_1(all_unique_by_less) fplus_fwd_flip_define_fn_1(is_infix_of) fplus_fwd_flip_define_fn_1(is_subsequence_of) fplus_fwd_flip_define_fn_1(count_if) fplus_fwd_flip_define_fn_1(count) fplus_fwd_flip_define_fn_1(is_unique_in_by) fplus_fwd_flip_define_fn_1(is_unique_in) fplus_fwd_flip_define_fn_1(is_permutation_of) fplus_fwd_flip_define_fn_1(fill_pigeonholes_to) fplus_fwd_flip_define_fn_1(fill_pigeonholes_bool_to) fplus_fwd_flip_define_fn_1(elem_at_idx_or_nothing) fplus_fwd_flip_define_fn_1(elem_at_idx_or_replicate) fplus_fwd_flip_define_fn_1(elem_at_idx_or_wrap) fplus_fwd_flip_define_fn_1(elem_at_float_idx) fplus_fwd_flip_define_fn_1(transform_map_values) fplus_fwd_flip_define_fn_1(map_union) fplus_fwd_flip_define_fn_1(create_map) fplus_fwd_flip_define_fn_1(create_map_with) fplus_fwd_flip_define_fn_1(create_unordered_map) fplus_fwd_flip_define_fn_1(create_unordered_map_with) fplus_fwd_flip_define_fn_1(get_from_map) fplus_fwd_flip_define_fn_1(get_from_map_unsafe) fplus_fwd_flip_define_fn_1(get_first_from_map) fplus_fwd_flip_define_fn_1(get_first_from_map_unsafe) fplus_fwd_flip_define_fn_1(map_contains) fplus_fwd_flip_define_fn_1(map_keep_if) fplus_fwd_flip_define_fn_1(map_drop_if) fplus_fwd_flip_define_fn_1(map_keep) fplus_fwd_flip_define_fn_1(map_drop) fplus_fwd_flip_define_fn_1(map_keep_if_value) fplus_fwd_flip_define_fn_1(map_drop_if_value) fplus_fwd_flip_define_fn_1(map_keep_values) fplus_fwd_flip_define_fn_1(map_drop_values) fplus_fwd_flip_define_fn_1(map_pluck) fplus_fwd_flip_define_fn_1(choose) fplus_fwd_flip_define_fn_1(choose_lazy) fplus_fwd_flip_define_fn_1(choose_def) fplus_fwd_flip_define_fn_1(choose_def_lazy) fplus_fwd_flip_define_fn_1(group_by) fplus_fwd_flip_define_fn_1(group_on) fplus_fwd_flip_define_fn_1(group_on_labeled) fplus_fwd_flip_define_fn_1(group_globally_by) fplus_fwd_flip_define_fn_1(group_globally_on) fplus_fwd_flip_define_fn_1(group_globally_on_labeled) fplus_fwd_flip_define_fn_1(cluster_by) fplus_fwd_flip_define_fn_1(split_by_keep_separators) fplus_fwd_flip_define_fn_1(split_keep_separators) fplus_fwd_flip_define_fn_1(split_at_idx) fplus_fwd_flip_define_fn_1(partition) fplus_fwd_flip_define_fn_1(split_at_idxs) fplus_fwd_flip_define_fn_1(split_every) fplus_fwd_flip_define_fn_1(run_length_encode_by) fplus_fwd_flip_define_fn_1(span) fplus_fwd_flip_define_fn_1(aperture) fplus_fwd_flip_define_fn_1(stride) fplus_fwd_flip_define_fn_1(winsorize) fplus_fwd_flip_define_fn_1(separate_on) fplus_fwd_flip_define_fn_1(transform_with_idx) fplus_fwd_flip_define_fn_1(transform_and_keep_justs) fplus_fwd_flip_define_fn_1(transform_and_keep_oks) fplus_fwd_flip_define_fn_1(transform_and_concat) fplus_fwd_flip_define_fn_1(replicate_elems) fplus_fwd_flip_define_fn_1(shuffle) fplus_fwd_flip_define_fn_1(random_element) fplus_fwd_flip_define_fn_1(apply_functions) fplus_fwd_flip_define_fn_1(transform_parallelly) fplus_fwd_flip_define_fn_1(reduce_1_parallelly) fplus_fwd_flip_define_fn_1(keep_if_parallelly) fplus_fwd_flip_define_fn_1(read_value_with_default) fplus_fwd_flip_define_fn_1(show_cont_with) fplus_fwd_flip_define_fn_1(split_words) fplus_fwd_flip_define_fn_1(split_lines) fplus_fwd_flip_define_fn_1(to_lower_case_loc) fplus_fwd_flip_define_fn_1(to_upper_case_loc) fplus_fwd_flip_define_fn_1(trees_from_sequence) fplus_fwd_flip_define_fn_1(are_trees_equal) libfplus-0.2.13/include/fplus/generate.hpp000066400000000000000000000445001376322245400205070ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include namespace fplus { // API search type: generate : ((() -> a), Int) -> [a] // Grab values from executing a nullary function // to generate a sequence of amount values. // generate(f, 3) == [f(), f(), f()] // Can for example be used to generate a list of random numbers. template ContainerOut generate(F f, std::size_t amount) { internal::trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, amount); auto it = internal::get_back_inserter(ys); for (std::size_t i = 0; i < amount; ++i) { *it = internal::invoke(f); } return ys; } // API search type: generate_by_idx : ((Int -> a), Int) -> [a] // fwd bind count: 1 // Grab values from executing a unary function with an index // to generate a sequence of amount values. // generate_by_idx(f, 3) == [f(0), f(1), f(2)] template ContainerOut generate_by_idx(F f, std::size_t amount) { internal:: trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, amount); auto it = internal::get_back_inserter(ys); for (std::size_t i = 0; i < amount; ++i) { *it = internal::invoke(f, i); } return ys; } // API search type: repeat : (Int, [a]) -> [a] // fwd bind count: 1 // Create a sequence containing xs concatenated n times. // repeat(3, [1, 2]) == [1, 2, 1, 2, 1, 2] template Container repeat(std::size_t n, const Container& xs) { std::vector xss(n, xs); return concat(xss); } // API search type: infixes : (Int, [a]) -> [[a]] // fwd bind count: 1 // Return als possible infixed of xs with a given length. // infixes(3, [1,2,3,4,5,6]) == [[1,2,3], [2,3,4], [3,4,5], [4,5,6]] // length must be > 0 template > ContainerOut infixes(std::size_t length, const ContainerIn& xs) { assert(length > 0); static_assert(std::is_convertible::value, "ContainerOut can not take values of type ContainerIn as elements."); ContainerOut result; if (size_of_cont(xs) < length) return result; internal::prepare_container(result, size_of_cont(xs) - length); auto itOut = internal::get_back_inserter(result); for (std::size_t idx = 0; idx <= size_of_cont(xs) - length; ++idx) { *itOut = get_segment(idx, idx + length, xs); } return result; } // API search type: carthesian_product_with_where : (((a, b) -> c), ((a -> b), Bool), [a], [b]) -> [c] // fwd bind count: 3 // carthesian_product_with_where(make_pair, always(true), "ABC", "XY") // == [(A,X),(A,Y),(B,X),(B,Y),(C,X),(C,Y)] // same as (in Haskell): // [ f x y | x <- xs, y <- ys, pred x y ] // same as (in pseudo SQL): // SELECT f(xs.x, ys.y) // FROM xs, ys // WHERE pred(xs.x, ys.y); template auto carthesian_product_with_where(F f, Pred pred, const Container1& xs, const Container2& ys) { using X = typename Container1::value_type; using Y = typename Container2::value_type; using FOut = internal::invoke_result_t; using ContainerOut = std::vector>; ContainerOut result; auto itOut = internal::get_back_inserter(result); for (const auto& x : xs) { for (const auto& y : ys) { if (internal::invoke(pred, x, y)) { itOut = f(x, y); } } } return result; } // API search type: carthesian_product_with : (((a, b) -> c), [a], [b]) -> [c] // fwd bind count: 2 // carthesian_product_with(make_pair, "ABC", "XY") // == [(A,X),(A,Y),(B,X),(B,Y),(C,X),(C,Y)] // same as (in Haskell): // [ f x y | x <- xs, y <- ys ] // same as (in pseudo SQL): // SELECT f(xs.x, ys.y) // FROM xs, ys; template auto carthesian_product_with(F f, const Container1& xs, const Container2& ys) { auto always_true_x_y = [](const auto&, const auto&) { return true; }; return carthesian_product_with_where(f, always_true_x_y, xs, ys); } // API search type: carthesian_product_where : (((a, b) -> Bool), [a], [b]) -> [(a, b)] // fwd bind count: 2 // carthesian_product_where(always(true), "ABC", "XY") // == [(A,X),(A,Y),(B,X),(B,Y),(C,X),(C,Y)] // same as (in Haskell): // [ (x, y) | x <- xs, y <- ys, pred x y ] // same as (in pseudo SQL): // SELECT (xs.x, ys.y) // FROM xs, ys // WHERE pred(xs.x, ys.y); template auto carthesian_product_where(Pred pred, const Container1& xs, const Container2& ys) { auto make_res_pair = [](const auto& x, const auto& y) { return std::make_pair(x, y); }; return carthesian_product_with_where(make_res_pair, pred, xs, ys); } // API search type: carthesian_product : ([a], [b]) -> [(a, b)] // fwd bind count: 1 // carthesian_product("ABC", "XY") // == [(A,X),(A,Y),(B,X),(B,Y),(C,X),(C,Y)] // same as (in Haskell): // [ (x, y) | x <- xs, y <- ys ] // same as (in pseudo SQL): // SELECT (xs.x, ys.y) // FROM xs, ys; template auto carthesian_product(const Container1& xs, const Container2& ys) { auto make_res_pair = [](const auto& x, const auto& y) { return std::make_pair(x, y); }; auto always_true_x_y = [](const auto&, const auto&) { return true; }; return carthesian_product_with_where( make_res_pair, always_true_x_y, xs, ys); } namespace internal { // productN :: Int -> [a] -> [[a]] // productN n = foldr go [[]] . replicate n // where go elems acc = [x:xs | x <- elems, xs <- acc] template std::vector> helper_carthesian_product_n_idxs (std::size_t power, const std::vector& xs) { static_assert(std::is_same::value, "T must be std::size_t"); typedef std::vector Vec; typedef std::vector VecVec; if (power == 0) return VecVec(); auto go = [](const Vec& elems, const VecVec& acc) { VecVec result; for (const T& x : elems) { for (const Vec& tail : acc) { result.push_back(append(Vec(1, x), tail)); } } return result; }; return fold_right(go, VecVec(1), replicate(power, xs)); } } // API search type: carthesian_product_n : (Int, [a]) -> [[a]] // fwd bind count: 1 // Returns the product set with a given power. // carthesian_product_n(2, "ABCD") // == AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD template > ContainerOut carthesian_product_n(std::size_t power, const ContainerIn& xs_in) { if (power == 0) return ContainerOut(1); std::vector xs = convert_container>(xs_in); auto idxs = all_idxs(xs); auto result_idxss = internal::helper_carthesian_product_n_idxs(power, idxs); typedef typename ContainerOut::value_type ContainerOutInner; auto to_result_cont = [&](const std::vector& indices) { return convert_container_and_elems( elems_at_idxs(indices, xs)); }; return transform(to_result_cont, result_idxss); } // API search type: permutations : (Int, [a]) -> [[a]] // fwd bind count: 1 // Generate all possible permutations with a given power. // permutations(2, "ABCD") == AB AC AD BA BC BD CA CB CD DA DB DC template > ContainerOut permutations(std::size_t power, const ContainerIn& xs_in) { if (power == 0) return ContainerOut(1); std::vector xs = convert_container>(xs_in); auto idxs = all_idxs(xs); typedef std::vector idx_vec; auto result_idxss = keep_if(all_unique, internal::helper_carthesian_product_n_idxs(power, idxs)); typedef typename ContainerOut::value_type ContainerOutInner; auto to_result_cont = [&](const std::vector& indices) { return convert_container_and_elems( elems_at_idxs(indices, xs)); }; return transform(to_result_cont, result_idxss); } // API search type: combinations : (Int, [a]) -> [[a]] // fwd bind count: 1 // Generate all possible combinations with a given power. // combinations(2, "ABCD") == AB AC AD BC BD CD template > ContainerOut combinations(std::size_t power, const ContainerIn& xs_in) { if (power == 0) return ContainerOut(1); std::vector xs = convert_container>(xs_in); auto idxs = all_idxs(xs); typedef std::vector idx_vec; auto result_idxss = keep_if(is_strictly_sorted, internal::helper_carthesian_product_n_idxs(power, idxs)); typedef typename ContainerOut::value_type ContainerOutInner; auto to_result_cont = [&](const std::vector& indices) { return convert_container_and_elems( elems_at_idxs(indices, xs)); }; return transform(to_result_cont, result_idxss); } // API search type: combinations_with_replacement : (Int, [a]) -> [[a]] // fwd bind count: 1 // Generate all possible combinations using replacement with a given power. // combinations_with_replacement(2, "ABCD") == AA AB AC AD BB BC BD CC CD DD template > ContainerOut combinations_with_replacement(std::size_t power, const ContainerIn& xs_in) { if (power == 0) return ContainerOut(1); std::vector xs = convert_container>(xs_in); auto idxs = all_idxs(xs); typedef std::vector idx_vec; auto result_idxss = keep_if(is_sorted, internal::helper_carthesian_product_n_idxs(power, idxs)); typedef typename ContainerOut::value_type ContainerOutInner; auto to_result_cont = [&](const std::vector& indices) { return convert_container_and_elems( elems_at_idxs(indices, xs)); }; return transform(to_result_cont, result_idxss); } // API search type: power_set : [a] -> [[a]] // fwd bind count: 0 // Return the set of all subsets of xs_in, // including the empty set and xs_in itself. // power_set("xyz") == ["", "x", "y", "z", "xy", "xz", "yz", "xyz"] // Also known as subsequences. template > ContainerOut power_set(const ContainerIn& xs_in) { return concat( generate_by_idx>( bind_1st_of_2( flip(combinations), xs_in), size_of_cont(xs_in) + 1)); } // API search type: iterate : ((a -> a), Int, a) -> [a] // fwd bind count: 2 // Repeatedly apply a function (n times) to a value (starting with x) // and recording the outputs on its way. // iterate((*2), 5, 3) = [3, 6, 12, 24, 48] // = [3, f(3), f(f(3)), f(f(f(3))), f(f(f(f(3))))] template > ContainerOut iterate(F f, std::size_t size, const T& x) { ContainerOut result; if (size == 0) return result; internal::prepare_container(result, size + 1); auto it_out = internal::get_back_inserter(result); T current = x; *it_out = current; for (std::size_t i = 1; i < size; ++i) { current = internal::invoke(f, current); *it_out = current; } return result; } // API search type: iterate_maybe : ((a -> Maybe a), a) -> [a] // fwd bind count: 1 // Repeatedly apply a function to a value (starting with x) // and recording the outputs on its way. // Stops when the function returns nothing. // iterate_maybe(next_collats_val, 5) = [5, 16, 8, 4, 2, 1] template > ContainerOut iterate_maybe(F f, const T& x) { ContainerOut result; auto it_out = internal::get_back_inserter(result); maybe current(x); while (current.is_just()) { *it_out = current.unsafe_get_just(); current = internal::invoke(f, current.unsafe_get_just()); } return result; } // API search type: adjacent_difference_by : [a] -> [a] // fwd bind count: 1 // Computes the differences between the second // and the first of each adjacent pair of elements of the sequence // using a binary function. // adjacent_difference_by([0,4,1,2,5]) == [0,4,-3,1,3] template auto adjacent_difference_by(F f, const ContainerIn& xs) { using X = typename ContainerIn::value_type; using TOut = internal::invoke_result_t; using ContainerOut = std::vector>; ContainerOut result; using std::begin; using std::end; internal::prepare_container(result, size_of_cont(xs)); std::adjacent_difference(begin(xs), end(xs), back_inserter(result), f); return result; } // API search type: adjacent_difference : [a] -> [a] // fwd bind count: 0 // Computes the differences between the second // and the first of each adjacent pair of elements of the sequence. // adjacent_difference([0,4,1,2,5]) == [0,4,-3,1,3] template Container adjacent_difference(const Container& xs) { return adjacent_difference_by( std::minus(), xs); } // API search type: rotate_left : [a] -> [a] // fwd bind count: 0 // Removes the first element and appends it to the back. // rotate_left("xyz") == "yzx" template Container rotate_left(const Container& xs) { if (is_empty(xs)) return xs; Container ys; auto size = size_of_cont(xs); internal::prepare_container(ys, size); auto it = std::begin(xs); auto it_out = internal::get_back_inserter(ys); ++it; while (it != std::end(xs)) { *it_out = *it; ++it; } *it_out = xs.front(); return ys; } // API search type: rotate_right : [a] -> [a] // fwd bind count: 0 // Removes the last element and prepends it to the front. // rotate_right("xyz") == "zxy" template Container rotate_right(const Container& xs) { return reverse(rotate_left(reverse(xs))); } // API search type: rotations_left : [a] -> [[a]] // fwd bind count: 0 // Returns all possible rotations using rotate_left. // rotations_left("abcd") == ["abcd", "bcda", "cdab", "dabc"] template > ContainerOut rotations_left(const ContainerIn& xs_in) { return iterate(rotate_left, size_of_cont(xs_in), xs_in); } // API search type: rotations_right : [a] -> [[a]] // fwd bind count: 0 // Returns all possible rotations using rotate_right. // rotations_right("abcd") == ["abcd", "dabc", "cdab", "bcda"] template > ContainerOut rotations_right(const ContainerIn& xs_in) { return iterate(rotate_right, size_of_cont(xs_in), xs_in); } // API search type: fill_left : (a, Int, [a]) -> [a] // fwd bind count: 2 // Right-align a sequence. // fill_left(0, 6, [1,2,3,4]) == [0,0,1,2,3,4] // Also known as pad_left. template Container fill_left(const T& x, std::size_t min_size, const Container& xs) { if (min_size <= size_of_cont(xs)) return xs; return append(replicate(min_size - size_of_cont(xs), x), xs); } // API search type: fill_right : (a, Int, [a]) -> [a] // fwd bind count: 2 // Left-align a sequence. // fill_right(0, 6, [1,2,3,4]) == [1,2,3,4,0,0] template Container fill_right(const T& x, std::size_t min_size, const Container& xs) { if (min_size <= size_of_cont(xs)) return xs; return append(xs, replicate(min_size - size_of_cont(xs), x)); } // API search type: inits : [a] -> [[a]] // fwd bind count: 0 // Generate all possible segments of xs that include the first element. // inits([0,1,2,3]) == [[],[0],[0,1],[0,1,2],[0,1,2,3]] template > ContainerOut inits(const ContainerIn& xs) { ContainerOut result; std::size_t xs_size = size_of_cont(xs); internal::prepare_container(result, xs_size + 1); auto it_out = internal::get_back_inserter(result); for (std::size_t i = 0; i <= xs_size; ++i) *it_out = get_segment(0, i, xs); return result; } // API search type: tails : [a] -> [[a]] // fwd bind count: 0 // Generate all possible segments of xs that include the last element. // tails([0,1,2,3]) == [[0,1,2,3],[1,2,3],[2,3],[3],[]] template > ContainerOut tails(const ContainerIn& xs) { ContainerOut result; std::size_t xs_size = size_of_cont(xs); internal::prepare_container(result, xs_size + 1); auto it_out = internal::get_back_inserter(result); for (std::size_t i = 0; i <= xs_size; ++i) *it_out = get_segment(i, xs_size, xs); return result; } } // namespace fplus libfplus-0.2.13/include/fplus/internal/000077500000000000000000000000001376322245400200155ustar00rootroot00000000000000libfplus-0.2.13/include/fplus/internal/apply.hpp000066400000000000000000000020121376322245400216460ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include namespace fplus { namespace internal { // C++17 std::apply (http://en.cppreference.com/w/cpp/utility/apply) template constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence) { return internal::invoke(std::forward(f), std::get(std::forward(t))...); } template constexpr decltype(auto) apply(F&& f, Tuple&& t) { return internal::apply_impl( std::forward(f), std::forward(t), std::make_index_sequence< std::tuple_size>::value>{}); } } } libfplus-0.2.13/include/fplus/internal/asserts/000077500000000000000000000000001376322245400215015ustar00rootroot00000000000000libfplus-0.2.13/include/fplus/internal/asserts/composition.hpp000066400000000000000000000104031376322245400245530ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include namespace fplus { namespace internal { struct bind_1st_of_2_tag { }; struct bind_2nd_of_2_tag { }; struct bind_1st_of_3_tag { }; struct bind_1st_and_2nd_of_3_tag { }; struct bind_2nd_and_3rd_of_3_tag { }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function can not take bound parameter type"); static_assert(std::is_convertible::value, "Function can not take provided parameter type"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function can not take provided parameter type"); static_assert(std::is_convertible::value, "Function can not take bound parameter type"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 3, "Function must take three parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; typedef typename utils::function_traits::template arg<2>::type FIn2; static_assert(std::is_convertible::value, "Function can not take bound parameter type"); static_assert(std::is_convertible::value, "Function can not take provided first parameter type"); static_assert(std::is_convertible::value, "Function can not take provided second parameter type"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 3, "Function must take three parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; typedef typename utils::function_traits::template arg<2>::type FIn2; static_assert(std::is_convertible::value, "Function can not take first bound parameter type"); static_assert(std::is_convertible::value, "Function can not take second bound parameter type"); static_assert(std::is_convertible::value, "Function can not take provided parameter type"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 3, "Function must take three parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; typedef typename utils::function_traits::template arg<2>::type FIn2; static_assert(std::is_convertible::value, "Function can not take provided parameter type"); static_assert(std::is_convertible::value, "Function can not take second bound parameter type"); static_assert(std::is_convertible::value, "Function can not take first bound parameter type"); }; } } libfplus-0.2.13/include/fplus/internal/asserts/functions.hpp000066400000000000000000000053001376322245400242200ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include namespace fplus { namespace internal { struct nullary_function_tag { }; struct unary_function_tag { }; struct binary_function_tag { }; struct binary_predicate_tag { }; struct check_arity_tag { }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 0, "Function must take no parameters."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 1, "Function must take one parameter."); typedef typename utils::function_traits::template arg<0>::type FIn0; static_assert(std::is_convertible::value, "Invalid argument type for function"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; static_assert(std::is_convertible::value, "Invalid first argument type for function"); typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Invalid second argument type for function"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_same::value, "Both parameters must have the same type."); static_assert(std::is_same>, bool>::value, "Predicate must return bool."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == sizeof...(Args), "Wrong arity."); }; } } libfplus-0.2.13/include/fplus/internal/asserts/pairs.hpp000066400000000000000000000101071376322245400233270ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include namespace fplus { namespace internal { struct apply_to_pair_tag { }; struct zip_with_tag { }; struct zip_with_3_tag { }; struct transform_fst_tag { }; struct transform_snd_tag { }; struct inner_product_with_tag { }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function does not take pair.first type as first Parameter."); static_assert(std::is_convertible::value, "Function does not take pair.second type as second Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function does not take elements from first Container as first Parameter."); static_assert(std::is_convertible::value, "Function does not take elements from second Container as second Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 3, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; typedef typename utils::function_traits::template arg<2>::type FIn2; static_assert(std::is_convertible::value, "Function does not take elements from first Container as first Parameter."); static_assert(std::is_convertible::value, "Function does not take elements from second Container as second Parameter."); static_assert(std::is_convertible::value, "Function does not take elements from third Container as third Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 1, "Function must take one parameter."); typedef typename utils::function_traits::template arg<0>::type FIn0; static_assert(std::is_convertible::value, "Function does not take pair.first type as first Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 1, "Function must take one parameter."); typedef typename utils::function_traits::template arg<0>::type FIn0; static_assert(std::is_convertible::value, "Function does not take pair.second type as first Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function does not take elements from first Container as first Parameter."); static_assert(std::is_convertible::value, "Function does not take elements from second Container as second Parameter."); }; } } libfplus-0.2.13/include/fplus/internal/compare.hpp000066400000000000000000000021321376322245400221520ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include namespace fplus { namespace internal { template auto ord_to_impl(Compare comp) { return [comp](auto x, auto y) { static_assert(std::is_same::value, "Argument types must be the same"); using In = decltype(x); internal::trigger_static_asserts(); using CompareOut = std::decay_t>; static_assert(std::is_same::value, "Function must return bool."); return std::make_pair(internal::invoke(comp, x, y), internal::invoke(comp, y, x)); }; } } } libfplus-0.2.13/include/fplus/internal/composition.hpp000066400000000000000000000073561376322245400231040ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include namespace fplus { namespace internal { // source: https://codereview.stackexchange.com/a/63893 // note: the code in the link above is called with the arguments in reverse order template class compose_impl { static constexpr std::size_t size = sizeof...(Fs); static_assert(size > 1, "Invalid number of functions to compose, minimum is two."); public: compose_impl(Fs&&... fs) : _functionTuple(std::forward(fs)...) { } template auto operator()(Ts&&... ts) const { return _apply(std::integral_constant{}, std::forward(ts)...); } private: template auto _apply(std::integral_constant, Ts&&... ts) const { return _apply(std::integral_constant{}, std::get(_functionTuple)(std::forward(ts)...)); } template auto _apply(std::integral_constant, Ts&&... ts) const { return internal::invoke(std::get(_functionTuple), std::forward(ts)...); } std::tuple _functionTuple; }; // Is BinaryLift really correct? template auto compose_binary_lift_impl(std::integral_constant, const Tuple& tup, const BinaryLift& lifter) { return lifter(std::get<0>(tup), std::get<1>(tup)); } template auto compose_binary_lift_impl(std::integral_constant, const Tuple& tup, const BinaryLift& lifter) { return lifter( compose_binary_lift_impl( std::integral_constant{}, tup, lifter), std::get(tup)); } template auto compose_binary_lift(const BinaryLift& lifter, Callables&&... args) { static_assert(sizeof...(Callables) > 1, "Invalid number of functions to compose, minimum is two."); const auto tup = std::forward_as_tuple(std::forward(args)...); return compose_binary_lift_impl( std::integral_constant{}, tup, lifter); } // concentrate asserts in this method. Lambda is provided by the library. template auto logical_binary_op(Lambda op, F f, G g) { // Perfect-forwarding might move twice, if we add a requirement on F and G, // that might not be an issue. return [op, f, g](auto x) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); using FRes = std::decay_t>; using GRes = std::decay_t>; static_assert(std::is_same::value, "Must return bool."); static_assert(std::is_same::value, "Must return bool."); return op(f, g, x); }; } } } libfplus-0.2.13/include/fplus/internal/container_common.hpp000066400000000000000000000025001376322245400240550ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include namespace fplus { namespace internal { template T accumulate(InputIt first, InputIt last, T init) { for (; first != last; ++first) { init = std::move(init) + *first; } return init; } template T accumulate(InputIt first, InputIt last, T init, BinaryOperation op) { for (; first != last; ++first) { init = op(std::move(init), *first); } return init; } template void scan_impl(F f, const Acc& init, OutputIterator itOut, InputIterator begin, InputIterator end) { *itOut = init; auto g = [itOut, f](auto acc, auto x) mutable { acc = internal::invoke(f, acc, x); *itOut = acc; return acc; }; internal::accumulate(begin, end, init, g); } } } libfplus-0.2.13/include/fplus/internal/function_traits_asserts.hpp000066400000000000000000000031451376322245400255100ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include namespace fplus { namespace internal { template struct function_traits_asserts; template < typename, typename F, typename... Args, typename std::enable_if::value, int>::type = 0> constexpr void trigger_static_asserts() { } // Marks a variable as unused. Prevents the compiler warning // for set but unused variables. template inline void unused(T&&) { } template ::value && !is_invocable::value, int>::type = 0> constexpr void trigger_static_asserts() { // don't perform checks if function_traits doesn't exist unused(function_traits_asserts{}); } template ::value && !is_invocable::value, int>::type = 0> constexpr void trigger_static_asserts() { static_assert(sizeof(F) == 0, "F is not a Callable, or its definition is ill-formed"); } } } libfplus-0.2.13/include/fplus/internal/invoke.hpp000066400000000000000000000204241376322245400220230ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include // borrowed to libc++ #define FPLUS_INVOKE_RETURN(...) \ ->decltype(__VA_ARGS__) \ { \ return __VA_ARGS__; \ } namespace fplus { namespace internal { // We need std::invoke to detect callable objects // // source: // http://en.cppreference.com/mwiki/index.php?title=cpp/utility/functional/invoke&oldid=82514 template static std::true_type is_refwrap_test(const std::reference_wrapper&); template static std::false_type is_refwrap_test(const U&); template struct is_reference_wrapper : decltype(is_refwrap_test(std::declval())) { }; template ::type> struct unwrap_reference_wrapper { using type = T; }; template struct unwrap_reference_wrapper> { using type = U&; }; template using unwrap_reference_wrapper_t = typename unwrap_reference_wrapper::type; // note: clang only triggers the second static_assert // - static_assert(is_invocable<&base_class::non_const_method, const derived_class&>::value, ""); // - static_assert(is_invocable<&base_class::non_const_method, const base_class&>::value, ""); // GCC triggers both. To workaround this clang bug, we have to manage cv correctness ourselves template struct is_const_member_function : std::false_type { }; // decay doesn't add pointer to abominable functions, don't bother writing them template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_volatile_member_function : std::false_type { }; // decay doesn't add pointer to abominable functions, don't bother writing them template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct has_correct_cv { // if object has no cv, every method can be called // else the method must have the same cv than the object static constexpr bool value = std::is_same::type, Object>::value || ((is_volatile_member_function::value == std::is_volatile::value) && (is_const_member_function::value == std::is_const::value)); }; // pointer to member function - reference to object template < typename Base, typename T, typename Derived, typename... Args, typename Unwrapped = unwrap_reference_wrapper_t, typename std::enable_if< is_function::value && has_correct_cv::type, T>::value && std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmf, Derived&& ref, Args&&... args) FPLUS_INVOKE_RETURN((std::forward(ref).* pmf)(std::forward(args)...)) // pointer to member function - pointer to object template < typename Base, typename T, typename Pointer, typename... Args, typename std::enable_if< is_function::value && has_correct_cv::type>::type, T>::value && !std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmf, Pointer&& ptr, Args&&... args) FPLUS_INVOKE_RETURN(((*std::forward(ptr)).* pmf)(std::forward(args)...)) // pointer to non-static data member - reference to object template < typename Base, typename T, typename Derived, typename Unwrapped = unwrap_reference_wrapper_t, typename std::enable_if< !is_function::value && std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmd, Derived&& ref) FPLUS_INVOKE_RETURN((std::forward(ref).*pmd)) // pointer to non-static data member - pointer to object template < typename Base, typename T, typename Pointer, typename std::enable_if< !is_function::value && !std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmd, Pointer&& ptr) FPLUS_INVOKE_RETURN((*std::forward(ptr)).*pmd) // normal case - functions, lambdas, function objects template ::type>::value, int>::type = 0> inline auto invoke_impl(F&& f, Args&&... args) FPLUS_INVOKE_RETURN((std::forward(f)(std::forward(args)...))) template struct invoke_result_impl { }; template struct invoke_result_impl(), std::declval()...))), F, Args...> { using type = decltype(invoke_impl(std::declval(), std::declval()...)); }; template struct invoke_result : invoke_result_impl { }; template using invoke_result_t = typename invoke_result::type; // noexcept omitted on purpose, cannot be implemented without C++17. // GCC 7.1 works with libstdc++, but clang fails, even with latest build, // on both libstdc++/libc++, I suspect an internal compiler trait is at // play to make GCC work. // // We could detect if C++17 is used and use std::invoke directly. template invoke_result_t invoke(F&& f, ArgTypes&&... args) { return invoke_impl(std::forward(f), std::forward(args)...); } // Invoke useful traits (libstdc++ 7.1.0's implementation, ugly-case removed) template struct is_invocable_impl : std::false_type { }; template struct is_invocable_impl> : disjunction, std::is_convertible>::type { }; template struct is_invocable : is_invocable_impl, void>::type { }; template struct is_invocable_r : is_invocable_impl, ReturnType>::type { }; } } #undef FPLUS_INVOKE_RETURN libfplus-0.2.13/include/fplus/internal/meta.hpp000066400000000000000000000130631376322245400214570ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include namespace fplus { namespace internal { // C++14 compatible void_t (http://en.cppreference.com/w/cpp/types/void_t) template struct make_void { using type = void; }; template using void_t = typename make_void::type; // Sometimes you don't want to use std::decay_t, and the temptation of short // writing can be huge... template using uncvref_t = std::remove_cv_t>; // disjunction/conjunction/negation, useful to short circuit SFINAE checks // Use with parsimony, MSVC 2015 can have ICEs quite easily template struct disjunction : std::false_type { }; template struct disjunction : B1 { }; template struct disjunction : std::conditional>::type { }; template struct conjunction : std::true_type { }; template struct conjunction : B1 { }; template struct conjunction : std::conditional, B1>::type { }; template struct negation : std::integral_constant { }; // non short-circuiting meta functions // source: https://stackoverflow.com/a/27221517/4116453 template struct bool_pack; template struct all_of : std::is_same, bool_pack> { }; // there seems to be a bug in libc++'s std::is_function // provide our own (cppreference one) // (the MSVC implementation seems correct) #ifndef _MSC_VER #define PROVIDE_IS_FUNCTION_POLYFILL #endif #ifndef PROVIDE_IS_FUNCTION_POLYFILL template using is_function = std::is_function; #else //PROVIDE_IS_FUNCTION_POLYFILL // primary template template struct is_function : std::false_type { }; // specialization for regular functions template struct is_function : std::true_type {}; // specialization for variadic functions such as std::printf template struct is_function : std::true_type {}; // specialization for function types that have cv-qualifiers template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; // specialization for function types that have ref-qualifiers template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; #endif //PROVIDE_IS_FUNCTION_POLYFILL template struct reverse_integer_sequence_impl; template struct reverse_integer_sequence_impl> : std::integer_sequence { }; template struct reverse_integer_sequence_impl> : std::integer_sequence { }; template using reverse_integer_sequence = reverse_integer_sequence_impl; template using make_reverse_integer_sequence = reverse_integer_sequence>; template using reverse_index_sequence = reverse_integer_sequence>; template using make_reverse_index_sequence = make_reverse_integer_sequence; } } libfplus-0.2.13/include/fplus/internal/split.hpp000066400000000000000000000015671376322245400216720ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include namespace fplus { namespace internal { template auto group_on_labeled_impl(GroupByCallable group, F f, const ContainerIn& xs) { const auto grouped = group(is_equal_by(f), xs); const auto attach_label = [f](const auto& g) { using std::begin; return std::make_pair(internal::invoke(f, *begin(g)), g); }; return fplus::transform(attach_label, grouped); } } } libfplus-0.2.13/include/fplus/interpolate.hpp000066400000000000000000000024551376322245400212460ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include namespace fplus { // API search type: elem_at_float_idx : (Float, [a]) -> a // fwd bind count: 1 // Interpolates linearly between elements. // xs must be non-empty. template T elem_at_float_idx(double idx, const Container& xs) { assert(is_not_empty(xs)); if (idx <= 0.0) { return xs.front(); } std::size_t idx_floor = static_cast(floor(idx)); std::size_t idx_ceil = static_cast(ceil(idx)); if (idx_ceil >= size_of_cont(xs)) { return xs.back(); } double idx_floor_float = static_cast(idx_floor); double idx_ceil_float = static_cast(idx_ceil); double weight_floor = idx_ceil_float - idx; double weight_ceil = idx - idx_floor_float; return (weight_floor * elem_at_idx(idx_floor, xs) + weight_ceil * elem_at_idx(idx_ceil, xs)); } } // namespace fplus libfplus-0.2.13/include/fplus/maps.hpp000066400000000000000000000544551376322245400176670ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include namespace fplus { // API search type: pairs_to_map : [(key, val)] -> Map key val // fwd bind count: 0 // Converts a Container of pairs (key, value) into a dictionary. template MapOut pairs_to_map(const ContainerIn& pairs) { return convert_container_and_elems(pairs); } // API search type: pairs_to_map_grouped : [(key, val)] -> Map key [val] // fwd bind count: 0 // Convert a list of key-value pairs to a dictionary // while pushing values having the same key into a vector. // pairs_to_map_grouped([("a", 1), ("a", 2), ("b", 6), ("a", 4)]) // -> {"a": [1, 2, 4], "b": [6]} template >> MapOut pairs_to_map_grouped(const ContainerIn& pairs) { MapOut result; for (const auto& p : pairs) { result[p.first].push_back(p.second); } return result; } // API search type: map_to_pairs : Map key val -> [(key, val)] // fwd bind count: 0 // Converts a dictionary into a Container of pairs (key, value). template ::type, typename Val = typename std::remove_const::type, typename OutPair = std::pair, typename ContainerOut = std::vector> ContainerOut map_to_pairs(const MapType& dict) { return convert_container_and_elems(dict); } // API search type: transform_map_values : ((old_val -> new_val), Map key old_val) -> Map key new_val // fwd bind count: 1 // Manipulate the values in a dictionary, keeping the key-value relationship. // transform_map_values((*2), {0: 2, 1: 3}) == {0: 4, 1: 6} template auto transform_map_values(F f, const MapIn& map) { using MapInPair = typename MapIn::value_type; using Key = std::remove_const_t; using InVal = std::remove_const_t; using OutVal = std::decay_t>; using MapOut = typename internal::SameMapTypeNewTypes::type; return pairs_to_map( transform( bind_1st_of_2(transform_snd, f), map_to_pairs(map))); } // API search type: map_union_with : (((val, val) -> val), Map key val, Map key val) -> Map key val // fwd bind count: 2 // Combine two dictionaries using a binary function for the values. // map_union_with((++), {0: a, 1: b}, {0: c, 2: d}) == {0: ac, 1: b, 2: d} template auto map_union_with(F f, const MapIn& dict1, const MapIn& dict2) { const auto both = append(map_to_pairs(dict1), map_to_pairs(dict2)); using Key = typename decltype(both)::value_type::first_type; using SingleValue = typename decltype(both)::value_type::second_type; auto full_map = pairs_to_map_grouped>::type>(both); const auto group_f = [f](const auto& vals) { return fold_left_1(f, vals); }; return transform_map_values(group_f, full_map); } // API search type: map_union : (Map key val, Map key val) -> Map key val // fwd bind count: 1 // Combine two dictionaries keeping the value of the first map // in case of key collissions. // map_union({0: a, 1: b}, {0: c, 2: d}) == {0: a, 1: b, 2: d} template MapType map_union(const MapType& dict1, const MapType& dict2) { using Value = typename MapType::value_type::second_type; const auto get_first = [](const Value& a, const Value&) -> Value { return a; }; return map_union_with(get_first, dict1, dict2); } // API search type: get_map_keys : Map key val -> [key] // fwd bind count: 0 // Returns all keys used in a map. template ::type>> ContainerOut get_map_keys(const MapType& dict) { auto pairs = map_to_pairs(dict); typedef typename decltype(pairs)::value_type::first_type FirstType; typedef typename decltype(pairs)::value_type::second_type SecondType; return transform_convert( fst, map_to_pairs(dict)); } // API search type: get_map_values : Map key val -> [val] // fwd bind count: 0 // Returns all values present in a map. template ::type>> ContainerOut get_map_values(const MapType& dict) { auto pairs = map_to_pairs(dict); typedef typename decltype(pairs)::value_type::first_type FirstType; typedef typename decltype(pairs)::value_type::second_type SecondType; return transform_convert( snd, map_to_pairs(dict)); } // API search type: swap_keys_and_values : Map a b -> Map b a // fwd bind count: 0 // Swaps keys and Values of a dict: // swap_keys_and_values({1: "a", 2: "b"}) == {"a": 1, "b": 2} template ::type, typename MapOut = typename internal::SameMapTypeNewTypes::type> MapOut swap_keys_and_values(const MapIn& dict) { auto inAsPairs = map_to_pairs(dict); auto outAsPairs = transform(swap_pair_elems, inAsPairs); return pairs_to_map(outAsPairs); } // API search type: create_map : ([key], [val]) -> Map key val // fwd bind count: 1 // Zip a sequence of keys with a sequence of values to obtain a dictionary. // create_map([1,2,3], ["one", "two"]) == {1: "one", 2: "two"} template ::type, typename Val = typename std::remove_const::type, typename MapOut = std::map> MapOut create_map(const ContainerIn1& keys, const ContainerIn2& values) { auto pairs = zip(keys, values); return pairs_to_map(pairs); } // API search type: create_map_with : ((key -> val), [key]) -> Map key val // fwd bind count: 1 // Take a list of keys and create a dictionary // generating the values by applying f to each key. // create_map_with(show, [1,2]) == {1: "1", 2: "2"} template auto create_map_with(F f, const ContainerIn& keys) { return create_map(keys, transform(f, keys)); } // API search type: create_unordered_map : ([key], [val]) -> Map key val // fwd bind count: 1 // Zip a sequence of keys with a sequence of values to obtain a dictionary. // create_unordered_map([1,2,3], ["one", "two"]) == {1: "one", 2: "two"} template ::type, typename Val = typename std::remove_const::type, typename MapOut = std::unordered_map> MapOut create_unordered_map( const ContainerIn1& keys, const ContainerIn2& values) { auto pairs = zip(keys, values); return pairs_to_map(pairs); } // API search type: create_unordered_map_with : ((key -> val), [key]) -> Map key val // fwd bind count: 1 // Take a list of keys and create a dictionary // generating the values by applying f to each key. // create_unordered_map_with(show, [1,2]) == {1: "1", 2: "2"} template auto create_unordered_map_with(F f, const ContainerIn& keys) { return create_unordered_map(keys, transform(f, keys)); } // API search type: get_from_map : (Map key val, key) -> Maybe val // fwd bind count: 1 // Returns just the value of a key if key is present. // Otherwise returns nothing. template maybe get_from_map(const MapType& map, const Key& key) { auto it = map.find(key); if (it == std::end(map)) return nothing(); return just(it->second); } // API search type: get_from_map_unsafe : (Map key val, key) -> val // fwd bind count: 1 // Returns the value of a key if key is present. // Crashes otherwise. template Val get_from_map_unsafe(const MapType& map, const Key& key) { return unsafe_get_just(get_from_map(map, key)); } // API search type: get_from_map_with_def : (Map key val, val, key) -> val // fwd bind count: 2 // Returns the value of a key if key is present. // Otherwise returns the provided default. // Also known as prop_or. template Val get_from_map_with_def(const MapType& map, const Val& defVal, const Key& key) { return just_with_default(defVal, get_from_map(map, key)); } // API search type: get_first_from_map : (Map key val, [key]) -> Maybe val // fwd bind count: 1 // Returns just the value of the first key present. // Otherwise returns nothing. template maybe get_first_from_map(const MapType& map, const KeysContainer& keys) { static_assert(std::is_same::value, "Key type does not match."); for (const auto& key: keys) { auto it = map.find(key); if (it != std::end(map)) return just(it->second); } return nothing(); } // API search type: get_first_from_map_unsafe : (Map key val, [key]) -> val // fwd bind count: 1 // Returns the value of the first key present. // Crashes otherwise. template Val get_first_from_map_unsafe(const MapType& map, const KeysContainer& keys) { return unsafe_get_just(get_first_from_map(map, keys)); } // API search type: get_first_from_map_with_def : (Map key val, val, [key]) -> val // fwd bind count: 2 // Returns the value of the first key present. // Otherwise returns the provided default. template Val get_first_from_map_with_def(const MapType& map, const Val& defVal, const KeysContainer& keys) { return just_with_default(defVal, get_first_from_map(map, keys)); } // API search type: map_contains : (Map key val, key) -> Bool // fwd bind count: 1 // Checks if a map contains a key. template bool map_contains(const MapType& map, const Key& key) { auto it = map.find(key); return it != std::end(map); } // API search type: map_keep_if : ((key -> Bool), Map key val) -> Map key val // fwd bind count: 1 // Filters the map by keys. // map_keep_if(is_upper_case, {a: 1, b: 2, A: 3, C: 4}) == {A: 3, C: 4} // Also known as pick_by. template MapType map_keep_if(Pred pred, const MapType& map) { MapType result; for (const auto& key_and_value : map) { if (internal::invoke(pred, key_and_value.first)) { result.insert(key_and_value); } } return result; } // API search type: map_drop_if : ((key -> Bool), Map key val) -> Map key val // fwd bind count: 1 // Filters the map by keys. // map_drop_if(is_lower_case, {a: 1, b: 2, A: 3, C: 4}) == {A: 3, C: 4} // Inverse of map_keep_if. template MapType map_drop_if(Pred pred, const MapType& map) { return map_keep_if(logical_not(pred), map); } // API search type: map_keep : ([key], Map key val) -> Map key val // fwd bind count: 1 // Keeps only the pairs of the map found in the key list. // map_keep([a, d], {a: 1, b: 2, c: 3, d: 4}) == {a: 1, d: 4} // map_keep([a, e, f], {a: 1, b: 2, c: 3, d: 4}) == {a: 1} // Also known as pick. template MapType map_keep(const KeyContainer& keys, const MapType& map) { static_assert(std::is_same< typename KeyContainer::value_type, typename MapType::key_type>::value, "Key types do not match."); return map_keep_if(bind_2nd_of_2(is_elem_of, keys), map); } // API search type: map_drop : ([key], Map key val) -> Map key val // fwd bind count: 1 // Keeps only the pairs of the map not found in the key list. // Inverse of map_keep. // map_drop([b, c], {a: 1, b: 2, c: 3, d: 4}); == {a: 1, d: 4} template MapType map_drop(const KeyContainer& keys, const MapType& map) { static_assert(std::is_same< typename KeyContainer::value_type, typename MapType::key_type>::value, "Key types do not match."); return map_drop_if(bind_2nd_of_2(is_elem_of, keys), map); } // API search type: map_keep_if_value : ((val -> Bool), Map key val) -> Map key val // fwd bind count: 1 // Filters the map by values. // map_keep_if_value(is_upper_case, {1: a, 2: b, 3: A, 4: C}) == {3: A, 4: C} // Also known as filter_values. template MapType map_keep_if_value(Pred pred, const MapType& map) { MapType result; for (const auto& key_and_value : map) { if (internal::invoke(pred, key_and_value.second)) { result.insert(key_and_value); } } return result; } // API search type: map_drop_if_value : ((value -> Bool), Map key val) -> Map key val // fwd bind count: 1 // Filters the map by values. // map_drop_if_value(is_lower_case, {1: a, 2: b, 3: A, 4: C}) == {3: A, 4: C} // Inverse of map_keep_if_value. template MapType map_drop_if_value(Pred pred, const MapType& map) { return map_keep_if_value(logical_not(pred), map); } // API search type: map_keep_values : ([value], Map key val) -> Map key val // fwd bind count: 1 // Keeps only the pairs of the map found in the value list. // map_keep_values([1, 4], {a: 1, b: 2, c: 3, d: 4}) == {a: 1, d: 4} // map_keep_values([1, 5, 6], {a: 1, b: 2, c: 3, d: 4}) == {a: 1} template MapType map_keep_values(const ValueContainer& values, const MapType& map) { static_assert(std::is_same< typename ValueContainer::value_type, typename MapType::mapped_type>::value, "Value types do not match."); return map_keep_if_value( bind_2nd_of_2(is_elem_of, values), map); } // API search type: map_drop_values : ([value], Map key val) -> Map key val // fwd bind count: 1 // Keeps only the pairs of the map not found in the value list. // Inverse of map_keep_values. // map_drop_values([2, 3], {a: 1, b: 2, c: 3, d: 4}) == {a: 1, d: 4} template MapType map_drop_values(const ValueContainer& values, const MapType& map) { static_assert(std::is_same< typename ValueContainer::value_type, typename MapType::mapped_type>::value, "Value types do not match."); return map_drop_if_value( bind_2nd_of_2(is_elem_of, values), map); } // API search type: map_pluck : (key, [Map key val]) -> [val] // fwd bind count: 1 // Extracts values to a specific key from a list of maps. // map_pluck('a', [{a: 1, b: 2}, {a: 3}, {c: 4}]) == [Just 1, Just 3, Nothing] template >, typename MapType = typename MapContainer::value_type, typename Key = typename MapType::key_type, typename Val = typename MapType::mapped_type> ContainerOut map_pluck(const Key& key, const MapContainer& maps) { return transform_convert( bind_2nd_of_2(get_from_map, key), maps); } // API search type: choose : ([(a, b)], a) -> Maybe b // fwd bind count: 1 // Selects a value assigned to a key iff the key exists exactly once. // choose([(1,a), (2,b)], 2) == Just b; // choose([(1,a), (1,b)], 2) == Nothing; // choose([(1,a), (2,b)], 3) == Nothing; template maybe choose(const std::vector>& pairs, const Key& x) { if (count(x, transform(fst, pairs)) != 1) return {}; return get_from_map(pairs_to_map>(pairs), x); } // API search type: choose_by : ([((a -> Bool), b)], a) -> Maybe b // fwd bind count: 2 // Iff exactly one predicate is fulfilled // the value assigned to this predicate is selected. // choose_by([(is_even,a), (is_bigger_than_3,b)], 2) == Just a; // choose_by([(is_even,a), (is_bigger_than_3,b)], 5) == Just b; // choose_by([(is_even,a), (is_bigger_than_3,b)], 1) == Nothing; // choose_by([(is_even,a), (is_bigger_than_3,b)], 4) == Nothing; template maybe choose_by( const std::vector, Val>>& pairs, const Key& x) { maybe result; for (const auto& p : pairs) { if (internal::invoke(p.first, x)) { if (is_just(result)) { return nothing(); } result = p.second; } } return result; } // API search type: choose_lazy : ([(a, (() -> b))], a) -> Maybe b // fwd bind count: 1 // Evaluates a lazy value assigned to a key iff the key exists exactly once. // choose_lazy([(1,a), (2,b)], 2) == Just b(); // choose_lazy([(1,a), (1,b)], 2) == Nothing; // choose_lazy([(1,a), (2,b)], 3) == Nothing; template auto choose_lazy(const std::vector>& pairs, const Key& x) { using Ret = maybe>>; const auto res = choose(pairs, x); if (res.is_nothing()) return Ret{}; else return Ret{res.unsafe_get_just()()}; } // API search type: choose_by_lazy : ([((a -> Bool), (() -> b))], a) -> Maybe b // fwd bind count: 2 // Iff exactly one predicate is fulfilled // the lazy value assigned to this predicate is evaluated. // choose_by_lazy([(is_even,a), (is_bigger_than_3,b)], 2) == Just a(); // choose_by_lazy([(is_even,a), (is_bigger_than_3,b)], 5) == Just b(); // choose_by_lazy([(is_even,a), (is_bigger_than_3,b)], 1) == Nothing; // choose_by_lazy([(is_even,a), (is_bigger_than_3,b)], 4) == Nothing; template auto choose_by_lazy( const std::vector, ValStub>>& pairs, const Key& x) { using Ret = maybe>>; const auto res = choose_by(pairs, x); if (res.is_nothing()) return Ret{}; else return Ret{res.unsafe_get_just()()}; } // API search type: choose_def : (b, [(a, b)], a) -> b // fwd bind count: 1 // Selects a value assigned to a key iff the key exists exactly once, // otherwise returns the given default value. // choose_def(c, [(1,a), (2,b)], 2) == b; // choose_def(c, [(1,a), (1,b)], 2) == c; // choose_def(c, [(1,a), (2,b)], 3) == c; template Val choose_def(const Val& def, const std::vector>& pairs, const Key& x) { if (count(x, transform(fst, pairs)) != 1) return def; return get_from_map_with_def( pairs_to_map>(pairs), def, x); } // API search type: choose_by_def : (b, [((a -> Bool), b)], a) -> b // fwd bind count: 2 // Iff exactly one predicate is fulfilled // the value assigned to this predicate is selected. // Otherwise the given default value is returned. // choose_by_def(c, [(is_even,a), (is_bigger_than_3,b)], 2) == Just a; // choose_by_def(c, [(is_even,a), (is_bigger_than_3,b)], 5) == Just b; // choose_by_def(c, [(is_even,a), (is_bigger_than_3,b)], 1) == c; // choose_by_def(c, [(is_even,a), (is_bigger_than_3,b)], 4) == c; template Val choose_by_def(const Val& def, const std::vector, Val>>& pairs, const Key& x) { return just_with_default(def, choose_by(pairs, x)); } // API search type: choose_def_lazy : ((() -> b), [(a, (() -> b))], a) -> b // fwd bind count: 1 // Evaluates a lazy value assigned to a key iff the key exists exactly once, // otherwise evaluates the given default lazy value. // choose_def_lazy(c, [(1,a), (2,b)], 2) == b(); // choose_def_lazy(c, [(1,a), (1,b)], 2) == c(); // choose_def_lazy(c, [(1,a), (2,b)], 3) == c(); template auto choose_def_lazy(const ValStub& def, const std::vector>& pairs, const Key& x) { return choose_def(def, pairs, x)(); } // API search type: choose_by_def_lazy : ((() -> b), [((a -> Bool), (() -> b))], a) -> b // fwd bind count: 2 // Iff exactly one predicate is fulfilled // the value assigned to this predicate is evaluated. // Otherwise the given default value is evaluated and returned. // choose_by_def_lazy(c, [(is_even,a), (is_bigger_than_3,b)], 2) == Just a(); // choose_by_def_lazy(c, [(is_even,a), (is_bigger_than_3,b)], 5) == Just b(); // choose_by_def_lazy(c, [(is_even,a), (is_bigger_than_3,b)], 1) == c(); // choose_by_def_lazy(c, [(is_even,a), (is_bigger_than_3,b)], 4) == c(); template auto choose_by_def_lazy( const ValStub& def, const std::vector, ValStub>>& pairs, const Key& x) { return choose_by_def(def, pairs, x)(); } } // namespace fplus libfplus-0.2.13/include/fplus/maybe.hpp000066400000000000000000000303131376322245400200070ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include namespace fplus { // Can hold a value of type T or nothing. template class maybe { public: bool is_just() const { return is_present_; } bool is_nothing() const { return !is_just(); } const T& unsafe_get_just() const { assert(is_just()); return *reinterpret_cast(&value_); } T& unsafe_get_just() { assert(is_just()); return *reinterpret_cast(&value_); } typedef T type; maybe() : is_present_(false), value_() {}; ~maybe() { destruct_content(); } maybe(const T& val_just) : is_present_(true), value_() { new (&value_) T(val_just); } maybe(T&& val_just) : is_present_(true), value_() { new (&value_) T(std::move(val_just)); } maybe(const maybe& other) : is_present_(other.is_just()), value_() { if (is_present_) { new (&value_) T(other.unsafe_get_just()); } } maybe(maybe&& other) : is_present_(std::move(other.is_present_)), value_() { if (is_present_) { new (&value_) T(std::move(other.unsafe_get_just())); } } maybe& operator = (const T& other) { destruct_content(); is_present_ = true; new (&value_) T(other); return *this; } maybe& operator = (T&& other) { destruct_content(); is_present_ = true; new (&value_) T(std::move(other)); return *this; } maybe& operator = (const maybe& other) { destruct_content(); if (other.is_just()) { is_present_ = true; new (&value_) T(other.unsafe_get_just()); } return *this; } maybe& operator = (maybe&& other) { destruct_content(); is_present_ = std::move(other.is_present_); if (is_present_) { new (&value_) T(std::move(other.unsafe_get_just())); } return *this; } private: void destruct_content() { if (is_present_) { is_present_ = false; (*reinterpret_cast(&value_)).~T(); } } bool is_present_; typename std::aligned_storage::type value_; }; namespace internal { template struct is_maybe : std::false_type { }; template struct is_maybe> : std::true_type { }; } // API search type: is_just : Maybe a -> Bool // fwd bind count: 0 // Is not nothing? template bool is_just(const maybe& maybe) { return maybe.is_just(); } // API search type: is_nothing : Maybe a -> Bool // fwd bind count: 0 // Has no value? template bool is_nothing(const maybe& maybe) { return !is_just(maybe); } // API search type: unsafe_get_just : Maybe a -> a // fwd bind count: 0 // Crashes if maybe is nothing! template T unsafe_get_just(const maybe& maybe) { return maybe.unsafe_get_just(); } // API search type: just_with_default : (a, Maybe a) -> a // fwd bind count: 0 // Get the value from a maybe or the default in case it is nothing. template T just_with_default(const T& defaultValue, const maybe& maybe) { if (is_just(maybe)) return unsafe_get_just(maybe); return defaultValue; } // API search type: throw_on_nothing : (e, Maybe a) -> a // fwd bind count: 1 // Throw exception if nothing. Return value if just. template T throw_on_nothing(const E& e, const maybe& maybe) { if (is_nothing(maybe)) throw e; return unsafe_get_just(maybe); } // API search type: just : a -> Maybe a // fwd bind count: 0 // Wrap a value in a Maybe as a Just. template maybe just(const T& val) { return val; } // API search type: as_just_if : ((a -> bool), a) -> Maybe a // fwd bind count: 1 // Wrap a value in a Maybe as a Just if the given predicate is fulfilled. // Otherwise a nothing is returned. template maybe as_just_if(Pred pred, const T& val) { internal::check_unary_predicate_for_type(); if (pred(val)) return val; else return {}; } // API search type: maybe_to_seq : Maybe a -> [a] // fwd bind count: 0 // Converts a maybe to a sequence. // singleton_seq(Just 3) == [3] // singleton_seq(Nothing) == [] template > ContainerOut maybe_to_seq(const maybe& maybe) { if (is_just(maybe)) return ContainerOut(1, unsafe_get_just(maybe)); return {}; } // API search type: singleton_seq_as_maybe : [a] -> Maybe a // fwd bind count: 0 // Converts a sequence to a maybe. // singleton_seq([]) == Nothing // singleton_seq([3]) == Just 3 // singleton_seq([3,4]) == Nothing template maybe singleton_seq_as_maybe(const Container& xs) { if (xs.size() == 1) return xs.front(); return {}; } // API search type: nothing : () -> Maybe a // Construct a nothing of a certain Maybe type. template maybe nothing() { return {}; } // True if just values are the same or if both are nothing. template bool operator == (const maybe& x, const maybe& y) { if (is_just(x) && is_just(y)) return unsafe_get_just(x) == unsafe_get_just(y); return is_just(x) == is_just(y); } // False if just values are the same or if both are nothing. template bool operator != (const maybe& x, const maybe& y) { return !(x == y); } // API search type: lift_maybe : ((a -> b), Maybe a) -> Maybe b // fwd bind count: 1 // Lifts a function into the maybe functor. // A function that for example was able to convert and int into a string, // now can convert a Maybe into a Maybe. // A nothing remains a nothing, regardless of the conversion. template auto lift_maybe(F f, const maybe& m) { internal::trigger_static_asserts(); using B = std::decay_t>; if (is_just(m)) return just(internal::invoke(f, unsafe_get_just(m))); return nothing(); } // API search type: lift_maybe_def : (b, (a -> b), Maybe a) -> b // fwd bind count: 2 // lift_maybe_def takes a default value and a function. // It returns a function taking a Maybe value. // This function returns the default value if the Maybe value is nothing. // Otherwise it applies the function to the value inside the Just // of the Maybe value and returns the result of this application. template auto lift_maybe_def(const Default& def, F f, const maybe& m) { internal::trigger_static_asserts(); using B = std::decay_t>; static_assert( std::is_convertible::value, "Default value must be convertible to Function's return type"); if (is_just(m)) return B(internal::invoke(f, unsafe_get_just(m))); return B(def); } // API search type: lift_maybe_2 : (((a, b) -> c), Maybe a, Maybe b) -> Maybe c // fwd bind count: 2 // Lifts a binary function into the maybe functor. // Applies the function only if both arguments are justs. // Otherwise returns a nothing. template auto lift_maybe_2(F f, const maybe& m_a, const maybe& m_b) { internal::trigger_static_asserts(); using FOut = std::decay_t>; if (is_just(m_a) && is_just(m_b)) { return just( internal::invoke(f, unsafe_get_just(m_a), unsafe_get_just(m_b))); } return nothing(); } // API search type: lift_maybe_2_def : (c, ((a, b) -> c), Maybe a, Maybe b) -> c // fwd bind count: 3 // lift_maybe_2_def takes a default value and a binary function. // It returns a function taking a two Maybe values. // This function returns the default value at least one of the // Maybe values is nothing. // Otherwise it applies the function to the two values inside the Justs // and returns the result of this application. template auto lift_maybe_2_def(const Default& def, F f, const maybe& m_a, const maybe& m_b) { internal::trigger_static_asserts(); using C = std::decay_t>; static_assert( std::is_convertible::value, "Default value must be convertible to Function's return type"); if (is_just(m_a) && is_just(m_b)) return C(internal::invoke(f, unsafe_get_just(m_a), unsafe_get_just(m_b))); return C(def); } // API search type: join_maybe : Maybe Maybe a -> Maybe a // Flattens a nested maybe. // join_maybe(Just Just x) == Just x // join_maybe(Just Nothing) == Nothing // join_maybe(Nothing) == Nothing template maybe join_maybe(const maybe>& m) { if (is_just(m)) return unsafe_get_just(m); else return nothing(); } // API search type: and_then_maybe : ((a -> Maybe b), (Maybe a)) -> Maybe b // fwd bind count: 1 // Monadic bind. // Returns nothing if the maybe already is nothing. // Otherwise return the result of applying // the function to the just value of the maybe. template auto and_then_maybe(F f, const maybe& m) { internal::trigger_static_asserts(); using FOut = std::decay_t>; static_assert(internal::is_maybe::value, "Function must return a maybe<> type"); if (is_just(m)) return internal::invoke(f, unsafe_get_just(m)); else return nothing(); } // API search type: compose_maybe : ((a -> Maybe b), (b -> Maybe c)) -> (a -> Maybe c) // Left-to-right Kleisli composition of monads. // Composes multiple callables taking a value and returning Maybe. // If the first callable returns a just, the value from the just // is extracted and shoved into the next callable. // If the first callable returns a nothing, it remains a nothing. // The first callable can take a variadic number of parameters. template auto compose_maybe(Callables&&... callables) { auto bind_maybe = [](auto f, auto g) { // next step would be to perfectly forward callables, as shown here: // https://vittorioromeo.info/index/blog/capturing_perfectly_forwarded_objects_in_lambdas.html return [f = std::move(f), g = std::move(g)](auto&&... args) { using FOut = std::decay_t< internal::invoke_result_t>; static_assert(internal::is_maybe::value, "Functions must return a maybe<> type"); using GOut = std::decay_t< internal::invoke_result_t>; static_assert(internal::is_maybe::value, "Functions must return a maybe<> type"); auto maybeB = internal::invoke(f, std::forward(args)...); if (is_just(maybeB)) return internal::invoke(g, unsafe_get_just(maybeB)); return GOut{}; }; }; return internal::compose_binary_lift(bind_maybe, std::forward(callables)...); } // API search type: flatten_maybe : (Maybe (Maybe a)) -> Maybe a // fwd bind count: 0 // Also known as join. template maybe flatten_maybe(const maybe>& maybe_maybe) { if (is_nothing(maybe_maybe)) return nothing(); return unsafe_get_just(maybe_maybe); } } // namespace fplus libfplus-0.2.13/include/fplus/numeric.hpp000066400000000000000000000751361376322245400203700ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include #include #include namespace fplus { // API search type: is_in_interval : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in [low, high), i.e. left-closed and right-open. template bool is_in_interval(const T& low, const T& high, const T& x) { return (low <= x) && (x < high); } // API search type: is_in_interval_around : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in [center - radius, center + radius), // i.e. left-closed and right-open. template bool is_in_interval_around(const T& radius, const T& center, const T& x) { return is_in_interval(center - radius, center + radius, x); } // API search type: is_in_open_interval : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in (low, high), i.e. left-open and right-open. template bool is_in_open_interval(const T& low, const T& high, const T& x) { return (low < x) && (x < high); } // API search type: is_in_open_interval_around : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in (center - radius, center + radius), // i.e. left-open and right-open. template bool is_in_open_interval_around(const T& radius, const T& center, const T& x) { return is_in_open_interval(center - radius, center + radius, x); } // API search type: is_in_closed_interval : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in [low, high], i.e. left-closed and right-closed. template bool is_in_closed_interval(const T& low, const T& high, const T& x) { return (low <= x) && (x <= high); } // API search type: is_in_closed_interval_around : (a, a, a) -> Bool // Checks if x is in [center - radius, center + radius], // i.e. left-closed and right-closed. template bool is_in_closed_interval_around(const T& radius, const T& center, const T& x) { return is_in_closed_interval(center - radius, center + radius, x); } // API search type: reference_interval : (Float, Float, Float, Float, Float) -> Float // fwd bind count: 4 // Linearly projects a value // from [old_low, old_high] into [new_low, new_high]. // Does not clamp. // reference_interval(2, 6, 0, 4, 3) == 5 // reference_interval(2, 10, 0, 4, 3) == 8 // reference_interval(2, 6, 0, 4, -1) == 1 // reference_interval(2, 10, 0, 4, -1) == 0 template T reference_interval(const T& new_low, const T& new_high, const T& old_low, const T& old_high, const T& x) { const T scale = (new_high - new_low) / (old_high - old_low); return scale * (x - old_low) + new_low; } // API search type: clamp : (a, a, a) -> a // fwd bind count: 2 // Puts value into [low, high], i.e. left-closed and right-closed. template T clamp(const T& low, const T& high, const T& x) { return std::max(low, std::min(high, x)); } // API search type: is_negative : a -> Bool // fwd bind count: 0 // Checks if x < 0. template bool is_negative(X x) { return x < 0; } // API search type: is_positive : a -> Bool // fwd bind count: 0 // Checks if x is not negative. template bool is_positive(X x) { return !is_negative(x); } // API search type: is_even : Int -> Bool // fwd bind count: 0 // Checks if x is even. template bool is_even(X x) { static_assert(std::is_integral::value, "type must be integral"); return x % 2 == 0; } // API search type: is_odd : Int -> Bool // fwd bind count: 0 // Checks if x is odd. template bool is_odd(X x) { static_assert(std::is_integral::value, "type must be integral"); return x % 1 == 0; } namespace internal { template typename std::enable_if::value, X>::type abs_helper(X x) { return x; } template typename std::enable_if::value, X>::type abs_helper(X x) { return std::abs(x); } } // API search type: abs : a -> a // fwd bind count: 0 // Returns the absolute (always non-negative) value of x. template X abs(X x) { return internal::abs_helper(x); } // API search type: abs_diff : (a, a) -> a // fwd bind count: 1 // Returns the absolute difference of two values. template X abs_diff(X a, X b) { return a > b ? a - b : b - a; } // API search type: square : a -> a // fwd bind count: 0 // Returns the square (x*x) of a value x. template X square(X x) { return x * x; } // API search type: cube : a -> a // fwd bind count: 0 // Returns the cube (x*x*x) of a value x. template X cube(X x) { return x * x * x; } // API search type: sign : a -> Int // fwd bind count: 0 // Returns -1 for negative values, 1 otherwise. // sign(-3) == -1 // sign(0) == 1 // sign(16) == 1 template int sign(X x) { return is_negative(x) ? -1 : 1; } // API search type: sign_with_zero : a -> Int // fwd bind count: 0 // Returns -1 for negative values, 0 for zero, 1 for positive values. // sign_with_zero(-3) == -1 // sign_with_zero(0) == 0 // sign_with_zero(16) == 1 template int sign_with_zero(X x) { return x == 0 ? 0 : sign(x); } // API search type: integral_cast_throw : Int -> Int // fwd bind count: 0 // Converts one integer type into another. // Throws an std::underflow_error or std::overflow_error // if the value does not fit into the destination type. template Out integral_cast_throw(X x) { #ifdef _MSC_VER __pragma(warning(push)) __pragma(warning(disable:4127)) #endif static_assert(std::is_integral::value, "type must be integral"); static_assert(std::is_integral::value, "type must be integral"); if (std::is_signed::value && std::is_signed::value) { if (static_cast(x) < static_cast(std::numeric_limits::lowest())) { throw std::underflow_error(""); } if (static_cast(x) > static_cast(std::numeric_limits::max())) { throw std::overflow_error(""); } return static_cast(x); } else if (!std::is_signed::value && !std::is_signed::value) { if (static_cast(x) < static_cast(std::numeric_limits::lowest())) { throw std::underflow_error(""); } if (static_cast(x) > static_cast(std::numeric_limits::max())) { throw std::overflow_error(""); } return static_cast(x); } else if (std::is_signed::value && !std::is_signed::value) { if (x < 0) return 0; if (static_cast(x) > static_cast(std::numeric_limits::max())) { throw std::overflow_error(""); } return static_cast(x); } else if (!std::is_signed::value && std::is_signed::value) { if (static_cast(x) > static_cast(std::numeric_limits::max())) { throw std::overflow_error(""); } return static_cast(x); } else { assert(false); return static_cast(x); } #ifdef _MSC_VER __pragma(warning(pop)) #endif } // API search type: integral_cast_clamp : Int -> Int // fwd bind count: 0 // Converts one integer type into another. // If the value does not fit into the destination type, // the nearest possible value is used. // Also known as saturate_cast. template Out integral_cast_clamp(X x) { static_assert(std::is_integral::value, "type must be integral"); static_assert(std::is_integral::value, "type must be integral"); if (std::is_signed::value && std::is_signed::value) { if (static_cast(x) < static_cast(std::numeric_limits::lowest())) { return std::numeric_limits::lowest(); } if (static_cast(x) > static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(x); } else if (!std::is_signed::value && !std::is_signed::value) { if (static_cast(x) < static_cast(std::numeric_limits::lowest())) { return std::numeric_limits::lowest(); } if (static_cast(x) > static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(x); } else if (std::is_signed::value && !std::is_signed::value) { if (x < 0) return 0; if (static_cast(x) > static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(x); } else if (!std::is_signed::value && std::is_signed::value) { if (static_cast(x) > static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(x); } else { assert(false); return static_cast(x); } } // API search type: round : a -> Int // fwd bind count: 0 // Converts a value to the nearest integer. template Out round(X x) { static_assert(!std::is_integral::value, "type must be non-integral"); static_assert(std::is_integral::value, "type must be integral"); if (static_cast(x) < static_cast(std::numeric_limits::lowest())) return std::numeric_limits::lowest(); if (static_cast(x) > static_cast(std::numeric_limits::max())) return std::numeric_limits::max(); if (is_negative(x)) x -= 1; return static_cast(x + 0.5); } // API search type: floor : a -> b // fwd bind count: 0 // Converts a value to the nearest smaller integer. template Out floor(X x) { static_assert(!std::is_integral::value, "type must be non-integral"); static_assert(std::is_integral::value, "type must be integral"); if (is_negative(x)) x -= 1; return static_cast(x); } // API search type: floor_to_int_mult : (Int, Int) -> Int // fwd bind count: 1 // Rounds an integer down to the nearest smaller or equal multiple of n. // n may not be zero. template X floor_to_int_mult(X n, X x) { static_assert(std::is_integral::value, "type must be integral"); assert(n != 0); if (is_negative(n)) n = abs(n); if (is_negative(x) && n != 1) x = static_cast(x - 1); return static_cast((x / n) * n); } // API search type: ceil_to_int_mult : (Int, Int) -> Int // fwd bind count: 1 // Rounds an integer up to the nearest greater or equal multiple of n. // n may not be zero. template X ceil_to_int_mult(X n, X x) { return floor_to_int_mult(n, static_cast(x + abs(n) - 1)); } // API search type: ceil : a -> b // fwd bind count: 0 // Converts a value to the nearest greater integer. template Out ceil(X x) { static_assert(!std::is_integral::value, "type must be non-integral"); static_assert(std::is_integral::value, "type must be integral"); return floor(x) + 1; } // API search type: int_power : (Int, Int) -> Int // fwd bind count: 1 // integer power, only exponents >= 0 template X int_power(X base, X exp) { static_assert(std::is_integral::value, "type must be unsigned integral"); assert(!is_negative(exp)); if (exp == 0) return 1; if (exp == 1) return base; return base * int_power(base, exp - 1); } namespace internal { // minimum of x values after transformation // (has an overload for non-POD types) // min_on(mod2, 4, 3) == 4 // min_on(mod7, 3, 5, 7, 3) == 7 template auto helper_min_on(F f, const FirstT& first, const FIn&... v) -> typename std::common_type::type { using rettype = typename std::common_type::type; using f_rettype = std::decay_t>; rettype result = first; f_rettype result_trans = internal::invoke(f, first); f_rettype v_trans; unused(result_trans); unused(v_trans); (void)std::initializer_list{ ((v_trans = internal::invoke(f, v), v_trans < result_trans) ? (result = static_cast(v), result_trans = v_trans, 0) : 0)...}; return result; } template struct helper_min_on_t { helper_min_on_t(F _f) : f(_f) {} template auto operator()(T&& x, Ts&&... xs) -> typename std::common_type::type { return helper_min_on(std::forward(f), std::forward(x), std::forward(xs)...); } private: F f; }; } // API search type: min_on : ((a -> b), a, a) -> a // minimum of x values after transformation (curried version) // min_on(mod2)(4, 3) == 4 // min_on(mod7)(3, 5, 7, 3) == 7 template auto min_on(F f) -> internal::helper_min_on_t { return internal::helper_min_on_t{f}; } // API search type: min_2_on : ((a -> b), a, a) -> a // fwd bind count: 2 // minimum of 2 values after transformation // min_2_on(mod2, 4, 3) == 4 template T min_2_on(F f, const T& x, const T& y) { return internal::invoke(f, y) < internal::invoke(f, x) ? y : x; } namespace internal { // maximum of x values after transformation // (has an overload for non-POD types) // max_on(mod2, 4, 3) == 3 // max_on(mod7, 3, 5, 7, 3) == 5 template auto helper_max_on(F f, const FirstT& first, const FIn&... v) -> typename std::common_type::type { using rettype = typename std::common_type::type; using f_rettype = decltype(f(first)); rettype result = first; f_rettype result_trans = internal::invoke(f, first); f_rettype v_trans; unused(result_trans); unused(v_trans); (void)std::initializer_list{ ((v_trans = internal::invoke(f, v), v_trans > result_trans) ? (result = static_cast(v), result_trans = v_trans, 0) : 0)...}; return result; } template struct helper_max_on_t { helper_max_on_t(F _f) : f(_f) {} template auto operator()(T&& x, Ts&&... xs) -> typename std::common_type::type { return helper_max_on(std::forward(f), std::forward(x), std::forward(xs)...); } private: F f; }; } // API search type: max_on : (a -> b) -> ((a, a) -> a) // maximum of x values after transformation (curried version) // (has an overload for non POD types) // max_on(mod2)(4, 3) == 3 // max_on(mod7)(3, 5, 7, 3) == 5 template auto max_on(F f) -> internal::helper_max_on_t { return internal::helper_max_on_t{f}; } // API search type: max_2_on : ((a -> b), a, a) -> a // fwd bind count: 2 // maximum of 2 values after transformation // max_2_on(mod2, 4, 3) == 3 template T max_2_on(F f, const T& x, const T& y) { return internal::invoke(f, y) > internal::invoke(f, x) ? y : x; } // API search type: min : (a, a) -> a // Minimum of x number of values // min(4, 3) == 3 // min(4, 3, 6, 2, 3) == 2 template auto min(const U& u, const V&... v) -> typename std::common_type::type { using rettype = typename std::common_type::type; rettype result = static_cast(u); (void)std::initializer_list{((v < result) ? (result = static_cast(v), 0) : 0)...}; return result; } // API search type: min_2 : (a, a) -> a // fwd bind count: 1 // minimum of 2 values // min_2(4, 3) == 3 template T min_2(const T& x, const T& y) { return y < x ? y : x; } // API search type: max : (a, a) -> a // Maximum of x number of values. // max(4, 3) == 4 // max(4, 3, 6, 2, 3) == 6 template auto max(const U& u, const V&... v) -> typename std::common_type::type { using rettype = typename std::common_type::type; rettype result = static_cast(u); (void)std::initializer_list{((v > result) ? (result = static_cast(v), 0) : 0)...}; return result; } // API search type: max_2 : (a, a) -> a // fwd bind count: 1 // maximum of 2 values // max_2(4, 3) == 4 template T max_2(const T& x, const T& y) { return y > x ? y : x; } namespace internal { template typename std::enable_if::value, X>::type cyclic_value_helper_mod(X x, X y) { return std::fmod(x, y); } template typename std::enable_if::value, X>::type cyclic_value_helper_mod(X x, X y) { return x % y; } } // API search type: cyclic_value : a -> (a -> a) // Modulo for floating point values. // circumfence must be > 0 // cyclic_value(8)(3) == 3 // cyclic_value(8)(11) == 3 // cyclic_value(8)(19) == 3 // cyclic_value(8)(-2) == 6 // cyclic_value(8)(-5) == 3 // cyclic_value(8)(-13) == 3 // Can be useful to normalize an angle into [0, 360] // For positive values it behaves like std::fmod with flipped arguments. template std::function cyclic_value(X circumfence) { assert(circumfence > 0); return [circumfence](X x) -> X { if (sign(x) < 0) return circumfence - internal::cyclic_value_helper_mod( abs(x), abs(circumfence)); else return internal::cyclic_value_helper_mod( abs(x), abs(circumfence)); }; } // API search type: cyclic_difference : a -> ((a, a) -> a) // Returns the distance the first value has to advance forward on a circle // to reach the second value. // circumfence must be > 0 // cyclic_difference(100)(5, 2) == 3 // cyclic_difference(100)(2, 5) == 97 // cyclic_difference(100)(3, -2) == 5 // cyclic_difference(100)(-2, 3) == 95 // cyclic_difference(100)(90, 10) == 80 // cyclic_difference(100)(10, 90) == 20 template std::function cyclic_difference(X circumfence) { assert(circumfence > 0); return [circumfence](X a, X b) -> X { auto cyclic_value_f = cyclic_value(circumfence); const auto c_v_a = cyclic_value_f(a); const auto c_v_b = cyclic_value_f(b); return c_v_a > c_v_b ? c_v_a - c_v_b : circumfence + c_v_a - c_v_b; }; } // API search type: cyclic_shortest_difference : a -> ((a, a) -> a) // Returns displacement (shortest way) the first value has to move on a circle // to reach the second value. // circumfence must be > 0 // cyclic_shortest_difference(100)(5, 2) == 3 // cyclic_shortest_difference(100)(2, 5) == -3 // cyclic_shortest_difference(100)(3, -2) == 5 // cyclic_shortest_difference(100)(-2, 3) == -5 // cyclic_shortest_difference(100)(90, 10) == -20 // cyclic_shortest_difference(100)(10, 90) == 20 template std::function cyclic_shortest_difference(X circumfence) { assert(circumfence > 0); return [circumfence](X a, X b) -> X { auto diff_func = cyclic_difference(circumfence); auto a_minus_b = diff_func(a, b); auto b_minus_a = diff_func(b, a); return a_minus_b <= b_minus_a ? a_minus_b : -b_minus_a; }; } // API search type: cyclic_distance : a -> ((a, a) -> a) // Returns distance (shortest way) the first value has to move on a circle // to reach the second value. // circumfence must be > 0 // cyclic_distance(100)(2, 5) == 3 // cyclic_distance(100)(5, 2) == 3 // cyclic_distance(100)(-2, 3) == 5 // cyclic_distance(100)(3, -2) == 5 // cyclic_distance(100)(10, 90) == 20 // cyclic_distance(100)(90, 10) == 20 // Can be useful to calculate the difference of two angles; template std::function cyclic_distance(X circumfence) { assert(circumfence > 0); return [circumfence](X a, X b) -> X { auto diff_func = cyclic_difference(circumfence); auto a_minus_b = diff_func(a, b); auto b_minus_a = diff_func(b, a); return a_minus_b <= b_minus_a ? a_minus_b : b_minus_a; }; } // API search type: pi : () -> Float // Pi. constexpr inline double pi() { return 3.14159265358979323846; } // API search type: deg_to_rad : Float -> Float // fwd bind count: 0 // converts degrees to radians template T deg_to_rad(T x) { static_assert(std::is_floating_point::value, "Please use a floating-point type."); return static_cast(x * pi() / 180.0); } // API search type: rad_to_deg : Float -> Float // fwd bind count: 0 // converts radians to degrees template T rad_to_deg(T x) { static_assert(std::is_floating_point::value, "Please use a floating-point type."); return static_cast(x * 180.0 / pi()); } namespace internal { template Container normalize_min_max(internal::reuse_container_t, const T& lower, const T& upper, Container&& xs) { assert(size_of_cont(xs) != 0); assert(lower <= upper); const auto minmax_it_p = std::minmax_element(std::begin(xs), std::end(xs)); const T x_min = *minmax_it_p.first; const T x_max = *minmax_it_p.second; const auto f = [&](const T& x) -> T { return lower + (upper - lower) * (x - x_min) / (x_max - x_min); }; std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); return std::forward(xs); } template Container normalize_min_max(internal::create_new_container_t, const T& lower, const T& upper, const Container& xs) { auto ys = xs; return normalize_min_max(internal::reuse_container_t(), lower, upper, std::move(ys)); } } // namespace internal // API search type: normalize_min_max : (a, a, [a]) -> [a] // fwd bind count: 2 // Linearly scales the values into the given interval. // normalize_min_max(0, 10, [1, 3, 6]) == [0, 4, 10] // It is recommended to convert integers to double beforehand. template ::value_type> auto normalize_min_max(const T& lower, const T& upper, Container&& xs) { return internal::normalize_min_max(internal::can_reuse_v{}, lower, upper, std::forward(xs)); } namespace internal { template Container normalize_mean_stddev(internal::reuse_container_t, const T& mean, const T& stddev, Container&& xs) { assert(size_of_cont(xs) != 0); const auto mean_and_stddev = fplus::mean_stddev(xs); const auto f = [&](const T& x) -> T { return mean + stddev * (x - mean_and_stddev.first) / mean_and_stddev.second; }; std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); return std::forward(xs); } template Container normalize_mean_stddev(internal::create_new_container_t, const T& mean, const T& stddev, const Container& xs) { auto ys = xs; return normalize_mean_stddev(internal::reuse_container_t(), mean, stddev, std::move(ys)); } } // namespace internal // API search type: normalize_mean_stddev : (a, a, [a]) -> [a] // fwd bind count: 2 // Linearly scales the values // to match the given mean and population standard deviation. // normalize_mean_stddev(3, 2, [7, 8]) == [1, 5] template ::value_type> auto normalize_mean_stddev( const T& mean, const T& stddev, Container&& xs) { return internal::normalize_mean_stddev(internal::can_reuse_v{}, mean, stddev, std::forward(xs)); } // API search type: standardize : [a] -> [a] // fwd bind count: 0 // Linearly scales the values to zero mean and population standard deviation 1. // standardize([7, 8]) == [-1, 1] template auto standardize(Container&& xs) { typedef typename internal::remove_const_and_ref_t::value_type T; T mean(0); T stddev(1); return normalize_mean_stddev(mean, stddev, std::forward(xs)); } // API search type: add_to : a -> (a -> a) // Provide a function adding to a given constant. // add_to(3)(2) == 5 template std::function add_to(const X& x) { return [x](X y) -> X { return x + y; }; } // API search type: subtract_from : a -> (a -> a) // Provide a function subtracting from a given constant. // subtract_from(3)(2) == 1 template std::function subtract_from(const X& x) { return [x](X y) -> X { return x - y; }; } // API search type: subtract : a -> (a -> a) // Provide a function subtracting a given constant. // subtract(2)(3) == 1 template std::function subtract(const X& x) { return [x](X y) -> X { return y - x; }; } // API search type: multiply_with : a -> (a -> a) // Provide a function multiplying with a given constant. // multiply_with(3)(2) == 6 template std::function multiply_with(const X& x) { return [x](X y) -> X { return y * x; }; } // API search type: divide_by : a -> (a -> a) // Provide a function dividing by a given constant. // divide_by(2)(6) == 3 template std::function divide_by(const X& x) { return [x](X y) -> X { return y / x; }; } // API search type: histogram_using_intervals : ([(a, a)], [a]) -> [((a, a), Int)] // fwd bind count: 1 // Generate a histogram of a sequence with given bins. // histogram_using_intervals([(0,4), (4,5), (6,8)], [0,1,4,5,6,7,8,9]) == // [((0, 4), 2), ((4, 5), 1), ((6, 8), 2)] template >, typename T = typename ContainerIn::value_type> ContainerOut histogram_using_intervals( const ContainerIntervals& intervals, const ContainerIn& xs) { ContainerOut bins; internal::prepare_container(bins, size_of_cont(intervals)); auto itOut = internal::get_back_inserter(bins); for (const auto& interval : intervals) { *itOut = std::make_pair(interval, 0); } for (const auto& x : xs) { for (auto& bin : bins) { if (x >= bin.first.first && x < bin.first.second) { ++bin.second; } } } return bins; } // API search type: generate_consecutive_intervals : (a, a, a) -> [(a, a)] // fwd bind count: 2 // Return intervals of a given size adjacent to each other // generate_consecutive_intervals(0, 2, 4) == [(0,2), (2,4), (4,6), (6,8)] template std::vector> generate_consecutive_intervals( const T& first_lower_bound, const T& step, std::size_t count) { const auto count_as_T = static_cast(count); return zip( numbers_step( first_lower_bound, first_lower_bound + count_as_T * step, step), numbers_step( first_lower_bound + step, first_lower_bound + step + count_as_T * step, step)); } // API search type: histogram : (a, a, a, [a]) -> [((a, a), Int)] // fwd bind count: 3 // Calculate the histogram of a sequence using a given bin width. // histogram(1, 2, 4, [0,1,4,5,7,8,9]) == [(1, 2), (3, 0), (5, 2), (7, 1)] template >, typename T = typename ContainerIn::value_type> ContainerOut histogram( const T& first_center, const T& bin_width, std::size_t count, const ContainerIn& xs) { const auto interval_histogram = histogram_using_intervals( generate_consecutive_intervals( first_center - bin_width / 2, bin_width, count), xs); assert(size_of_cont(interval_histogram) == count); ContainerOut histo; internal::prepare_container(histo, count); auto itOut = internal::get_back_inserter(histo); for (const auto& bin : interval_histogram) { const auto current_center = (bin.first.first + bin.first.second) / 2; *itOut = std::make_pair(current_center, bin.second); } return histo; } // API search type: modulo_chain : ([Int], Int) -> [Int] // fwd bind count: 1 // For every factor (value % factor) is pushed into the result, // and value is divided by this factor for the next iteration. // Can be useful to convert a time in seconds // into hours, minutes and seconds and similar calculations. // modulo_chain([24, 60, 60], 7223) == [0, 2, 0, 23] template std::vector modulo_chain(const std::vector& factors, T val) { std::vector result; result.reserve(factors.size()); const auto factors_reversed = reverse(factors); for (const auto& factor : factors_reversed) { result.push_back(val % factor); val /= factor; } result.push_back(val); return reverse(result); } // API search type: line_equation : ((Float, Float), (Float, Float), Float) -> Float // fwd bind count: 2 // Can be used to interpolate and to extrapolate // based on two given two-dimensional points (x, y). // Using slope, return NaN if x_1 == x_2. // line_equation((0.0, 0.0), (2.0, 1.0), 3.0) == 1.5 // line_equation((-1.0, 1.0), (-2.0, 4.0), 0.0) == -2.0 template T line_equation(const std::pair& a, const std::pair& b, T x) { static_assert(std::is_floating_point::value, "Please use a floating-point type."); const double m = (b.second - a.second) / (b.first - a.first); return m * x + a.second - m * a.first; } } // namespace fplus libfplus-0.2.13/include/fplus/optimize.hpp000066400000000000000000000135411376322245400205560ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include namespace fplus { // Optimizes the initial position to the nearest local minimum // in regards to the objective_function // using numerical gradient descent based on the epsilon neighborhood. // momentum_conservation should be in [0, 1). A low value means much decay. // If no fixed step size is provided, each step advances by the length // of the gradient. // In both cases the step is scaled with a step factor, starting at 1.0. // If one iteration results in no further improvement, // the step factor is reduced by a factor of 0.5. // The callback is executed with // iteration, step factor, momentum and current position // after every iteration. // A initial step factor other than 1.0 in all dimensions // can be emulated by scaling ones objective function accordingly. // Optimization stops if one of the provided criteria is met. // minimize_downhill<1>(\x -> square(x[0] + 2), 0.0001, 0.01, {123})[0] == -2; template > pos_t minimize_downhill( F objective_function, double epsilon, const pos_t& init_pos, maybe fixed_step_size = nothing(), double momentum_conservation = 0.5, double sufficing_value = std::numeric_limits::lowest(), double min_step_factor = std::numeric_limits::min(), std::size_t max_iterations = std::numeric_limits::max(), long int max_milliseconds = std::numeric_limits::max(), const std::function< void (std::size_t, double, const pos_t&, const pos_t&)>& callback = std::function< void (std::size_t, double, const pos_t&, const pos_t&)>()) { std::size_t iteration = 0; double step_factor = 1.0; pos_t position = init_pos; double value = internal::invoke(objective_function, position); const auto start_time = std::chrono::steady_clock::now(); const auto is_done = [&]() -> bool { if (max_milliseconds != std::numeric_limits::max()) { const auto current_time = std::chrono::steady_clock::now(); const auto elapsed = current_time - start_time; const auto elapsed_ms = std::chrono::duration_cast(elapsed).count(); if (elapsed_ms >= max_milliseconds) { return true; } } return iteration >= max_iterations || step_factor <= min_step_factor || value <= sufficing_value; }; const auto calc_gradient = [&](const pos_t& pos) -> pos_t { pos_t result; for (std::size_t dim = 0; dim < N; ++dim) { auto test_pos_1 = pos; auto test_pos_2 = pos; test_pos_1[dim] -= epsilon / 2.0; test_pos_2[dim] += epsilon / 2.0; const auto val_1 = internal::invoke(objective_function, test_pos_1); const auto val_2 = internal::invoke(objective_function, test_pos_2); result[dim] = (val_2 - val_1) / epsilon; } return result; }; const auto add = [](const pos_t& p1, const pos_t& p2) -> pos_t { pos_t result; for (std::size_t dim = 0; dim < N; ++dim) { result[dim] = p1[dim] + p2[dim]; } return result; }; const auto multiply = [](const pos_t& p, double f) -> pos_t { pos_t result; for (std::size_t dim = 0; dim < N; ++dim) { result[dim] = p[dim] * f; } return result; }; const auto dist_to_origin = [](const pos_t& p) -> double { double acc = 0; for (std::size_t dim = 0; dim < N; ++dim) { acc += square(p[dim]); } return sqrt(acc); }; const auto normalize = [&](const pos_t& p) -> pos_t { return multiply(p, 1.0 / dist_to_origin(p)); }; const auto null_vector = []() -> pos_t { pos_t result; for (std::size_t dim = 0; dim < N; ++dim) { result[dim] = 0; } return result; }; pos_t momentum = null_vector(); while (!is_done()) { auto new_momentum = multiply(momentum, momentum_conservation); pos_t gradient = calc_gradient(add(position, new_momentum)); const auto inverse_gradient = multiply(gradient, -1.0); auto new_momentum_add = is_nothing(fixed_step_size) ? inverse_gradient : multiply( normalize(inverse_gradient), fixed_step_size.unsafe_get_just()); new_momentum = multiply( add(new_momentum, new_momentum_add), step_factor); if (dist_to_origin(momentum) <= std::numeric_limits::min() && dist_to_origin(new_momentum) <= std::numeric_limits::min()) { break; } const auto new_position = add(position, new_momentum); const auto new_value = internal::invoke(objective_function, new_position); if (new_value >= value) { step_factor /= 2.0; } else { value = new_value; position = new_position; momentum = new_momentum; } ++iteration; if (callback) { callback(iteration, step_factor, momentum, position); } } return position; } } // namespace fplus libfplus-0.2.13/include/fplus/pairs.hpp000066400000000000000000000620431376322245400200350ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include namespace fplus { // API search type: apply_to_pair : (((a, b) -> c), (a, b)) -> c // fwd bind count: 1 // Apply binary function to parts of a pair. template auto apply_to_pair(F f, const std::pair& p) { internal::trigger_static_asserts(); return internal::invoke(f, p.first, p.second); } // API search type: zip_with : (((a, b) -> c), [a], [b]) -> [c] // fwd bind count: 2 // Zip two sequences using a binary function. // zip_with((+), [1, 2, 3], [5, 6]) == [1+5, 2+6] == [6, 8] template >, typename ContainerOut = std::vector> ContainerOut zip_with(F f, const ContainerIn1& xs, const ContainerIn2& ys) { internal::trigger_static_asserts(); ContainerOut result; std::size_t resultSize = std::min(size_of_cont(xs), size_of_cont(ys)); internal::prepare_container(result, resultSize); auto itResult = internal::get_back_inserter(result); auto itXs = std::begin(xs); auto itYs = std::begin(ys); for (std::size_t i = 0; i < resultSize; ++i) { *itResult = internal::invoke(f, *itXs, *itYs); ++itXs; ++itYs; } return result; } // API search type: zip_with_3 : (((a, b, c) -> d), [a], [b], [c]) -> [c] // fwd bind count: 3 // Zip three sequences using a ternary function. // zip_with_3((+), [1, 2, 3], [5, 6], [1, 1]) == [7, 9] template < typename ContainerIn1, typename ContainerIn2, typename ContainerIn3, typename F, typename X = typename ContainerIn1::value_type, typename Y = typename ContainerIn2::value_type, typename Z = typename ContainerIn3::value_type, typename TOut = std::decay_t>, typename ContainerOut = typename std::vector> ContainerOut zip_with_3(F f, const ContainerIn1& xs, const ContainerIn2& ys, const ContainerIn3& zs) { internal::trigger_static_asserts(); static_assert(std::is_same< typename internal::same_cont_new_t::type, typename internal::same_cont_new_t::type>::value, "All three Containers must be of same outer type."); static_assert(std::is_same< typename internal::same_cont_new_t::type, typename internal::same_cont_new_t::type>::value, "All three Containers must be of same outer type."); ContainerOut result; std::size_t resultSize = std::min(size_of_cont(xs), size_of_cont(ys)); internal::prepare_container(result, resultSize); auto itResult = internal::get_back_inserter(result); auto itXs = std::begin(xs); auto itYs = std::begin(ys); auto itZs = std::begin(zs); for (std::size_t i = 0; i < resultSize; ++i) { *itResult = internal::invoke(f, *itXs, *itYs, *itZs); ++itXs; ++itYs; ++itZs; } return result; } // API search type: zip_with_defaults : (((a, b) -> c), a, b, [a], [b]) -> [c] // fwd bind count: 4 // Zip two sequences and using a binary function // and extrapolate the shorter sequence with a default value. // zip_with_defaults((+), 6, 7, [1,2,3], [1,2]) == [2,4,10] // zip_with_defaults((+), 6, 7, [1,2], [1,2,3]) == [2,4,9] template < typename ContainerIn1, typename ContainerIn2, typename F, typename X = typename ContainerIn1::value_type, typename Y = typename ContainerIn2::value_type> auto zip_with_defaults(F f, const X& default_x, const Y& default_y, const ContainerIn1& xs, const ContainerIn2& ys) { internal::trigger_static_asserts(); const auto size_xs = size_of_cont(xs); const auto size_ys = size_of_cont(ys); if (size_xs < size_ys) { const auto extended_xs = append( xs, replicate(size_ys - size_xs, default_x)); return zip_with(f, extended_xs, ys); } else if (size_xs > size_ys) { const auto extended_ys = append( ys, replicate(size_xs - size_ys, default_y)); return zip_with(f, xs, extended_ys); } return zip_with(f, xs, ys); } // API search type: zip : ([a], [b]) -> [(a, b)] // fwd bind count: 1 // Combine two sequences to one sequence of pairs. // zip([1, 2, 3], [5, 6]) == [(1, 5), (2, 6)] template auto zip(const ContainerIn1& xs, const ContainerIn2& ys) { auto MakePair = [](const X& x, const Y& y) { return std::make_pair(x, y); }; return zip_with(MakePair, xs, ys); } // API search type: unzip : [(a, b)] -> ([a], [b]) // fwd bind count: 0 // Split a sequence of pairs into two sequences. // unzip([(1, 5), (2, 6)]) == ([1, 2], [5, 6]) template ::type, typename ContainerOutY = typename internal::same_cont_new_t::type> std::pair unzip(const ContainerIn& pairs) { ContainerOutX firsts; ContainerOutY seconds; internal::prepare_container(firsts, size_of_cont(pairs)); internal::prepare_container(seconds, size_of_cont(pairs)); auto itFirsts = internal::get_back_inserter(firsts); auto itSeconds = internal::get_back_inserter(seconds); for (const auto& pair : pairs) { *itFirsts = pair.first; *itSeconds = pair.second; } return std::make_pair(firsts, seconds); } // API search type: fst : ((a, b)) -> a // fwd bind count: 0 // Return the first element of a pair. // fst((0, 1)) == 0 template X fst(const std::pair& pair) { return pair.first; } // API search type: snd : ((a, b)) -> b // fwd bind count: 0 // Return the second element of a pair. // snd((0, 1)) == 1 template Y snd(const std::pair& pair) { return pair.second; } // API search type: transform_fst : ((a -> c), (a, b)) -> (c, b) // fwd bind count: 1 // Apply a function to the first element of a pair. // transform_fst(square, (4, 5)) == (16, 5) template >> std::pair transform_fst(F f, const std::pair& pair) { internal::trigger_static_asserts(); return std::make_pair(internal::invoke(f, pair.first), pair.second); } // API search type: transform_snd : ((b -> c), (a, b)) -> (a, c) // fwd bind count: 1 // Apply a function to the second element of a pair. // transform_snd(square, (4, 5)) == (4, 25) template >> std::pair transform_snd(F f, const std::pair& pair) { internal::trigger_static_asserts(); return std::make_pair(pair.first, internal::invoke(f, pair.second)); } // API search type: transform_pair : ((a -> c), (b -> d), (a, b)) -> (c, d) // fwd bind count: 2 // Apply functions the both parts of a pair. // transform_pair(square, square, (4, 5)) == (16, 25) template < typename X, typename Y, typename F, typename G, typename ResultFirst = std::decay_t>, typename ResultSecond = std::decay_t>> std::pair transform_pair(F f, G g, const std::pair& pair) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); return std::make_pair(internal::invoke(f, pair.first), internal::invoke(g, pair.second)); } // API search type: swap_pair_elems : (a, b) -> (b, a) // fwd bind count: 0 // Swap the first and the second element of a pair. // swap_pair_elems((3,4)) == (4,3) template std::pair swap_pair_elems(const std::pair& pair) { return std::make_pair(pair.second, pair.first); } // API search type: swap_pairs_elems : [(a, b)] -> [(b, a)] // fwd bind count: 0 // Swap the first and the second element of every pair in a sequence. // swap_pairs_elems([(1,2), (3,4)]) == [(2,1), (4,3)] template auto swap_pairs_elems(const ContainerIn& xs) { return fplus::transform(swap_pair_elems, xs); } // API search type: adjacent_pairs : [a] -> [(a, a)] // fwd bind count: 0 // Split a sequence into pairs of elements. // adjacent_pairs([0,1,2,3,4]) == [(0,1), (2,3)] // Also known as zip_with_next. template >::type> ContainerOut adjacent_pairs(const Container& xs) { typedef typename Container::value_type T; static_assert(std::is_convertible< std::pair, typename ContainerOut::value_type>::value, "ContainerOut can not store pairs of elements from ContainerIn."); ContainerOut result; if (size_of_cont(xs) < 2) return result; const std::size_t out_size = size_of_cont(xs) / 2; internal::prepare_container(result, out_size); auto itOut = internal::get_back_inserter(result); auto it1 = std::begin(xs); auto it2 = it1; internal::advance_iterator(it2, 1); const auto it_source_end = internal::add_to_iterator(std::begin(xs), out_size + out_size); for (;;) { *itOut = std::make_pair(*it1, *it2); internal::advance_iterator(it1, 2); if (it1 == it_source_end) break; internal::advance_iterator(it2, 2); } return result; } // API search type: overlapping_pairs : [a] -> [(a, a)] // fwd bind count: 0 // Zip a sequence with itself shifted one element. // overlapping_pairs([0,1,2,3]) == [(0,1),(1,2),(2,3)] template , -1>::type> ContainerOut overlapping_pairs(const Container& xs) { typedef typename Container::value_type T; static_assert(std::is_convertible< std::pair, typename ContainerOut::value_type>::value, "ContainerOut can not store pairs of elements from ContainerIn."); ContainerOut result; if (size_of_cont(xs) < 2) return result; internal::prepare_container(result, size_of_cont(xs) - 1); auto itOut = internal::get_back_inserter(result); auto it1 = std::begin(xs); auto it2 = it1; internal::advance_iterator(it2, 1); for (; it2 != std::end(xs); ++it1, ++it2) { *itOut = std::make_pair(*it1, *it2); } return result; } // API search type: overlapping_pairs_cyclic : [a] -> [(a, a)] // fwd bind count: 0 // Zip a sequence with itself shifted one element, // finally zipping the last element with the first one. // overlapping_pairs_cyclic([0,1,2,3]) == [(0,1),(1,2),(2,3),(3,0)] template , 0>::type> ContainerOut overlapping_pairs_cyclic(const Container& xs) { typedef typename Container::value_type T; static_assert(std::is_convertible< std::pair, typename ContainerOut::value_type>::value, "ContainerOut can not store pairs of elements from ContainerIn."); ContainerOut result; if (size_of_cont(xs) < 2) return result; internal::prepare_container(result, size_of_cont(xs)); auto itOut = internal::get_back_inserter(result); auto it1 = std::begin(xs); auto it2 = it1; internal::advance_iterator(it2, 1); for (; it2 != std::end(xs); ++it1, ++it2) { *itOut = std::make_pair(*it1, *it2); } *itOut = std::make_pair(*it1, xs.front()); return result; } // API search type: enumerate : [a] -> [(Int, a)] // fwd bind count: 0 // Attach its index to every element of a sequence. // enumerate([6,4,7,6]) == [(0, 6), (1, 4), (2, 7), (3, 6)] template auto enumerate(const Container& xs) { return zip(all_idxs(xs), xs); } // API search type: inner_product_with : (((a, a) -> b), ((b, b) -> b), b, [a], [a]) -> b // fwd bind count: 4 // Calculate the inner product of two sequences using custom operations. // inner_product_with((+), (*), [1, 2, 3], [4, 5, 6]) == [32] template < typename ContainerIn1, typename ContainerIn2, typename OP1, typename OP2, typename Acc, typename X = typename ContainerIn1::value_type, typename Y = typename ContainerIn2::value_type, typename OP2Out = internal::invoke_result_t> auto inner_product_with(OP1 op1, OP2 op2, const Acc& value, const ContainerIn1& xs, const ContainerIn2& ys) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); assert(size_of_cont(xs) == size_of_cont(ys)); return std::inner_product( std::begin(xs), std::end(xs), std::begin(ys), value, op1, op2); } // API search type: inner_product : (a, [a], [a]) -> a // fwd bind count: 2 // Calculate the inner product of two sequences. // inner_product([1, 2, 3], [4, 5, 6]) == [32] template Z inner_product(const Z& value, const ContainerIn1& xs, const ContainerIn2& ys) { assert(size_of_cont(xs) == size_of_cont(ys)); return std::inner_product( std::begin(xs), std::end(xs), std::begin(ys), value); } // API search type: first_mismatch_idx_by : (((a, b) -> Bool), [a], [b]) -> Maybe Int // fwd bind count: 2 // Find the first index at which the two sequences differ // using a binary predicate. // first_mismatch_idx_by((==), [1, 2, 3], [1, 4, 3]) == Just 1 // first_mismatch_idx_by((==), [1, 2, 3], [1, 4]) == Just 1 // first_mismatch_idx_by((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch_idx_by((==), [], [1, 2]) == Nothing template maybe first_mismatch_idx_by(BinaryPredicate p, const ContainerIn1& xs, const ContainerIn2& ys) { auto itXs = std::begin(xs); auto itYs = std::begin(ys); std::size_t minSize = std::min(size_of_cont(xs), size_of_cont(ys)); for (std::size_t i = 0; i < minSize; ++i) { if (!internal::invoke(p, *itXs, *itYs)) { return just(i); } ++itXs; ++itYs; } return nothing(); } // API search type: first_mismatch_by : (((a, b) -> Bool), [a], [b]) -> Maybe (a, b) // fwd bind count: 2 // Find the first pair of elements differing in the two sequences // using a binary predicate. // first_mismatch_by((==), [1, 2, 3], [1, 4, 3]) == Just (2, 4) // first_mismatch_by((==), [1, 2, 3], [1, 4]) == Just (2, 4) // first_mismatch_by((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch_by((==), [], [1, 2]) == Nothing template > maybe first_mismatch_by(BinaryPredicate p, const ContainerIn1& xs, const ContainerIn2& ys) { const auto maybe_idx = first_mismatch_idx_by(p, xs, ys); if (is_nothing(maybe_idx)) { return nothing(); } else { const auto idx = maybe_idx.unsafe_get_just(); return just(std::make_pair( elem_at_idx(idx, xs), elem_at_idx(idx, ys))); } } // API search type: first_mismatch_idx_on : ((a -> b), [a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of elements differing in the two sequences // using a transformer. // first_mismatch_idx_on((mod 2), [1, 2, 3], [3, 5, 3]) == 1 // first_mismatch_idx_on((mod 2), [1, 2, 3], [1, 5]) == 1 // first_mismatch_idx_on((mod 2), [1, 2, 3], [1, 6]) == Nothing // first_mismatch_idx_on((mod 2), [], [1, 2]) == Nothing template > maybe first_mismatch_idx_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_idx_by(is_equal_by(f), xs, ys); } // API search type: first_mismatch_on : ((a -> b), [a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of elements differing in the two sequences // using a transformer. // first_mismatch_on((mod 2), [1, 2, 3], [3, 5, 3]) == Just (2, 5) // first_mismatch_on((mod 2), [1, 2, 3], [1, 5]) == Just (2, 5) // first_mismatch_on((mod 2), [1, 2, 3], [1, 6]) == Nothing // first_mismatch_on((mod 2), [], [1, 2]) == Nothing template > maybe first_mismatch_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_by(is_equal_by(f), xs, ys); } // API search type: first_mismatch_idx : ([a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of elements differing in the two sequences. // first_mismatch_idx((==), [1, 2, 3], [1, 4, 3]) == 1 // first_mismatch_idx((==), [1, 2, 3], [1, 4]) == 1 // first_mismatch_idx((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch_idx((==), [], [1, 2]) == Nothing template maybe first_mismatch_idx( const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_idx_by(std::equal_to(), xs, ys); } // API search type: first_mismatch : ([a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of elements differing in the two sequences // first_mismatch((==), [1, 2, 3], [1, 4, 3]) == Just (2, 4) // first_mismatch((==), [1, 2, 3], [1, 4]) == Just (2, 4) // first_mismatch((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch((==), [], [1, 2]) == Nothing template > maybe first_mismatch(const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_by(std::equal_to(), xs, ys); } // API search type: first_match_idx_by : (((a, b) -> Bool), [a], [b]) -> Maybe Int // fwd bind count: 2 // Find the first index at which the two sequences equal // using a binary predicate. // first_match_idx_by((==), [1, 2, 3], [3, 2, 3]) == Just 1 // first_match_idx_by((==), [], [1, 2]) == Nothing template maybe first_match_idx_by(F f, const ContainerIn1& xs, const ContainerIn2& ys) { auto itXs = std::begin(xs); auto itYs = std::begin(ys); std::size_t minSize = std::min(size_of_cont(xs), size_of_cont(ys)); for (std::size_t i = 0; i < minSize; ++i) { if (internal::invoke(f, *itXs, *itYs)) { return just(i); } ++itXs; ++itYs; } return nothing(); } // API search type: first_match_by : (((a, b) -> Bool), [a], [b]) -> Maybe (a, b) // fwd bind count: 2 // Find the first pair of equal elements in the two sequences // using a binary predicate. // first_match_by((==), [1, 2, 3], [3, 2, 3]) == Just (2, 2) // first_match_by((==), [], [1, 2]) == Nothing template > maybe first_match_by(F f, const ContainerIn1& xs, const ContainerIn2& ys) { const auto maybe_idx = first_match_idx_by(f, xs, ys); if (is_nothing(maybe_idx)) { return nothing(); } else { const auto idx = maybe_idx.unsafe_get_just(); return just(std::make_pair( elem_at_idx(idx, xs), elem_at_idx(idx, ys))); } } // API search type: first_match_idx_on : ((a -> b), [a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of equal elements in the two sequences // using a transformer. // first_match_idx_on((mod 2), [1, 2, 3], [2, 4, 3]) == 1 // first_match_idx_on((mod 2), [], [1, 2]) == Nothing template maybe first_match_idx_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_idx_by(is_equal_by(f), xs, ys); } // API search type: first_match_on : ((a -> b), [a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of equal elements in the two sequences // using a transformer. // first_match_on((mod 2), [1, 2, 3], [2, 4, 3]) == Just (2, 4) // first_match_on((mod 2), [], [1, 2]) == Nothing template > maybe first_match_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_by(is_equal_by(f), xs, ys); } // API search type: first_match_idx : ([a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of equal elements in the two sequences. // first_match_idx((==), [1, 2, 3], [5, 2, 3]) == 1 // first_match_idx((==), [], [1, 2]) == Nothing template maybe first_match_idx( const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_idx_by(std::equal_to(), xs, ys); } // API search type: first_match : ([a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of equal elements in the two sequences. // first_match((==), [1, 2, 3], [5, 2, 3]) == Just (2, 2) // first_match((==), [], [1, 2]) == Nothing template > maybe first_match(const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_by(std::equal_to(), xs, ys); } } // namespace fplus libfplus-0.2.13/include/fplus/queue.hpp000066400000000000000000000040301376322245400200330ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include namespace fplus { // A thread-safe queue. template class queue { public: queue() : queue_(), mutex_(), cond_() {} fplus::maybe pop() { std::unique_lock lock(mutex_); if (queue_.empty()) { return {}; } auto item = queue_.front(); queue_.pop_front(); return item; } void push(const T& item) { { std::unique_lock lock(mutex_); queue_.push_back(item); } cond_.notify_one(); } std::vector pop_all() { std::unique_lock mlock(mutex_); const auto result = fplus::convert_container>(queue_); queue_.clear(); return result; } std::vector wait_and_pop_all() { std::unique_lock mlock(mutex_); cond_.wait(mlock, [&]() -> bool { return !queue_.empty(); }); const auto result = fplus::convert_container>(queue_); queue_.clear(); return result; } std::vector wait_for_and_pop_all(std::int64_t max_wait_time_us) { std::unique_lock mlock(mutex_); const auto t = std::chrono::microseconds{ max_wait_time_us }; cond_.wait_for(mlock, t, [&]() -> bool { return !queue_.empty(); }); const auto result = fplus::convert_container>(queue_); queue_.clear(); return result; } private: std::deque queue_; std::mutex mutex_; std::condition_variable cond_; }; } // namespace fplus libfplus-0.2.13/include/fplus/raii.hpp000066400000000000000000000017151376322245400176420ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include namespace fplus { // A generic RAII class. // It is recommended to use make_raii for constructing an instance. template class raii { public: raii(INIT init, QUIT quit) : quit_(quit) { init(); } ~raii() { quit_(); } raii(const raii&) = delete; raii(raii&&) = default; raii& operator=(const raii&) = delete; raii& operator=(raii&&) = default; private: QUIT quit_; }; template shared_ref> make_raii(INIT init, QUIT quit) { return make_shared_ref>(init, quit); } } // namespace fplus libfplus-0.2.13/include/fplus/read.hpp000066400000000000000000000123131376322245400176250ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include namespace fplus { namespace internal { template struct helper_read_value_struct {}; template <> struct helper_read_value_struct { static void read(const std::string& str, int& result, std::size_t& num_chars_used) { result = std::stoi(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, long& result, std::size_t& num_chars_used) { result = std::stol(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, long long& result, std::size_t& num_chars_used) { result = std::stoll(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, unsigned int& result, std::size_t& num_chars_used) { unsigned long result_u_l = std::stoul(str, &num_chars_used); result = static_cast(result_u_l); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, unsigned long& result, std::size_t& num_chars_used) { result = std::stoul(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, unsigned long long& result, std::size_t& num_chars_used) { result = std::stoull(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, float& result, std::size_t& num_chars_used) { result = std::stof(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, double& result, std::size_t& num_chars_used) { result = std::stod(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, long double& result, std::size_t& num_chars_used) { result = std::stold(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, std::string& result, std::size_t& num_chars_used) { num_chars_used = str.size(); result = str; } }; } // API search type: read_value_result : String -> Result a // Try to deserialize a value. template result read_value_result(const std::string& str) { try { T result; std::size_t num_chars_used = 0; internal::helper_read_value_struct::read(str, result, num_chars_used); if (num_chars_used != str.size()) { return error(std::string("String not fully parsable.")); } return ok(result); } catch(const std::invalid_argument& e) { return error(e.what()); } catch(const std::out_of_range& e) { return error(e.what()); } } // API search type: read_value : String -> Maybe a // Try to deserialize/parse a value, e.g.: // String to Int // String to Float // String to Double // read_value("42") == 42 // etc. template maybe read_value(const std::string& str) { return to_maybe(read_value_result(str)); } // API search type: read_value_with_default : (a, String) -> a // fwd bind count: 1 // Try to deserialize a value, return given default on failure, e.g.: // String to Int // String to Float // String to Double // read_value_with_default(3, "42") == 42 // read_value_with_default(3, "") == 3 // read_value_with_default(3, "foo") == 3 // etc. template T read_value_with_default(const T& def, const std::string& str) { return just_with_default(def, to_maybe(read_value_result(str))); } // API search type: read_value_unsafe : String -> a // Try to deserialize a value, crash on failure, e.g.: // String to Int // String to Float // String to Double // read_value_unsafe("42") == 42 // read_value_unsafe("") == crash // read_value_unsafe("foo") == crash // See read_value and read_value_with_default for safe versions. // etc. template T read_value_unsafe(const std::string& str) { return unsafe_get_just(to_maybe(read_value_result(str))); } } // namespace fplus libfplus-0.2.13/include/fplus/replace.hpp000066400000000000000000000074561376322245400203410ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include namespace fplus { namespace internal { template Container replace_if(internal::reuse_container_t, UnaryPredicate p, const T& dest, Container&& xs) { std::replace_if(std::begin(xs), std::end(xs), p, dest); return std::forward(xs); } template Container replace_if(internal::create_new_container_t, UnaryPredicate p, const T& dest, const Container& xs) { Container ys = xs; return replace_if(internal::reuse_container_t(), p, dest, std::move(ys)); } } // namespace internal // API search type: replace_if : ((a -> Bool), a, [a]) -> [a] // fwd bind count: 2 // Replace every element fulfilling a predicate with a specific value. // replace_if(is_even, 0, [1, 3, 4, 6, 7]) == [1, 3, 0, 0, 7] template > ContainerOut replace_if(UnaryPredicate p, const typename ContainerOut::value_type& dest, Container&& xs) { return internal::replace_if(internal::can_reuse_v{}, p, dest, std::forward(xs)); } namespace internal { template Container replace_elem_at_idx(internal::reuse_container_t, std::size_t idx, const T& dest, Container&& xs) { assert(idx < xs.size()); auto it = std::begin(xs); advance_iterator(it, idx); *it = dest; return std::forward(xs); } template Container replace_elem_at_idx(internal::create_new_container_t, std::size_t idx, const T& dest, const Container& xs) { Container ys = xs; return replace_elem_at_idx(internal::reuse_container_t(), idx, dest, std::move(ys)); } } // namespace internal // API search type: replace_elem_at_idx : (Int, a, [a]) -> [a] // fwd bind count: 2 // Replace the element at a specific index. // replace_elem_at_idx(2, 0, [1, 3, 4, 4, 7]) == [1, 3, 0, 4, 7] template , typename T = typename ContainerOut::value_type> ContainerOut replace_elem_at_idx(std::size_t idx, const T& dest, Container&& xs) { return internal::replace_elem_at_idx(internal::can_reuse_v{}, idx, dest, std::forward(xs)); } // API search type: replace_elems : (a, a, [a]) -> [a] // fwd bind count: 2 // Replace all elements matching source with dest. // replace_elems(4, 0, [1, 3, 4, 4, 7]) == [1, 3, 0, 0, 7] template , typename T = typename ContainerOut::value_type> ContainerOut replace_elems(const T& source, const T& dest, Container&& xs) { return replace_if(bind_1st_of_2(is_equal, source), dest, xs); } // API search type: replace_tokens : ([a], [a], [a]) -> [a] // fwd bind count: 2 // Replace all segments matching source with dest. // replace_tokens("haha", "hihi", "oh, hahaha!") == "oh, hihiha!" // replace_tokens("haha", "o", "oh, hahaha!") == "oh, oha!" template Container replace_tokens (const Container& source, const Container& dest, const Container& xs) { auto splitted = split_by_token(source, true, xs); return join(dest, splitted); } } // namespace fplus libfplus-0.2.13/include/fplus/result.hpp000066400000000000000000000270401376322245400202330ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include namespace fplus { template class result; template result ok(const Ok& val); template result error(const Error& error); // Can hold a value of type Ok or an error of type Error. template class result { public: bool is_ok() const { return static_cast(ptr_ok_); } bool is_error() const { return static_cast(ptr_error_); } const Ok& unsafe_get_ok() const { check_either_or_invariant(); assert(is_ok()); return *ptr_ok_; } const Error& unsafe_get_error() const { check_either_or_invariant(); assert(is_error()); return *ptr_error_; } typedef Ok ok_t; typedef Error error_t; result(const result& other) : ptr_ok_(other.is_ok() ? ptr_ok(new Ok(other.unsafe_get_ok())) : ptr_ok()), ptr_error_(other.is_error() ? ptr_error(new Error(other.unsafe_get_error())) : ptr_error()) { check_either_or_invariant(); } result& operator = (const result& other) { ptr_ok_ = other.is_ok() ? ptr_ok(new Ok(other.unsafe_get_ok())) : ptr_ok(); ptr_error_ = other.is_error() ? ptr_error(new Error(other.unsafe_get_error())) : ptr_error(); return *this; } private: void check_either_or_invariant() const { assert(is_ok() != is_error()); } result() : ptr_ok_(ptr_ok()), ptr_error_(ptr_error()) {} typedef std::unique_ptr ptr_ok; typedef std::unique_ptr ptr_error; friend result ok(const Ok& ok); friend result error(const Error& error); ptr_ok ptr_ok_; ptr_error ptr_error_; }; // API search type: is_ok : Result a b -> Bool // fwd bind count: 0 // Is not error? template bool is_ok(const result& result) { return result.is_ok(); } // API search type: is_error : Result a b -> Bool // fwd bind count: 0 // Is not OK? template bool is_error(const result& result) { return !is_ok(result); } // API search type: unsafe_get_ok : Result a b -> a // fwd bind count: 0 // Crashes if result is error! template Ok unsafe_get_ok(const result& result) { return result.unsafe_get_ok(); } // API search type: unsafe_get_error : Result a b -> b // fwd bind count: 0 // Crashes if result is ok! template Error unsafe_get_error(const result& result) { return result.unsafe_get_error(); } // API search type: ok_with_default : (a, Result a b) -> a // fwd bind count: 1 // Get the value from a result or the default in case it is error. template Ok ok_with_default(const Ok& defaultValue, const result& result) { if (is_ok(result)) return unsafe_get_ok(result); return defaultValue; } // API search type: ok : a -> Result a b // fwd bind count: 0 // Wrap a value in a result as a Ok. template result ok(const Ok& val) { result x; x.ptr_ok_.reset(new Ok(val)); return x; } // API search type: error : b -> Result a b // fwd bind count: 0 // Construct an error of a certain result type. template result error(const Error& error) { result x; x.ptr_error_.reset(new Error(error)); return x; } // API search type: to_maybe : Result a b -> Maybe a // fwd bind count: 0 // Convert ok to just, error to nothing. template maybe to_maybe(const result& result) { if (is_ok(result)) return just(unsafe_get_ok(result)); else return nothing(); } // API search type: from_maybe : (b, Maybe a) -> Result a b // fwd bind count: 1 // Convert just to ok, nothing to error. template result from_maybe(const Error& err, const maybe& maybe) { if (is_just(maybe)) return ok(unsafe_get_just(maybe)); else return error(err); } // API search type: throw_on_error : (e, Result a b) -> a // fwd bind count: 1 // Throws the given exception in case of error. // Return ok value if ok. template Ok throw_on_error(const E& e, const result& result) { if (is_error(result)) throw e; return unsafe_get_ok(result); } // API search type: throw_type_on_error : Result a b -> a // Throws the given exception type constructed with error value if error. // Return ok value if ok. template Ok throw_type_on_error(const result& result) { if (is_error(result)) throw E(unsafe_get_error(result)); return unsafe_get_ok(result); } // True if ok values are the same or if errors are the same. template bool operator == (const result& x, const result& y) { if (is_ok(x) && is_ok(y)) return unsafe_get_ok(x) == unsafe_get_ok(y); if (is_error(x) && is_error(y)) return unsafe_get_error(x) == unsafe_get_error(y); return false; } // False if ok values are the same or if both errors are the same. template bool operator != (const result& x, const result& y) { return !(x == y); } // API search type: lift_result : ((a -> b), Result a c) -> Result b c // fwd bind count: 1 // Lifts a function into the result functor. // A function that for example was able to convert and int into a string, // now can convert a result into a result. // An error stays the same error, regardless of the conversion. template auto lift_result(F f, const result& r) { internal::trigger_static_asserts(); using B = std::decay_t>; if (is_ok(r)) return ok(internal::invoke(f, unsafe_get_ok(r))); return error(unsafe_get_error(r)); } // API search type: lift_result_both : ((a -> c), (b -> d), Result a b) -> Result c d // fwd bind count: 2 // Lifts two functions into the result functor. template auto lift_result_both(F f, G g, const result& r) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); using C = std::decay_t>; using D = std::decay_t>; if (is_ok(r)) return ok(internal::invoke(f, unsafe_get_ok(r))); return error(internal::invoke(g, unsafe_get_error(r))); } // API search type: unify_result : ((a -> c), (b -> c), Result a b) -> c // fwd bind count: 2 // Extracts the value (Ok or Error) from a Result // as defined by the two given functions. template auto unify_result(F f, G g, const result& r) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); static_assert(std::is_same, internal::invoke_result_t>::value, "Both functions must return the same type."); if (is_ok(r)) return internal::invoke(f, unsafe_get_ok(r)); return internal::invoke(g, unsafe_get_error(r)); } // API search type: join_result : Result (Result a b) b -> Result a b // Flattens a nested result. // join_result(Ok Ok x) == Ok x // join_result(Ok Error e) == Error e // join_result(Error e) == Error e template result join_result(const result, Error>& r) { if (is_ok(r)) return unsafe_get_ok(r); else return error(r.unsafe_get_error()); } // API search type: and_then_result : ((a -> Result c b), (Result a b)) -> Result c b // fwd bind count: 1 // Monadic bind. // Returns the error if the result is an error. // Otherwise return the result of applying // the function to the ok value of the result. template auto and_then_result(F f, const result& r) { internal::trigger_static_asserts(); using FOut = std::decay_t>; static_assert(std::is_same::value, "Error type must stay the same."); if (is_ok(r)) return internal::invoke(f, unsafe_get_ok(r)); else return error(r.unsafe_get_error()); } // API search type: compose_result : ((a -> Result b c), (b -> Result d c)) -> (a -> Result d c) // Left-to-right Kleisli composition of monads. // It is possible to compose a variadic number of callables. // The first callable can take a variadic number of parameters. template auto compose_result(Callables&&... callables) { auto bind_result = [](auto f, auto g) { return [f = std::move(f), g = std::move(g)](auto&&... args) { internal::trigger_static_asserts(); #if defined(_MSC_VER) && _MSC_VER >= 1920 // in VS2019, compilation with /permissive- breaks with 'using' syntax below struct FOut : std::decay_t< internal::invoke_result_t> {}; #else using FOut = std::decay_t< internal::invoke_result_t>; #endif internal::trigger_static_asserts(); #if defined(_MSC_VER) && _MSC_VER >= 1920 // in VS2019, compilation with /permissive- breaks with 'using' syntax below struct GOut : std::decay_t< internal::invoke_result_t> {}; #else using GOut = std::decay_t< internal::invoke_result_t>; #endif static_assert(std::is_same::value, "Error type must stay the same."); auto resultB = internal::invoke(f, std::forward(args)...); if (is_ok(resultB)) return internal::invoke(g, unsafe_get_ok(resultB)); return error( unsafe_get_error(resultB)); }; }; return internal::compose_binary_lift(bind_result, std::forward(callables)...); } } // namespace fplus libfplus-0.2.13/include/fplus/search.hpp000066400000000000000000000176201376322245400201650ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include namespace fplus { // API search type: find_first_by : ((a -> Bool), [a]) -> Maybe a // fwd bind count: 1 // Returns the first element fulfilling the predicate. // find_first_by(is_even, [1, 3, 4, 6, 9]) == Just(4) // find_first_by(is_even, [1, 3, 5, 7, 9]) == Nothing template maybe find_first_by(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto it = std::find_if(std::begin(xs), std::end(xs), pred); if (it == std::end(xs)) return nothing(); return just(*it); } // API search type: find_last_by : ((a -> Bool), [a]) -> Maybe a // fwd bind count: 1 // Returns the last element fulfilling the predicate. // find_last_by(is_even, [1, 3, 4, 6, 9]) == Just(6) // find_last_by(is_even, [1, 3, 5, 7, 9]) == Nothing template maybe find_last_by(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); return find_first_by(pred, reverse(xs)); } // API search type: find_first_idx_by : ((a -> Bool), [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the first element fulfilling the predicate. // find_first_idx_by(is_even, [1, 3, 4, 6, 9]) == Just(2) // find_first_idx_by(is_even, [1, 3, 5, 7, 9]) == Nothing template maybe find_first_idx_by (UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto it = std::find_if(std::begin(xs), std::end(xs), pred); if (it == std::end(xs)) return nothing(); return static_cast(std::distance(std::begin(xs), it)); } // API search type: find_last_idx_by : ((a -> Bool), [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the last element fulfilling the predicate. // find_last_idx_by(is_even, [1, 3, 4, 6, 9]) == Just(3) // find_last_idx_by(is_even, [1, 3, 5, 7, 9]) == Nothing template maybe find_last_idx_by (UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto calcRevIdx = [&](std::size_t idx) { return size_of_cont(xs) - (idx + 1); }; return lift_maybe(calcRevIdx, find_first_idx_by(pred, reverse(xs))); } // API search type: find_first_idx : (a, [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the first element equal to x. // find_first_idx(4, [1, 3, 4, 4, 9]) == Just(2) // find_first_idx(4, [1, 3, 5, 7, 9]) == Nothing template maybe find_first_idx (const typename Container::value_type& x, const Container& xs) { return find_first_idx_by(is_equal_to(x), xs); } // API search type: find_last_idx : (a, [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the last element equal to x. // find_last_idx(4, [1, 3, 4, 4, 9]) == Just(3) // find_last_idx(4, [1, 3, 5, 7, 9]) == Nothing template maybe find_last_idx (const typename Container::value_type& x, const Container& xs) { return find_last_idx_by(is_equal_to(x), xs); } // API search type: find_all_idxs_by : ((a -> Bool), [a]) -> [Int] // fwd bind count: 1 // Returns the indices off all elements fulfilling the predicate. // find_all_idxs_by(is_even, [1, 3, 4, 6, 9]) == [2, 3] template , typename UnaryPredicate, typename Container> ContainerOut find_all_idxs_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); std::size_t idx = 0; ContainerOut result; auto itOut = internal::get_back_inserter(result); for (const auto& x : xs) { if (internal::invoke(p, x)) *itOut = idx; ++idx; } return result; } // API search type: find_all_idxs_of : (a, [a]) -> [Int] // fwd bind count: 1 // Returns the indices off all elements equal to x. // find_all_idxs_of(4, [1, 3, 4, 4, 9]) == [2, 3] template , typename Container, typename T = typename Container::value_type> ContainerOut find_all_idxs_of (const T& x, const Container& xs) { return find_all_idxs_by(is_equal_to(x), xs); } // API search type: find_all_instances_of_token : ([a], [a]) -> [Int] // fwd bind count: 1 // Returns the starting indices of all segments matching token. // find_all_instances_of_token("haha", "oh, hahaha!") == [4, 6] template , typename Container> ContainerOut find_all_instances_of_token(const Container& token, const Container& xs) { if (size_of_cont(token) > size_of_cont(xs)) return ContainerOut(); auto itInBegin = std::begin(xs); auto itInEnd = itInBegin; internal::advance_iterator(itInEnd, size_of_cont(token)); std::size_t idx = 0; ContainerOut result; auto outIt = internal::get_back_inserter(result); std::size_t last_possible_idx = size_of_cont(xs) - size_of_cont(token); auto check_and_push = [&]() { if (std::equal(itInBegin, itInEnd, std::begin(token))) { *outIt = idx; } }; while (idx != last_possible_idx) { check_and_push(); ++itInBegin; ++itInEnd; ++idx; } check_and_push(); return result; } // API search type: find_all_instances_of_token_non_overlapping : ([a], [a]) -> [Int] // fwd bind count: 1 // Returns the starting indices // of all non-overlapping segments matching token. // find_all_instances_of_token_non_overlapping("haha", "oh, hahaha!") == [4] template , typename Container> ContainerOut find_all_instances_of_token_non_overlapping (const Container& token, const Container& xs) { auto overlapping_instances = find_all_instances_of_token( token, xs); ContainerOut result; auto outIt = internal::get_back_inserter(result); std::size_t token_size = size_of_cont(token); for (const auto idx : overlapping_instances) { if (result.empty() || result.back() + token_size <= idx) { *outIt = idx; } } return result; } // API search type: find_first_instance_of_token : ([a], [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the first segment matching token. // find_first_instance_of_token("haha", "oh, hahaha!") == just 4 template maybe find_first_instance_of_token (const Container& token, const Container& xs) { if (size_of_cont(token) > size_of_cont(xs)) return nothing(); auto itInBegin = std::begin(xs); auto itInEnd = itInBegin; internal::advance_iterator(itInEnd, size_of_cont(token)); std::size_t idx = 0; std::size_t last_possible_idx = size_of_cont(xs) - size_of_cont(token); while (idx != last_possible_idx) { if (std::equal(itInBegin, itInEnd, std::begin(token))) { return just(idx); } ++itInBegin; ++itInEnd; ++idx; } if (std::equal(itInBegin, itInEnd, std::begin(token))) { return just(idx); } return nothing(); } } // namespace fplus libfplus-0.2.13/include/fplus/sets.hpp000066400000000000000000000160061376322245400176730ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include namespace fplus { // API search type: set_includes : (Set a, Set a) -> Bool // fwd bind count: 1 // Checks if every element of the second set is also present in the first set. // Also known as is_subset_of. template bool set_includes(const SetType& set1, const SetType& set2) { return std::includes(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2)); } // API search type: unordered_set_includes : (Unordered_Set a, Unordered_Set a) -> Bool // fwd bind count: 1 // Checks if every element of the second unordered_set // is also present in the first unordered_set. // Also known as is_subset_of. template bool unordered_set_includes(const UnorderSetType& set1, const UnorderSetType& set2) { auto first_not_included = std::find_if(set2.begin(), set2.end(), [&](const typename UnorderSetType::value_type& x) -> bool { return set1.find(x) == set1.end();}); return first_not_included == set2.end(); } // API search type: set_merge : (Set a, Set a) -> Set a // fwd bind count: 1 // Returns the union of two given sets. template SetType set_merge(const SetType& set1, const SetType& set2) { SetType result; auto itOut = internal::get_back_inserter(result); std::merge(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2), itOut); return result; } // API search type: unordered_set_merge : (Unordered_Set a, Unordered_Set a) -> Unordered_Set a // fwd bind count: 1 // Returns the union of two given sets. template UnorderSetType unordered_set_merge(const UnorderSetType& set1, const UnorderSetType& set2) { UnorderSetType result; auto itOut = internal::get_back_inserter(result); std::copy(std::begin(set1), std::end(set1), itOut); std::copy(std::begin(set2), std::end(set2), itOut); return result; } // API search type: set_intersection : (Set a, Set a) -> Set a // fwd bind count: 1 // Returns the intersection of both sets. template SetType set_intersection(const SetType& set1, const SetType& set2) { SetType result; auto itOut = internal::get_back_inserter(result); std::set_intersection(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2), itOut); return result; } // API search type: unordered_set_intersection : (Unordered_Set a, Unordered_Set a) -> Unordered_Set a // fwd bind count: 1 // Returns the intersection of both unordered_sets. template UnorderSetType unordered_set_intersection( const UnorderSetType& set1, const UnorderSetType& set2) { UnorderSetType result; auto itOut = internal::get_back_inserter(result); std::copy_if(std::begin(set2), std::end(set2), itOut, [&](const typename UnorderSetType::value_type &x) -> bool {return set1.find(x) != set1.end();}); return result; } // API search type: set_is_disjoint : (Set a, Set a) -> Bool // fwd bind count: 1 // Checks if two sets are disjoint. template bool set_is_disjoint(const SetType& set1, const SetType& set2) { return set_intersection(set1, set2).empty(); } // API search type: unordered_set_is_disjoint : (Unordered_Set a, Unordered_Set a) -> Bool // fwd bind count: 1 // Checks if two unordered_sets are disjoint. template bool unordered_set_is_disjoint( const UnorderSetType& set1, const UnorderSetType& set2) { return unordered_set_intersection(set1, set2).empty(); } // API search type: set_difference : (Set a, Set a) -> Set a // fwd bind count: 1 // Returns the elements in set1 that are not present in set2. template SetType set_difference(const SetType& set1, const SetType& set2) { SetType result; auto itOut = internal::get_back_inserter(result); std::set_difference(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2), itOut); return result; } // API search type: unordered_set_difference : (Unordered_Set a, Unordered_Set a) -> Unordered_Set a // fwd bind count: 1 // Returns the elements in unordered_set1 // that are not present in unordered_set2. template UnorderSetType unordered_set_difference(const UnorderSetType& set1, const UnorderSetType& set2) { UnorderSetType result; auto itOut = internal::get_back_inserter(result); std::copy_if(std::begin(set1), std::end(set1), itOut, [&](const typename UnorderSetType::value_type &x) -> bool {return set2.find(x) == set2.end();}); return result; } // API search type: set_symmetric_difference : (Set a, Set a) -> Set a // fwd bind count: 1 // Returns the symmetric difference of both sets. template SetType set_symmetric_difference(const SetType& set1, const SetType& set2) { SetType result; auto itOut = internal::get_back_inserter(result); std::set_symmetric_difference(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2), itOut); return result; } // API search type: unordered_set_symmetric_difference : (Unordered_Set a, Unordered_Set a) -> Unordered_Set a // fwd bind count: 1 // Returns the symmetric difference of both unordered_sets. template UnorderSetType unordered_set_symmetric_difference( const UnorderSetType& set1, const UnorderSetType& set2) { UnorderSetType result; auto itOut = internal::get_back_inserter(result); std::copy_if(std::begin(set1), std::end(set1), itOut, [&](const typename UnorderSetType::value_type &x) -> bool {return set2.find(x) == set2.end();}); std::copy_if(std::begin(set2), std::end(set2), itOut, [&](const typename UnorderSetType::value_type &x) -> bool {return set1.find(x) == set1.end();}); return result; } // API search type: sets_intersection : [Set a] -> Set a // fwd bind count: 0 // Returns the intersection of the given sets. // Also known as intersect_many. // For performance try to sort the inputs sets prior, ascendending by size. template SetType sets_intersection(const ContainerIn& sets) { return fold_left_1(set_intersection, sets); } // API search type: unordered_sets_intersection : [Unordered_Set a] -> Unordered_Set a // fwd bind count: 0 // Returns the intersection of the given unordered_sets. // Also known as intersect_many. template UnordSetType unordered_sets_intersection(const ContainerIn& sets) { return fold_left_1(unordered_set_intersection, sets); } } // namespace fplus libfplus-0.2.13/include/fplus/shared_ref.hpp000066400000000000000000000031311376322245400210120ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include namespace fplus { // A std::shared_ptr expresses // optionality of the contained value (can be nullptr) // and shared ownership that can be transferred. // A std::optional expresses optionality only. // The standard does not provide a class to // express only shared ownership without optionality. // shared_ref fills this gap. // It is recommended to use make_shared_ref for constructing an instance. template class shared_ref { public: shared_ref(const shared_ref&) = default; shared_ref(shared_ref&&) = default; shared_ref& operator=(const shared_ref&) = default; shared_ref& operator=(shared_ref&&) = default; ~shared_ref() = default; T* operator->() { return m_ptr.get(); } const T* operator->() const { return m_ptr.get(); } T& operator*() { return *m_ptr.get(); } const T& operator*() const { return *m_ptr.get(); } template friend shared_ref make_shared_ref(XTypes&&...args); private: std::shared_ptr m_ptr; shared_ref(T* value) :m_ptr(value) { assert(value != nullptr); } }; // http://stackoverflow.com/a/41976419/1866775 template shared_ref make_shared_ref(Types&&...args) { return shared_ref(new T(std::forward(args)...)); } } // namespace fplus libfplus-0.2.13/include/fplus/show.hpp000066400000000000000000000200261376322245400176720ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include namespace fplus { // API search type: show : a -> String // fwd bind count: 0 // 42 -> "42" // Useful to simply show values, e.g.: // Int to String // Float to String // Double to String // Pair to String // std::vector> to String // std::vector to String template std::string show(const T& x) { std::ostringstream ss; ss << x; return ss.str(); } // string identity // "foo" -> "foo" inline std::string show(const std::string& str) { return str; } template std::string show(const std::vector& xs); template std::string show(const std::list& xs); // {1, "one"} -> "(1, one)" template std::string show(const std::pair& p) { return std::string("(") + show(p.first) + ", " + show(p.second) + ")"; } template std::string show_cont(const Container& xs); template std::string show(const std::vector& xs) { return show_cont(xs); } template std::string show(const std::list& xs) { return show_cont(xs); } template std::string show(const std::set& xs) { return show_cont(xs); } template std::string show(const std::deque& xs) { return show_cont(xs); } // API search type: show_cont_with_frame_and_newlines : (String, String, String, [a], Int) -> String // fwd bind count: 3 // show_cont_with_frame_and_newlines (",", "(", ")", [1, 2, 3, 4, 5], 2) // == "(1,2) // 3,4) // 5)" template std::string show_cont_with_frame_and_newlines( const std::string& separator, const std::string& prefix, const std::string& suffix, const Container& xs, std::size_t new_line_every_nth_elem ) { std::vector elemStrs; elemStrs.reserve(xs.size()); if (new_line_every_nth_elem == 0) { for (const auto& x : xs) { elemStrs.push_back(show(x)); } } else { std::size_t i = 0; std::string newline = std::string("\n") + std::string(prefix.size(), ' '); for (const auto& x : xs) { if ( i && i % new_line_every_nth_elem == 0) elemStrs.push_back(newline + show(x)); else elemStrs.push_back(show(x)); ++i; } } return prefix + join(separator, elemStrs) + suffix; } // API search type: show_cont_with_frame : (String, String, String, [a]) -> String // fwd bind count: 3 // show_cont_with_frame (" => ", "{", "}", [1, 2, 3]) == "{1 => 2 => 3}" template std::string show_cont_with_frame( const std::string& separator, const std::string& prefix, const std::string& suffix, const Container& xs) { return show_cont_with_frame_and_newlines( separator, prefix, suffix, xs, 0); } // API search type: show_cont_with : (String, [a]) -> String // fwd bind count: 1 // show_cont_with( " - ", [1, 2, 3]) == "[1 - 2 - 3]" template std::string show_cont_with(const std::string& separator, const Container& xs) { return show_cont_with_frame(separator, "[", "]", xs); } // API search type: show_cont : [a] -> String // fwd bind count: 0 // show_cont [1, 2, 3] -> "[1, 2, 3]" // Can i.a show std::vector and std::map. template std::string show_cont(const Container& xs) { return show_cont_with(", ", xs); } // API search type: show_maybe : Maybe a -> String // fwd bind count: 0 // show_maybe(Just 42) -> "Just 42" template std::string show_maybe(const maybe& maybe) { if (is_nothing(maybe)) return "Nothing"; else return std::string("Just " + show(unsafe_get_just(maybe))); } // API search type: show_result : Result a b -> String // fwd bind count: 0 // show_result(Ok 42) -> "Ok 42" // show_result(Error "fail") -> "Error fail" template std::string show_result(const result& result) { if (is_error(result)) return std::string("Error " + show(unsafe_get_error(result))); else return std::string("Ok " + show(unsafe_get_ok(result))); } // API search type: show_float : (Int, Int, Float) -> String // fwd bind count: 2 // Can be used to show floating point values in a specific format // (Float to String, Double to String etc.) // Examples: // const double pi = 3.14159 // show_float(0, 3, pi) == "3.142" // show_float(1, 3, pi) == "3.142" // show_float(2, 3, pi) == "03.142" // show_float(3, 3, pi) == "003.142" // show_float(1, 2, pi) == "3.14" // show_float(1, 4, pi) == "3.1416" // show_float(1, 7, pi) == "3.1415900" // show_float(0, 3, -pi) == "-3.142" // show_float(1, 3, -pi) == "-3.142" // show_float(2, 3, -pi) == "-3.142" // show_float(3, 3, -pi) == "-03.142" // show_float(4, 3, -pi) == "-003.142" // show_float(0, 3, 0.142) == "0.142"; // show_float(1, 3, 0.142) == "0.142"; // show_float(2, 3, 0.142) == "00.142"; // fill_left(8, ' ', show_float(0, 3, -pi)) == " -3.142" template std::string show_float( std::size_t min_left_chars, std::size_t right_char_count, const T& x) { bool is_negative = x < 0; std::size_t min_left_chars_final = is_negative && min_left_chars > 0 ? min_left_chars - 1 : min_left_chars; std::stringstream stream; stream << std::fixed << std::setprecision(static_cast(right_char_count)) << std::abs(x); std::string s = stream.str(); std::size_t min_dest_length = min_left_chars_final + 1 + right_char_count; std::string result = fill_left('0', min_dest_length, s); if (is_negative) { result = std::string("-") + result; } return result; } // API search type: show_float_fill_left : (Char, Int, Int, Float) -> String // fwd bind count: 3 // Can be used to show floating point values in a specific precision // left-padded with some character. // (Float to String, Double to String etc.) // Examples: // const double pi = 3.14159 // show_float_fill_left(' ', 8, 3, pi) == " 3.142" // show_float_fill_left(' ', 8, 6, pi) == "3.141590" // show_float_fill_left(' ', 8, 3, -pi) == " -3.142" // show_float_fill_left(' ', 2, 3, -pi) == "-3.142" template std::string show_float_fill_left(const std::string::value_type& filler, std::size_t min_size, std::size_t right_char_count, const T& x) { return fill_left(filler, min_size, show_float(0, right_char_count, x)); } // API search type: show_fill_left : (Char, Int, a) -> String // fwd bind count: 2 // Convert some value to a string with left-padded with some character. // (Int to String etc.) // Examples: // show_fill_left(' ', 4, 3) == " 3" // show_fill_left('0', 4, 3) == "0003" // show_fill_left(' ', 4, 12345) == "12345" template std::string show_fill_left( const std::string::value_type& filler, std::size_t min_size, const T& x) { return fill_left(filler, min_size, show(x)); } // API search type: show_fill_right : (Char, Int, a) -> String // fwd bind count: 2 // Convert some value to a string with left-padded with some character. // (Int to String etc.) // Examples: // show_fill_right(' ', 4, 3) == "3 " // show_fill_right(' ', 4, 12345) == "12345" template std::string show_fill_right(const std::string::value_type& filler, std::size_t min_size, const T& x) { return fill_right(filler, min_size, show(x)); } } // namespace fplus libfplus-0.2.13/include/fplus/side_effects.hpp000066400000000000000000000411161376322245400213400ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fplus { // Executes a function f in a fixed interval, // i.e. an average timespan between two consecutive calls of f, // given in microseconds. // f is a unary function, taking the time delta (in microseconds) // between the last and the current call as its argument. // In case of a delay outdated calls are be executed immediately. // So the average executation time of f should be way shorter // than the requested interval. // Call ticker::start() to run. // The ticker stops when ticker::stop() is called // or the instance runs out of scope. // // Example usage: // // void say_hi(std::int64_t) // { // std::cout << "hi " << std::endl; // } // int main() // { // ticker hi_ticker(say_hi, 2 * 1000 * 1000); // hi_ticker.start(); // std::this_thread::sleep_for(std::chrono::milliseconds(4500)); // } class ticker { public: typedef std::function function; ticker(const function& f, std::int64_t interval_us) : f_(f), interval_us_(interval_us), control_mutex_(), is_running_(false), thread_(), stop_mutex_() { } bool is_running() { std::lock_guard lock(control_mutex_); return is_running_; } bool start() { std::lock_guard lock(control_mutex_); if (is_running_) return false; stop_mutex_.lock(); thread_ = std::thread([this]() { thread_function(); }); is_running_ = true; return true; } bool stop() { std::lock_guard lock(control_mutex_); if (!is_running_) return false; stop_mutex_.unlock(); if (thread_.joinable()) { thread_.join(); thread_ = std::thread(); } is_running_ = false; return true; } ~ticker() { stop(); } private: void thread_function() { auto last_wake_up_time = std::chrono::steady_clock::now(); auto last_time = last_wake_up_time; bool quit = false; while (!quit) { const auto wake_up_time = last_wake_up_time + std::chrono::microseconds{ interval_us_ }; const auto sleep_time = wake_up_time - std::chrono::steady_clock::now(); if (stop_mutex_.try_lock_for(sleep_time)) { stop_mutex_.unlock(); quit = true; } const auto current_time = std::chrono::steady_clock::now(); const auto elapsed = current_time - last_time; last_wake_up_time = wake_up_time; last_time = current_time; const auto elapsed_us = std::chrono::duration_cast( elapsed).count(); try { f_(elapsed_us); } catch (...) { } } } const function f_; const std::int64_t interval_us_; std::mutex control_mutex_; bool is_running_; std::thread thread_; std::timed_mutex stop_mutex_; }; // API search type: sleep_for_n_seconds : Int -> Io () // Returns a function that suspends // the calling thread for n seconds when executed. inline std::function sleep_for_n_seconds(std::size_t seconds) { return [seconds]() { std::this_thread::sleep_for(std::chrono::seconds(seconds)); }; } // API search type: sleep_for_n_milliseconds : Int -> Io () // Returns a function that suspends // the calling thread for n milliseconds when executed. inline std::function sleep_for_n_milliseconds(std::size_t milliseconds) { return [milliseconds]() { std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); }; } // API search type: sleep_for_n_microseconds : Int -> Io () // Returns a function that suspends // the calling thread for n microseconds when executed. inline std::function sleep_for_n_microseconds(std::size_t microseconds) { return [microseconds]() { std::this_thread::sleep_for(std::chrono::microseconds(microseconds)); }; } // API search type: execute_serially : [Io ()] -> Io () // Returns a function that executes // the given side effects one after another when called. template auto execute_serially(const Container& effs) { using Effect = typename Container::value_type; using Result = internal::invoke_result_t; return [effs] { std::vector> results; for (const Effect& e : effs) { results.push_back(internal::invoke(e)); } return results; }; } // API search type: execute_serially_until_success : [Io Bool] -> Io Bool // Returns a function that (when called) executes // the given side effects one after another until one of it returns true. template auto execute_serially_until_success(const Container& effs) { using Effect = typename Container::value_type; using Result = internal::invoke_result_t; static_assert(std::is_convertible::value, "Effects must return a boolish type."); return [effs]() -> bool { for (const Effect& e : effs) { if (internal::invoke(e)) { return true; } } return false; }; } // API search type: execute_and_return_fixed_value : (a, [Io b]) -> Io a // Returns a function that executes the given side effect // and returns a fixed value when called. template std::function execute_and_return_fixed_value( Result result, Effect eff) { return [eff, result]() -> Result { eff(); return result; }; } // Converts an arbitrary callable effect to an std::function. template std::function ()> effect_to_std_function(Effect eff) { return [eff] { return internal::invoke(eff); }; } // API search type: execute_max_n_times_until_success : (Int, Io (), Int) -> Io Bool // Returns a function that (when called) executes a side effect // until it succeds once or the maximum number // of attempts with an optional pause in between. template auto execute_max_n_times_until_success(std::size_t n, const Effect& eff, std::size_t pause_in_milliseconds = 0) { if (pause_in_milliseconds > 0) { auto sleep_and_return_false = execute_and_return_fixed_value( false, sleep_for_n_milliseconds(pause_in_milliseconds)); return execute_serially_until_success( intersperse( sleep_and_return_false, replicate(n, effect_to_std_function(eff)))); } return execute_serially_until_success( replicate(n, effect_to_std_function(eff))); } // API search type: execute_n_times : (Int, Io a) -> Io () // Returns a function that (when called) executes n times // the provided side effect function. // The return values (if present) are dropped. template auto execute_n_times(std::size_t n, const Effect& eff) { for (auto _ : fplus::numbers(static_cast(0), n)) { (void) _; // suppress warning / unused variable eff(); } } // API search type: execute_serially_until_failure : [Io Bool] -> Io Bool // Returns a function that (when called) executes the given side effects // one after another until one of them returns false. template std::function execute_serially_until_failure(const Container& effs) { using Effect = typename Container::value_type; using Result = internal::invoke_result_t; static_assert(std::is_convertible::value, "Effects must return a boolish type."); return [effs]() -> bool { for (const Effect& e : effs) { if (!internal::invoke(e)) { return false; } } return true; }; } // API search type: execute_parallelly : [Io a] -> Io [a] // Returns a function that (when called) executes the given side effects // in parallel (one thread each) and returns the collected results. template auto execute_parallelly(const Container& effs) { return [effs] { // Bluntly re-using the transform implementation to execute side effects. return transform_parallelly([](const auto& eff) { return internal::invoke(eff); }, effs); }; } // API search type: execute_parallelly_n_threads : (Int, [Io a]) -> Io [a] // Returns a function that (when called) executes the given side effects // in parallel (one thread each) and returns the collected results. template auto execute_parallelly_n_threads(std::size_t n, const Container& effs) { return [n, effs] { // Bluntly re-using the transform implementation to execute side effects. return transform_parallelly_n_threads(n, [](const auto& eff) { return internal::invoke(eff); }, effs); }; } // API search type: execute_fire_and_forget : Io a -> Io a // Returns a function that (when called) executes the given side effect // in a new thread and returns immediately. template std::function execute_fire_and_forget(Effect eff) { return [eff]() { std::thread t(eff); t.detach(); }; } // API search type: read_text_file_maybe : String -> Io (Maybe String) // Returns a function that reads the content of a text file when called. inline std::function()> read_text_file_maybe( const std::string& filename) { return [filename]() -> maybe { std::ifstream input(filename); if (!input.good()) return {}; return just(std::string( std::istreambuf_iterator(input), std::istreambuf_iterator())); }; } // API search type: read_text_file : String -> Io String // Returns a function that reads the content of a text file when called. // This function then returns an empty string if the file could not be read. inline std::function read_text_file(const std::string& filename) { return [filename]() -> std::string { return just_with_default( std::string(), read_text_file_maybe(filename)()); }; } // API search type: read_binary_file_maybe : String -> Io (Maybe [Int]) // Returns a function that reads the content of a binary file when executed. inline std::function>()> read_binary_file_maybe( const std::string& filename) { return [filename]() -> maybe> { std::ifstream file(filename, std::ios::binary); if (!file.good()) return {}; file.unsetf(std::ios::skipws); std::streampos fileSize; file.seekg(0, std::ios::end); fileSize = file.tellg(); if (fileSize == static_cast(0)) return {}; file.seekg(0, std::ios::beg); std::vector vec(static_cast(fileSize), 0); file.read(reinterpret_cast(&vec[0]), fileSize); return vec; }; } // API search type: read_binary_file : String -> Io [Int] // Returns a function that reads the content of a binary file when executed. // This function then returns an empty vector if the file could not be read. inline std::function()> read_binary_file( const std::string& filename) { return [filename]() -> std::vector { return just_with_default( std::vector(), read_binary_file_maybe(filename)()); }; } // API search type: read_text_file_lines_maybe : (String, Bool) -> Io (Maybe [String]) // Returns a function that (when called) reads the content of a text file // and returns it line by line. inline std::function>()> read_text_file_lines_maybe( bool allow_empty, const std::string& filename) { return [filename, allow_empty]() -> maybe> { const auto maybe_content = read_text_file_maybe(filename)(); if (maybe_content.is_nothing()) return {}; else return split_lines(allow_empty, maybe_content.unsafe_get_just()); }; } // API search type: read_text_file_lines : (String, Bool) -> Io [String] // Returns a function that (when called) reads the content of a text file // and returns it line by line. // This function then returns an empty vector if the file could not be read. inline std::function()> read_text_file_lines( bool allow_empty, const std::string& filename) { return [filename, allow_empty]() -> std::vector { return just_with_default( std::vector(), read_text_file_lines_maybe(allow_empty, filename)()); }; } // API search type: write_text_file : (String, String) -> Io Bool // Returns a function that (when called) writes content into a text file, // replacing it if it already exists. inline std::function write_text_file(const std::string& filename, const std::string& content) { return [filename, content]() -> bool { std::ofstream output(filename); output << content; return output.good(); }; } // API search type: write_binary_file : (String, [Int]) -> Io Bool // Returns a function that (when called) writes content into a binary file, // replacing it if it already exists. inline std::function write_binary_file(const std::string& filename, const std::vector& content) { return [filename, content]() -> bool { std::ofstream file(filename, std::ios::binary); file.write(reinterpret_cast(&content[0]), static_cast(content.size())); return file.good(); }; } // API search type: write_text_file_lines : (String, [String], Bool) -> Io Bool // Returns a function that (when called) writes lines into a text file, // replacing it if it already exists. inline std::function write_text_file_lines(bool trailing_newline, const std::string& filename, const std::vector& lines) { std::string content = join(std::string("\n"), lines); if (trailing_newline) { content += "\n"; } return write_text_file(filename, content); } // API search type: execute_effect : Io a -> a // Simply run a side effect (call a function without parameters) // and returns the result. // Can be useful for chaining. template auto execute_effect(const F f) { return internal::invoke(f); } // API search type: interact : (String -> String) -> Io () // Takes a function F of type (String -> String) // and returns a function that // reads the entire input from standard input, // passes it through the given function, // and writes the result to standard output. template std::function interact(F f) { return [f]() -> void { std::cout << f(std::string( std::istreambuf_iterator(std::cin.rdbuf()), std::istreambuf_iterator())); }; } // API search type: execute_with_maybe : ((a -> void), Maybe a) -> Io Bool // Returns a function that // akes a unary side-effect function with // a maybe holding a matching type // and runs the sideeffect if the Maybe holds a just. // The returned function returns false if the maybe was a nothing. template std::function execute_with_maybe(Effect eff, const maybe& m) { return [eff, m]() -> bool { if (m.is_nothing()) { return false; } eff(m.unsafe_get_just()); return true; }; } } // namespace fplus libfplus-0.2.13/include/fplus/split.hpp000066400000000000000000000664011376322245400200540ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include namespace fplus { // API search type: group_by : (((a, a) -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Arrange the elements into groups using a given predicate. // Only groups of consecutive elements are formed. // For a version scanning the whole container see group_globally_by. // group_by((==), [1,2,2,2,3,2,2,4,5,5]) == [[1],[2,2,2],[3],[2,2],[4],[5,5]] // BinaryPredicate p is a (not neccessarily transitive) connectivity check. // O(n) template > ContainerOut group_by(BinaryPredicate p, const ContainerIn& xs) { // ContainerOut is not deduced to // SameContNewType(ContainerIn, ContainerIn) // here, since ContainerIn could be a std::string. internal::check_binary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); ContainerOut result; if (is_empty(xs)) return result; typedef typename ContainerOut::value_type InnerContainerOut; *internal::get_back_inserter(result) = InnerContainerOut(1, xs.front()); for (auto it = ++std::begin(xs); it != std::end(xs); ++it) { if (internal::invoke(p, result.back().back(), *it)) *internal::get_back_inserter(result.back()) = *it; else *internal::get_back_inserter(result) = InnerContainerOut(1, *it); } return result; } // API search type: group_on : ((a -> b), [a]) -> [[a]] // fwd bind count: 1 // Arrange elements equal after applying a transformer into groups. // Only groups of consecutive elements are formed. // For a version scanning the whole container see group_globally_on. // group_on((mod 10), [12,22,34]) == [[12,22],[34]] // O(n) template auto group_on(F f, const ContainerIn& xs) { return group_by(is_equal_by(f), xs); } // API search type: group_on_labeled : ((a -> b), [a]) -> [(b, [a])] // fwd bind count: 1 // Arrange elements equal after applying a transformer into groups, // adding the transformation result as a label to the group. // Only groups of consecutive elements are formed. // For a version scanning the whole container see group_globally_on_labeled. // group_on_labeled((mod 10), [12,22,34]) == [(2,[12,22]), (4,[34])] // O(n) template auto group_on_labeled(F f, const ContainerIn& xs) { const auto group = [](auto f1, const auto& xs1) { return group_by(f1, xs1); }; return internal::group_on_labeled_impl(group, f, xs); } // API search type: group : [a] -> [[a]] // fwd bind count: 0 // Arrange equal elements into groups. // Only groups of consecutive elements are formed. // For a version scanning the whole container see group_globally. // group([1,2,2,2,3,2,2,4,5,5]) == [[1],[2,2,2],[3],[2,2],[4],[5,5]] // O(n) template > ContainerOut group(const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); typedef typename ContainerIn::value_type T; auto pred = [](const T& x, const T& y) { return x == y; }; return group_by(pred, xs); } // API search type: group_globally_by : (((a, a) -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Arrange equal elements into groups. // group_globally_by((==), [1,2,2,2,3,2,2,4,5,5]) // == [[1],[2,2,2,2,2],[3],[4],[5,5]] // BinaryPredicate p is a // transitive (whenever p(x,y) and p(y,z), then also p(x,z)) equality check. // O(n^2) // If you need O(n*log(n)), sort and then use group_by template > ContainerOut group_globally_by(BinaryPredicate p, const ContainerIn& xs) { internal::check_binary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); typedef typename ContainerOut::value_type InnerContainerOut; ContainerOut result; for (const auto& x : xs) { bool found = false; for (auto& ys : result) { if (internal::invoke(p, x, ys.back())) { *internal::get_back_inserter(ys) = x; found = true; break; } } if (!found) { *internal::get_back_inserter(result) = InnerContainerOut(1, x); } } return result; } // API search type: group_globally_on : ((a -> b), [a]) -> [[a]] // fwd bind count: 1 // Arrange elements equal after applying a transformer into groups. // group_globally_on((mod 10), [12,34,22]) == [[12,22],[34]] // O(n^2) // If you need O(n*log(n)), sort and then use group_on template auto group_globally_on(F f, const ContainerIn& xs) { return group_globally_by(is_equal_by(f), xs); } // API search type: group_globally_on_labeled : ((a -> b), [a]) -> [(b, [a])] // fwd bind count: 1 // Arrange elements equal after applying a transformer into groups, // adding the transformation result as a label to the group. // group_globally_on_labeled((mod 10), [12,34,22]) == [(2,[12,22]),(4, [34])] // O(n^2) // If you need O(n*log(n)), sort and then use group_on_labeled template auto group_globally_on_labeled(F f, const ContainerIn& xs) { const auto group = [](auto f1, const auto& xs1) { return group_globally_by(f1, xs1); }; return internal::group_on_labeled_impl(group, f, xs); } // API search type: group_globally : [a] -> [[a]] // fwd bind count: 0 // Arrange equal elements into groups. // group_globally([1,2,2,2,3,2,2,4,5,5]) == [[1],[2,2,2,2,2],[3],[4],[5,5]] // O(n^2) // If you need O(n*log(n)), sort and then use group template > ContainerOut group_globally(const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); typedef typename ContainerIn::value_type T; auto pred = [](const T& x, const T& y) { return x == y; }; return group_globally_by(pred, xs); } // API search type: cluster_by : (((a, a) -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Groups connected components, stable regarding initial order. // cluster_by(\x y -> abs (y - x) <= 3), [2,3,6,4,12,11,20,23,8,4]) // == [[2,3,6,4,12,11,8,4],[20,23]] // BinaryPredicate p is a connectivity check, being // a) commutative (p(x,y) = p(y,x)) // b) reflexive (p(x,x) = true) // c) not neccessarily transitive, but can be // O(n^2), memory complexity also O(n^2) template > ContainerOut cluster_by(BinaryPredicate p, const ContainerIn& xs) { internal::check_binary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); typedef std::vector bools; bools zero_filled_row(size_of_cont(xs), 0); // adjecency matrix typedef std::vector boolss; boolss adj_mat(size_of_cont(xs), zero_filled_row); for (const auto& idx_and_val_y : enumerate(xs)) { auto idx_y = idx_and_val_y.first; auto val_y = idx_and_val_y.second; for (const auto& idx_and_val_x : enumerate(xs)) { auto idx_x = idx_and_val_x.first; auto val_x = idx_and_val_x.second; if (internal::invoke(p, val_y, val_x)) { adj_mat[idx_y][idx_x] = 1; } } } bools already_used = zero_filled_row; auto is_already_used = [&](std::size_t i) -> bool { return already_used[i] != 0; }; typedef std::vector idxs; typedef std::vector idxss; auto bools_to_idxs = [](const bools& activations) -> idxs { auto unsigned_char_to_bool = [](unsigned char x) { return x != 0; }; return find_all_idxs_by(unsigned_char_to_bool, activations); }; idxss idx_clusters; std::function process_idx = [&](std::size_t idx) -> void { auto connected_idxs = bools_to_idxs(adj_mat[idx]); auto new_connected_idxs = drop_if(is_already_used, connected_idxs); if (is_empty(new_connected_idxs)) { return; } idx_clusters.back() = append(idx_clusters.back(), new_connected_idxs); for (const auto& new_idx : new_connected_idxs) { already_used[new_idx] = 1; } for (const auto& new_idx : new_connected_idxs) { process_idx(new_idx); } }; typedef typename ContainerOut::value_type InnerContainerOut; for (const auto& idx : all_idxs(xs)) { if (is_already_used(idx)) { continue; } *internal::get_back_inserter(idx_clusters) = idxs(); *internal::get_back_inserter(idx_clusters.back()) = idx; already_used[idx] = 1; process_idx(idx); } typedef typename ContainerIn::value_type T; auto idx_to_val = [&](std::size_t idx) -> T { return elem_at_idx(idx, xs); }; auto idxs_to_vals = [&](const idxs& val_idxs) -> InnerContainerOut { return transform_convert(idx_to_val, sort(val_idxs)); }; return transform_convert(idxs_to_vals, idx_clusters); } // API search type: split_by : ((a -> Bool), Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every element fulfilling a predicate. // The splitting elements are discarded. // split_by(is_even, true, [1,3,2,2,5,5,3,6,7,9]) == [[1,3],[],[5,5,3],[7,9]] // also known as split_when // O(n) template > ContainerOut split_by (UnaryPredicate pred, bool allow_empty, const ContainerIn& xs) { internal::check_unary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); if (allow_empty && is_empty(xs)) { return {{}}; } ContainerOut result; auto itOut = internal::get_back_inserter(result); auto start = std::begin(xs); while (start != std::end(xs)) { const auto stop = std::find_if(start, std::end(xs), pred); if (start != stop || allow_empty) { *itOut = { start, stop }; } if (stop == std::end(xs)) { break; } start = internal::add_to_iterator(stop); if (allow_empty && start == std::end(xs)) { *itOut = typename ContainerOut::value_type(); } } return result; } // API search type: split_by_keep_separators : ((a -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Split a sequence at every element fulfilling a predicate. // The splitting elements are kept. // split_by_keep_separators(is_even, true, [1,3,2,2,5,5,3,6,7,9]) // == [[1,3],[2],[2,5,5,3],[6,7,9]] // O(n) template > ContainerOut split_by_keep_separators (UnaryPredicate pred, const ContainerIn& xs) { internal::check_unary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); ContainerOut result; if (is_empty(xs)) return result; auto itOut = internal::get_back_inserter(result); auto start = std::begin(xs); while (start != std::end(xs)) { const auto stop = std::find_if( internal::add_to_iterator(start), std::end(xs), pred); *itOut = { start, stop }; if (stop == std::end(xs)) { break; } start = stop; } return result; } // API search type: split : (a, Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every element equal to x. // The splitting elements are discarded. // split(0, true, [1,3,2,0,0,6,0,7,5]) == [[1,3,2],[],[6],[7,5]] // O(n) template auto split(const T& x, bool allow_empty, const ContainerIn& xs) { return split_by(is_equal_to(x), allow_empty, xs); } // API search type: split_one_of : ([a], Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every element present in delimiters. // The splitting elements are discarded. // Also known as split_words_by_many. // split_one_of([0,3], true [1,3,2,0,0,6,0,7,5]) == [[1],[2],[],[6],[7,5]] // split_one_of(" o", false, "How are u?") == ["H","w","are","u?"] // O(n) template auto split_one_of( const ContainerDelims delimiters, bool allow_empty, const ContainerIn& xs) { const auto pred = [&](const typename ContainerIn::value_type& x) -> bool { return is_elem_of(x, delimiters); }; return split_by(pred, allow_empty, xs); } // API search type: split_keep_separators : ((a -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Split a sequence at every element equal to x. // The splitting elements are kept. // split_keep_separators(2, true, [1,3,2,2,5,5,3,2,7,9]) // == [[1,3],[2],[2,5,5,3],[6,7,9]] // O(n) template auto split_keep_separators(const T& x, const ContainerIn& xs) { return split_by_keep_separators(is_equal_to(x), xs); } // API search type: split_at_idx : (Int, [a]) -> ([a], [a]) // fwd bind count: 1 // Split a sequence at a specific position. // split_at_idx(2, [0,1,2,3,4]) == ([0,1],[2,3,4]) template std::pair split_at_idx (std::size_t idx, const Container& xs) { assert(idx <= size_of_cont(xs)); return make_pair(get_segment(0, idx, xs), get_segment(idx, size_of_cont(xs), xs)); } // API search type: insert_at_idx : (Int, a, [a]) -> [a] // fwd bind count: 2 // Insert an element into a sequence at a specific position. // insert_at_idx(2, 0, [1,2,3,4]) == [1,2,0,3,4]. template Container insert_at_idx(std::size_t idx, const T& x, const Container& xs) { const auto splitted = split_at_idx(idx, xs); return concat(std::vector( { splitted.first, singleton_seq(x), splitted.second })); } // API search type: partition : ((a -> Bool), [a]) -> ([a], [a]) // fwd bind count: 1 // Split a sequence into two groups. // The first group contains all elements fulfilling the predicate. // The second group contains the remaining elements. // partition(is_even, [0,1,1,3,7,2,3,4]) == ([0,2,4],[1,1,3,7,3]) template std::pair partition (UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); Container matching; Container notMatching; auto itOutMatching = internal::get_back_inserter(matching); auto itOutNotMatching = internal::get_back_inserter(notMatching); for (const auto& x : xs) { if (internal::invoke(pred, x)) *itOutMatching = x; else *itOutNotMatching = x; } return make_pair(matching, notMatching); } // API search type: split_at_idxs : ([Int], [a]) -> [[a]] // fwd bind count: 1 // Split a sequence at specific indices. // split_at_idxs([2,5], [0,1,2,3,4,5,6,7]) == [[0,1],[2,3,4],[5,6,7]] // split_at_idxs([2,5,5], [0,1,2,3,4,5,6,7]) == [[0,1],[2,3,4],[],[5,6,7]] template > ContainerOut split_at_idxs(const ContainerIdxs& idxsIn, const ContainerIn& xs) { static_assert(std::is_same::value, "Indices must be std::size_t"); static_assert(std::is_same::value, "Containers do not match."); ContainerIdxs idxStartC = {0}; ContainerIdxs idxEndC = {size_of_cont(xs)}; std::vector containerIdxss = {idxStartC, idxsIn, idxEndC}; auto idxs = concat(containerIdxss); auto idxsClean = sort(idxs); ContainerOut result; internal::prepare_container(result, size_of_cont(idxsClean) - 1); auto itOut = internal::get_back_inserter(result); auto idxPairs = overlapping_pairs(idxsClean); for (const auto& idxPair : idxPairs) { *itOut = get_segment(idxPair.first, idxPair.second, xs); } return result; } // API search type: split_every : (Int, [a]) -> [[a]] // fwd bind count: 1 // Split a sequence every n elements. // split_every(3, [0,1,2,3,4,5,6,7]) == [[0,1,2],[3,4,5],[6,7]] // Also known as chunk or chunks. template > ContainerOut split_every(std::size_t n, const ContainerIn& xs) { return split_at_idxs< std::vector, ContainerIn, ContainerOut>( numbers_step( n, size_of_cont(xs), n), xs); } // API search type: split_by_token : ([a], Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every segment matching a token. // split_by_token(", ", true, "foo, bar, baz") == ["foo", "bar", "baz"] template > ContainerOut split_by_token(const ContainerIn& token, bool allow_empty, const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); const auto token_begins = find_all_instances_of_token_non_overlapping(token, xs); const auto token_ends = transform(add_to(size_of_cont(token)), token_begins); assert(is_sorted(interweave(token_begins, token_ends))); typedef std::vector idx_vec; const auto segments = zip( fplus::append(idx_vec(1, 0), token_ends), fplus::append(token_begins, idx_vec(1, size_of_cont(xs)))); ContainerOut result; auto itOut = internal::get_back_inserter(result); for (const auto& segment : segments) { if (segment.first != segment.second || allow_empty) *itOut = get_segment(segment.first, segment.second, xs); } return result; } // API search type: run_length_encode_by : (((a, a) -> Bool), [a]) -> [(Int, a)] // fwd bind count: 1 // RLE using a specific binary predicate as equality check. // run_length_encode_by((==),[1,2,2,2,2,3,3,2)) == [(1,1),(4,2),(2,3),(1,2)] template >> ContainerOut run_length_encode_by(BinaryPredicate pred, const ContainerIn& xs) { internal::check_binary_predicate_for_container(); ContainerOut result; auto groups = group_by(pred, xs); auto group_to_pair = [](const ContainerIn& group) -> std::pair { return std::make_pair(size_of_cont(group), group.front()); }; return transform(group_to_pair, groups); } // API search type: run_length_encode : [a] -> [(Int, a)] // fwd bind count: 0 // RLE. // run_length_encode([1,2,2,2,2,3,3,2)) == [(1,1),(4,2),(2,3),(1,2)] template auto run_length_encode(const ContainerIn& xs) { return run_length_encode_by(is_equal, xs); } // API search type: run_length_decode : [(Int, a)] -> [a] // fwd bind count: 0 // Inverse operation to run_length_encode. // run_length_decode([(1,1),(4,2),(2,3),(1,2)]) == [1,2,2,2,2,3,3,2) template auto run_length_decode(const ContainerIn& pairs) { static_assert(std::is_convertible::value, "Count type must be convertible to std::size_t."); const auto pair_to_vec = [](const Pair& p) { return replicate(p.first, p.second); }; return concat(transform(pair_to_vec, pairs)); } // API search type: span : ((a -> Bool), [a]) -> ([a], [a]) // fwd bind count: 1 // span, applied to a predicate p and a list xs, // returns a tuple where first element is longest prefix (possibly empty) // of xs of elements that satisfy p // and second element is the remainder of the list. // span(is_even, [0,2,4,5,6,7,8]) == ([0,2,4], [5,6,7,8]) template std::pair span(UnaryPredicate pred, const Container& xs) { auto maybeIdx = find_first_idx_by(logical_not(pred), xs); return { take(just_with_default(size_of_cont(xs), maybeIdx), xs), drop(just_with_default(size_of_cont(xs), maybeIdx), xs) }; } // API search type: divvy : (Int, Int, [a]) -> [[a]] // fwd bind count: 2 // Generates subsequences overlapping with a specific step. // divvy(5, 2, [0,1,2,3,4,5,6,7,8,9]) == [[0,1,2,3,4],[2,3,4,5,6],[4,5,6,7,8]] // divvy(length, 1, xs) is also known as aperture // divvy(1, step, xs) is also known as stride // (but withouts the nested lists in the result) template > ContainerOut divvy(std::size_t length, std::size_t step, const ContainerIn& xs) { assert(length > 0); assert(step > 0); const auto start_idxs = numbers_step( 0, size_of_cont(xs) - (length - 1), step); ContainerOut result; internal::prepare_container(result, size_of_cont(start_idxs)); auto itOut = internal::get_back_inserter(result); for (const auto start_idx : start_idxs) { *itOut = get_segment(start_idx, start_idx + length, xs); } return result; } // API search type: aperture : (Int, [a]) -> [[a]] // fwd bind count: 1 // Generates overlapping subsequences. // aperture(5, [0,1,2,3,4,5,6]) == [[0,1,2,3,4],[1,2,3,4,5],[2,3,4,5,6]] template > ContainerOut aperture(std::size_t length, const ContainerIn& xs) { assert(length > 0); const auto start_idxs = numbers( 0, size_of_cont(xs) - (length - 1)); ContainerOut result; internal::prepare_container(result, size_of_cont(start_idxs)); auto itOut = internal::get_back_inserter(result); for (const auto start_idx : start_idxs) { *itOut = get_segment(start_idx, start_idx + length, xs); } return result; } // API search type: stride : (Int, [a]) -> [a] // fwd bind count: 1 // Keeps every nth element. // stride(3, [0,1,2,3,4,5,6,7]) == [0,3,6] template Container stride(std::size_t step, const Container& xs) { assert(step > 0); Container ys; auto it = internal::get_back_inserter(ys); auto it_in = std::begin(xs); std::size_t i = 0; const auto xs_size = size_of_cont(xs); while(it_in != std::end(xs)) { *it = *it_in; std::size_t increment = std::min(step, xs_size - i); internal::advance_iterator(it_in, increment); i += increment; } return ys; } // API search type: winsorize : (Float, [Float]) -> [Float] // fwd bind count: 1 // Winsorizing // winsorize(0.1, [1,3,4,4,4,4,4,4,6,8]) == [3,3,4,4,4,4,4,4,6,6] template Container winsorize(double trim_ratio, const Container& xs) { if (size_of_cont(xs) < 2) { return xs; } trim_ratio = std::max(trim_ratio, 0.0); const auto xs_sorted = sort(xs); std::size_t amount = floor( trim_ratio * static_cast(size_of_cont(xs_sorted))); amount = std::min(size_of_cont(xs_sorted) / 2, amount); const auto parts = split_at_idxs( std::vector({amount, size_of_cont(xs_sorted) - amount}), xs_sorted); assert(size_of_cont(parts) == 3); typedef typename Container::value_type T; if (is_empty(parts[1])) { return Container(size_of_cont(xs_sorted), median(xs_sorted)); } else { const T lower = parts[1].front(); const T upper = parts[1].back(); const auto result = concat(std::vector({ Container(amount, lower), parts[1], Container(amount, upper)})); assert(size_of_cont(result) == size_of_cont(xs_sorted)); return result; } } // API search type: separate_on : ((a -> b), [a]) -> [[a]] // fwd bind count: 1 // Separate elements equal after applying a transformer into groups. // separate_on((mod 10), [12,22,34]) == [[12,34],[22]] template > ContainerOut separate_on(F f, const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); ContainerOut result; if (is_empty(xs)) { return result; } const auto groups = group_globally_on(f, xs); bool found = true; auto itOut = internal::get_back_inserter(result); std::size_t index = 0; while (found) { typename ContainerOut::value_type sub_result; found = false; auto itOutInner = internal::get_back_inserter(sub_result); for (auto& group: groups) { if (size_of_cont(group) > index) { *itOutInner = group[index]; found = true; } } if (found) { *itOut = sub_result; ++index; } } return result; } // API search type: separate : [a] -> [[a]] // fwd bind count: 0 // Separate equal elements into groups. // separate([1, 2, 2, 3, 3, 4, 4, 4]) == [[1, 2, 3, 4], [2, 3, 4], [4]] template > ContainerOut separate(const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); typedef typename ContainerIn::value_type T; return separate_on(identity, xs); } } // namespace fplus libfplus-0.2.13/include/fplus/stopwatch.hpp000066400000000000000000000014451376322245400207320ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include namespace fplus { class stopwatch { public: stopwatch() : beg_(clock::now()) {} void reset() { beg_ = clock::now(); } // time since creation or last reset in seconds double elapsed() const { return std::chrono::duration_cast (clock::now() - beg_).count(); } private: typedef std::chrono::high_resolution_clock clock; typedef std::chrono::duration> second; std::chrono::time_point beg_; }; } // namespace fplus libfplus-0.2.13/include/fplus/string_tools.hpp000066400000000000000000000140121376322245400214360ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include namespace fplus { // API search type: is_letter_or_digit : Char -> Bool // fwd bind count: 0 // Is character alphanumerical? template bool is_letter_or_digit(const typename String::value_type& c) { return std::isdigit(static_cast(c)) || std::isalpha(static_cast(c)); } // API search type: is_whitespace : Char -> Bool // fwd bind count: 0 // Is character a whitespace. template bool is_whitespace(const typename String::value_type& c) { return (c == 32 || is_in_interval(9, 14, static_cast(c))); } // API search type: is_line_break : Char -> Bool // fwd bind count: 0 // Newline character ('\n')? template bool is_line_break(const typename String::value_type& c) { return c == '\n'; } // API search type: clean_newlines : String -> String // fwd bind count: 0 // Replaces windows and mac newlines with linux newlines. template String clean_newlines(const String& str) { return replace_elems('\r', '\n', replace_tokens(String("\r\n"), String("\n"), str)); } // API search type: split_words : (Bool, String) -> [String] // fwd bind count: 1 // Splits a string by non-letter and non-digit characters. // split_words(false, "How are you?") == ["How", "are", "you"] template > ContainerOut split_words(const bool allowEmpty, const String& str) { return split_by(logical_not(is_letter_or_digit), allowEmpty, str); } // API search type: split_lines : (Bool, String) -> [String] // fwd bind count: 1 // Splits a string by the found newlines. // split_lines(false, "Hi,\nhow are you?") == ["Hi,", "How are you"] template > ContainerOut split_lines(bool allowEmpty, const String& str) { return split_by(is_line_break, allowEmpty, clean_newlines(str)); } // API search type: trim_whitespace_left : String -> String // fwd bind count: 0 // trim_whitespace_left(" text ") == "text " template String trim_whitespace_left(const String& str) { return drop_while(is_whitespace, str); } // API search type: trim_whitespace_right : String -> String // fwd bind count: 0 // Remove whitespace characters from the end of a string. // trim_whitespace_right(" text ") == " text" template String trim_whitespace_right(const String& str) { return trim_right_by(is_whitespace, str); } // API search type: trim_whitespace : String -> String // fwd bind count: 0 // Remove whitespace characters from the beginning and the end of a string. // trim_whitespace(" text ") == "text" template String trim_whitespace(const String& str) { return trim_by(is_whitespace, str); } // API search type: to_lower_case : String -> String // fwd bind count: 0 // Convert a string to lowercase characters. // to_lower_case("ChaRacTer&WorDs23") == "character&words23" template String to_lower_case(const String& str) { typedef typename String::value_type Char; return transform([](Char c) -> Char { return static_cast( std::tolower(static_cast(c))); }, str); } // API search type: to_lower_case_loc : (Locale, String) -> String // fwd bind count: 1 // Convert a string to lowercase characters using specified locale. // to_upper_case_loc(locale("ru_RU.utf8"), "cYrIlLiC КиРиЛлИцА") == "cyrillic кириллица" template String to_lower_case_loc(const std::locale &lcl, const String &str) { typedef typename String::value_type Char; return transform([&lcl](Char c) -> Char { return static_cast( std::tolower(c, lcl)); }, str); } // API search type: to_upper_case : String -> String // fwd bind count: 0 // Convert a string to uppercase characters. // to_upper_case("ChaRacTer&WorDs34") == "CHARACTER&WORDS34" template String to_upper_case(const String& str) { typedef typename String::value_type Char; return transform([](Char c) -> Char { return static_cast( std::toupper(static_cast(c))); }, str); } // API search type: to_upper_case_loc : (Locale, String) -> String // fwd bind count: 1 // Convert a string to uppercase characters using specified locale. // to_upper_case_loc(locale("ru_RU.utf8"), "cYrIlLiC КиРиЛлИцА") == "CYRILLIC КИРИЛЛИЦА" template String to_upper_case_loc(const std::locale &lcl, const String &str) { typedef typename String::value_type Char; return transform([&lcl](Char c) -> Char { return static_cast( std::toupper(c, lcl)); }, str); } // API search type: to_string_fill_left : (Char, Int, a) -> String // fwd bind count: 2 // Convert a type right-aligned string using a fill character. // to_string_fill_left('0', 5, 42) == "00042" // to_string_fill_left(' ', 5, 42) == " 42" template std::string to_string_fill_left(const std::string::value_type& filler, std::size_t min_size, const T& x) { return fill_left(filler, min_size, std::to_string(x)); } // API search type: to_string_fill_right : (Char, Int, a) -> String // fwd bind count: 2 // Convert a type left-aligned string using a fill character. // to_string_fill_right(' ', 5, 42) == "42 " template std::string to_string_fill_right(const std::string::value_type& filler, std::size_t min_size, const T& x) { return fill_right(filler, min_size, std::to_string(x)); } } // namespace fplus libfplus-0.2.13/include/fplus/timed.hpp000066400000000000000000000076411376322245400200240ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace fplus { using ExecutionTime = double; // in seconds // Holds a value of type T plus an execution time template class timed : public std::pair { using base_pair = std::pair; public: timed() : base_pair() {} timed(const T& val, ExecutionTime t = 0.) : base_pair(val, t) {} // Execution time in seconds (returns a double) ExecutionTime time_in_s() const { return base_pair::second; } // Execution time as a std::chrono::duration std::chrono::duration> duration_in_s() const { return std::chrono::duration>(time_in_s()); } // Inner value const T& get() const { return base_pair::first; } T& get() { return base_pair::first; } }; // API search type: show_timed : Timed a -> String // fwd bind count: 0 // show_timed((42,1)) -> "42 (1000ms)" template std::string show_timed(const fplus::timed& v) { std::string result = fplus::show(v.get()) + " (" + fplus::show(v.time_in_s() * 1000.) + "ms)"; return result; } namespace internal { template class timed_function_impl { public: explicit timed_function_impl(Fn fn) : _fn(fn) {}; template auto operator()(Args... args) { return _timed_result(args...); } private: template auto _timed_result(Args... args) { fplus::stopwatch timer; auto r = _fn(args...); auto r_t = fplus::timed(r, timer.elapsed()); return r_t; } Fn _fn; }; } // API search type: make_timed_function : ((a -> b)) -> (a -> Timed b) // fwd bind count: 0 // Transforms a function into a timed / benchmarked version of the same function. // - // Example: // - // using Ints = std::vector; // Ints ascending_numbers = fplus::numbers(0, 1000); // Ints shuffled_numbers = fplus::shuffle(std::mt19937::default_seed, ascending_numbers); // auto sort_func = [](const Ints& values) { return fplus::sort(values); }; // auto sort_bench = fplus::make_timed_function(sort_func); // auto sorted_numbers = sort_bench(shuffled_numbers); // assert(sorted_numbers.get() == ascending_numbers); // sorted_numbers.get() <=> actual output // assert(sorted_numbers.time_in_s() < 0.1); // // sorted_numbers.time_in_s() <=> execution time template auto make_timed_function(Fn f) { return internal::timed_function_impl(f); } namespace internal { template class timed_void_function_impl { public: explicit timed_void_function_impl(Fn fn) : _fn(fn) {}; template auto operator()(Args... args) { return _timed_result(args...); } private: template auto _timed_result(Args... args) { fplus::stopwatch timer; _fn(args...); return timer.elapsed(); } Fn _fn; }; } // API search type: make_timed_void_function : ((a -> Void)) -> (a -> Double) // fwd bind count: 0 // Transforms a void function into a timed / benchmarked version of the same function. // - // Example: // - // void foo() { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } // ... // auto foo_bench = make_timed_void_function(foo); // auto r = foo_bench(); // double run_time = foo_bench(); // in seconds template auto make_timed_void_function(Fn f) { return internal::timed_void_function_impl(f); } } libfplus-0.2.13/include/fplus/transform.hpp000066400000000000000000000567741376322245400207500ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fplus { // API search type: transform_with_idx : (((Int, a) -> b), [a]) -> [b] // fwd bind count: 1 // Apply a function to every index and corresponding element of a sequence. // transform_with_idx(f, [6, 4, 7]) == [f(0, 6), f(1, 4), f(2, 7)] template ::type> ContainerOut transform_with_idx(F f, const ContainerIn& xs) { internal::trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); std::size_t idx = 0; for (const auto& x : xs) { *it = internal::invoke(f, idx++, x); } return ys; } // API search type: transform_and_keep_justs : ((a -> Maybe b), [a]) -> [b] // fwd bind count: 1 // Map function over values and drop resulting nothings. // Also known as filter_map. template auto transform_and_keep_justs(F f, const ContainerIn& xs) { using X = typename ContainerIn::value_type; internal:: trigger_static_asserts(); using ContainerOut = typename internal::same_cont_new_t< ContainerIn, typename std::decay_t>::type>::type; auto transformed = transform(f, xs); return justs(transformed); } // API search type: transform_and_keep_oks : ((a -> Result b), [a]) -> [b] // fwd bind count: 1 // Map function over values and drop resulting errors. template auto transform_and_keep_oks(F f, const ContainerIn& xs) { using X = typename ContainerIn::value_type; internal:: trigger_static_asserts(); using ContainerOut = typename internal::same_cont_new_t< ContainerIn, typename std::decay_t>::ok_t>::type; auto transformed = transform(f, xs); return oks(transformed); } // API search type: transform_and_concat : ((a -> [b]), [a]) -> [b] // fwd bind count: 1 // Map function over values and concat results. // Also known as flat_map or concat_map. template auto transform_and_concat(F f, const ContainerIn& xs) { internal::trigger_static_asserts(); return concat(transform(f, xs)); } // API search type: replicate_elems : (Int, [a]) -> [a] // fwd bind count: 1 // Replicate every element n times, concatenate the result. // replicate_elems(3, [1,2]) == [1, 1, 1, 2, 2, 2] template Container replicate_elems(std::size_t n, const Container& xs) { typedef typename Container::value_type T; return transform_and_concat(bind_1st_of_2(replicate, n), xs); } // API search type: interleave : [[a]] -> [a] // fwd bind count: 0 // Return a sequence that contains elements from the provided sequences // in alternating order. If one list runs out of items, // appends the items from the remaining list. // interleave([[1,2,3],[4,5],[6,7,8]]) == [1,4,6,2,5,7,3,8] template ContainerOut interleave(const ContainerIn& xss) { typedef typename ContainerIn::value_type inner_t; typedef std::vector its_t; const auto inner_cbegin = [](const inner_t& xs) { return xs.cbegin(); }; const auto inner_cend = [](const inner_t& xs) { return xs.cend(); }; auto it_pairs = zip( transform_convert(inner_cbegin, xss), transform_convert(inner_cend, xss)); ContainerOut result; const std::size_t length = sum(transform(size_of_cont, xss)); internal::prepare_container(result, length); auto it_out = internal::get_back_inserter(result); bool still_appending = true; while (still_appending) { still_appending = false; for (auto& it_pair : it_pairs) { if (it_pair.first != it_pair.second) { *it_out = *it_pair.first; still_appending = true; ++it_pair.first; } } } return result; } // API search type: transpose : [[a]] -> [[a]] // fwd bind count: 0 // Transpose a nested sequence aka. table aka. two-dimensional matrix. // transpose([[1,2,3],[4,5,6],[7,8,9]]) == [[1,4,7],[2,5,8],[3,6,9]] // transpose([[1,2,3],[4,5],[7,8,9]]) == [[1,4,7],[2,5,8],[3,9]] template Container transpose(const Container& rows) { if (is_empty(rows)) { return {}; } return split_every( size_of_cont(rows), interleave(rows)); } namespace internal { template Container shuffle(internal::reuse_container_t, std::uint_fast32_t seed, Container&& xs) { std::mt19937 g(seed); std::shuffle(std::begin(xs), std::end(xs), g); return std::forward(xs); } template Container shuffle(internal::create_new_container_t, std::uint_fast32_t seed, const Container& xs) { Container ys = xs; return internal::shuffle(internal::reuse_container_t(), seed, std::move(ys)); } } // namespace internal // API search type: shuffle : (Int, [a]) -> [a] // fwd bind count: 1 // Returns a randomly shuffled version of xs. // Example call: shuffle(std::mt19937::default_seed, xs); // Example call: shuffle(std::random_device()(), xs); template auto shuffle(std::uint_fast32_t seed, Container&& xs) { return(internal::shuffle(internal::can_reuse_v{}, seed, std::forward(xs))); } // API search type: sample : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Returns n random elements from xs without replacement. // n has to be smaller than or equal to the number of elements in xs. // Also known as rnd_select. // Example call: sample(std::mt19937::default_seed, 3, xs); // Example call: sample(std::random_device()(), 3, xs); template Container sample(std::uint_fast32_t seed, std::size_t n, const Container& xs) { assert(n <= size_of_cont(xs)); return get_segment(0, n, shuffle(seed, xs)); } // API search type: random_element : (Int, [a]) -> a // fwd bind count: 1 // Returns one random element from xs. // xs must be non-empty. // Example call: random_element(std::mt19937::default_seed, xs) // Example call: random_element(std::random_device()(), xs) // Also known as choice. template typename Container::value_type random_element( std::uint_fast32_t seed, const Container& xs) { assert(is_not_empty(xs)); std::mt19937 gen(seed); std::uniform_int_distribution dis(0, size_of_cont(xs) - 1); return elem_at_idx(dis(gen), xs); } // API search type: random_elements : (Int, Int, [a]) -> a // fwd bind count: 2 // Returns random elements from xs with replacement. // xs must be non-empty. // Example call: random_elements(std::mt19937::default_seed, 10, xs) // Example call: random_elements(std::random_device()(), 10, xs) template Container random_elements( std::uint_fast32_t seed, std::size_t n, const Container& xs) { assert(is_not_empty(xs)); std::mt19937 gen(seed); std::uniform_int_distribution dis(0, size_of_cont(xs) - 1); const auto draw = [&]() -> typename Container::value_type { return elem_at_idx(dis(gen), xs); }; return generate(draw, n); } // API search type: apply_functions : ([(a -> b)], a) -> [b] // fwd bind count: 1 // Applies a list of functions to a value. template auto apply_functions(const FunctionContainer& functions, const FIn& x) { internal::trigger_static_asserts(); using FOut = std::decay_t>; using ContainerOut = typename internal::same_cont_new_t::type; ContainerOut ys; internal::prepare_container(ys, size_of_cont(functions)); auto it = internal::get_back_inserter(ys); for (const auto& f : functions) { *it = internal::invoke(f, x); } return ys; } // API search type: apply_function_n_times : ((a -> a), Int, a) -> a // fwd bind count: 2 // Applies a functional n times in a row. template auto apply_function_n_times(F f, std::size_t n, const FIn& x) { internal::trigger_static_asserts(); using FOut = std::decay_t>; static_assert(std::is_same::value, "Input and output of F must be the same type."); if (n == 0) { return x; } FOut y = internal::invoke(f, x); for (std::size_t i = 1; i < n; ++i) { y = internal::invoke(f, y); } return y; } // API search type: transform_parallelly : ((a -> b), [a]) -> [b] // fwd bind count: 1 // transform_parallelly((*2), [1, 3, 4]) == [2, 6, 8] // Same as transform, but can utilize multiple CPUs by using std::launch::async. // Only makes sense if one run of the provided function // takes enough time to justify the synchronization overhead. // One thread per container element is spawned. // Check out transform_parallelly_n_threads to limit the number of threads. template auto transform_parallelly(F f, const ContainerIn& xs) { using ContainerOut = typename internal:: same_cont_new_t_from_unary_f::type; using X = typename ContainerIn::value_type; internal::trigger_static_asserts(); auto handles = transform([&f](const X& x) { return std::async(std::launch::async, [&x, &f]() { return internal::invoke(f, x); }); }, xs); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); for (auto& handle : handles) { *it = handle.get(); } return ys; } // API search type: transform_parallelly_n_threads : (Int, (a -> b), [a]) -> [b] // fwd bind count: 2 // transform_parallelly_n_threads(4, (*2), [1, 3, 4]) == [2, 6, 8] // Same as transform, but uses n threads in parallel. // Only makes sense if one run of the provided function // takes enough time to justify the synchronization overhead. // Can be used for applying the MapReduce pattern. template auto transform_parallelly_n_threads(std::size_t n, F f, const ContainerIn& xs) { using ContainerOut = typename internal:: same_cont_new_t_from_unary_f::type; using X = typename ContainerIn::value_type; using Y = internal::invoke_result_t; using x_ptr_t = const X*; auto queue = transform_convert>( [](const X& x) -> x_ptr_t { return &x; }, xs); std::mutex queue_mutex; std::mutex thread_results_mutex; std::map> thread_results; std::size_t queue_idx = 0; const auto worker_func = [&]() { for (;;) { std::size_t idx = std::numeric_limits::max(); x_ptr_t x_ptr = nullptr; { std::lock_guard queue_lock(queue_mutex); if (queue_idx == queue.size()) { return; } idx = queue_idx; x_ptr = queue[idx]; ++queue_idx; } const auto y = internal::invoke(f, *x_ptr); { std::lock_guard thread_results_lock( thread_results_mutex); thread_results.insert(std::make_pair(idx, y)); } } }; const auto create_thread = [&]() -> std::thread { return std::thread(worker_func); }; auto threads = generate>(create_thread, n); for (auto& thread : threads) { thread.join(); } return get_map_values( thread_results); } // API search type: reduce_parallelly : (((a, a) -> a), a, [a]) -> a // fwd bind count: 2 // reduce_parallelly((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 // Same as reduce, but can utilize multiple CPUs by using std::launch::async. // Combines the initial value and all elements of the sequence // using the given function in unspecified order. // The set of f, init and value_type should form a commutative monoid. // One thread per container element is spawned. // Check out reduce_parallelly_n_threads to limit the number of threads. template typename Container::value_type reduce_parallelly( F f, const typename Container::value_type& init, const Container& xs) { if (is_empty(xs)) { return init; } else if (size_of_cont(xs) == 1) { return internal::invoke(f, init, xs.front()); } else { typedef typename Container::value_type T; const auto f_on_pair = [f](const std::pair& p) -> T { return internal::invoke(f, p.first, p.second); }; auto transform_result = transform_parallelly(f_on_pair, adjacent_pairs(xs)); if (size_of_cont(xs) % 2 == 1) { transform_result.push_back(last(xs)); } return reduce_parallelly(f, init, transform_result); } } // API search type: reduce_parallelly_n_threads : (Int, ((a, a) -> a), a, [a]) -> a // fwd bind count: 3 // reduce_parallelly_n_threads(2, (+), 0, [1, 2, 3]) == (0+1+2+3) == 6 // Same as reduce, but can utilize multiple CPUs by using std::launch::async. // Combines the initial value and all elements of the sequence // using the given function in unspecified order. // The set of f, init and value_type should form a commutative monoid. template typename Container::value_type reduce_parallelly_n_threads( std::size_t n, F f, const typename Container::value_type& init, const Container& xs) { if (is_empty(xs)) { return init; } else if (size_of_cont(xs) == 1) { return internal::invoke(f, init, xs.front()); } else { typedef typename Container::value_type T; const auto f_on_pair = [f](const std::pair& p) -> T { return internal::invoke(f, p.first, p.second); }; auto transform_result = transform_parallelly_n_threads(n, f_on_pair, adjacent_pairs(xs)); if (size_of_cont(xs) % 2 == 1) { transform_result.push_back(last(xs)); } return reduce_parallelly_n_threads(n, f, init, transform_result); } } // API search type: reduce_1_parallelly : (((a, a) -> a), [a]) -> a // fwd bind count: 1 // reduce_1_parallelly((+), [1, 2, 3]) == (1+2+3) == 6 // Same as reduce_1, but can utilize multiple CPUs by using std::launch::async. // Joins all elements of the sequence using the given function // in unspecified order. // The set of f and value_type should form a commutative semigroup. // One thread per container element is spawned. // Check out reduce_1_parallelly_n_threads to limit the number of threads. template typename Container::value_type reduce_1_parallelly(F f, const Container& xs) { assert(is_not_empty(xs)); if (size_of_cont(xs) == 1) { return xs.front(); } else { typedef typename Container::value_type T; const auto f_on_pair = [f](const std::pair& p) -> T { return internal::invoke(f, p.first, p.second); }; auto transform_result = transform_parallelly(f_on_pair, adjacent_pairs(xs)); if (size_of_cont(xs) % 2 == 1) { transform_result.push_back(last(xs)); } return reduce_1_parallelly(f, transform_result); } } // API search type: reduce_1_parallelly_n_threads : (Int, ((a, a) -> a), [a]) -> a // fwd bind count: 2 // reduce_1_parallelly_n_threads(2, (+), [1, 2, 3]) == (1+2+3) == 6 // Same as reduce_1, but can utilize multiple CPUs by using std::launch::async. // Joins all elements of the sequence using the given function // in unspecified order. // The set of f and value_type should form a commutative semigroup. template typename Container::value_type reduce_1_parallelly_n_threads( std::size_t n, F f, const Container& xs) { assert(is_not_empty(xs)); if (size_of_cont(xs) == 1) { return xs.front(); } else { typedef typename Container::value_type T; const auto f_on_pair = [f](const std::pair& p) -> T { return internal::invoke(f, p.first, p.second); }; auto transform_result = transform_parallelly_n_threads(n, f_on_pair, adjacent_pairs(xs)); if (size_of_cont(xs) % 2 == 1) { transform_result.push_back(last(xs)); } return reduce_1_parallelly_n_threads(n, f, transform_result); } } // API search type: keep_if_parallelly : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Same as keep_if but using multiple threads. // Can be useful if calling the predicate takes some time. // keep_if_parallelly(is_even, [1, 2, 3, 2, 4, 5]) == [2, 2, 4] // One thread per container element is spawned. // Check out keep_if_parallelly_n_threads to limit the number of threads. template Container keep_if_parallelly(Pred pred, const Container& xs) { // Avoid a temporary std::vector. const auto idxs = find_all_idxs_by( is_equal_to(1), transform_parallelly([pred](const auto & x) -> std::uint8_t { return pred(x) ? 1 : 0; }, xs)); return elems_at_idxs(idxs, xs); } // API search type: keep_if_parallelly_n_threads : (Int, (a -> Bool), [a]) -> [a] // fwd bind count: 2 // Same as keep_if but using multiple threads. // Can be useful if calling the predicate takes some time. // keep_if_parallelly_n_threads(3, is_even, [1, 2, 3, 2, 4, 5]) == [2, 2, 4] template Container keep_if_parallelly_n_threads( std::size_t n, Pred pred, const Container& xs) { // Avoid a temporary std::vector. const auto idxs = find_all_idxs_by( is_equal_to(1), transform_parallelly_n_threads(n, [pred](const auto & x) -> std::uint8_t { return pred(x) ? 1 : 0; }, xs)); return elems_at_idxs(idxs, xs); } // API search type: transform_reduce : ((a -> b), ((b, b) -> b), b, [a]) -> b // fwd bind count: 3 // transform_reduce(square, add, 0, [1,2,3]) == 0+1+4+9 = 14 // The set of binary_f, init and unary_f::output should form a // commutative monoid. template auto transform_reduce(UnaryF unary_f, BinaryF binary_f, const Acc& init, const Container& xs) { return reduce(binary_f, init, transform(unary_f, xs)); } // API search type: transform_reduce_1 : ((a -> b), ((b, b) -> b), [a]) -> b // fwd bind count: 2 // transform_reduce_1(square, add, [1,2,3]) == 0+1+4+9 = 14 // The set of binary_f, and unary_f::output should form // a commutative semigroup. template auto transform_reduce_1(UnaryF unary_f, BinaryF binary_f, const Container& xs) { return reduce_1(binary_f, transform(unary_f, xs)); } // API search type: transform_reduce_parallelly : ((a -> b), ((b, b) -> b), b, [a]) -> b // fwd bind count: 3 // transform_reduce_parallelly(square, add, 0, [1,2,3]) == 0+1+4+9 = 14 // Also known as map_reduce. // The set of binary_f, init and unary_f::output // should form a commutative monoid. // One thread per container element is spawned. // Check out transform_reduce_parallelly_n_threads to limit the number of threads. template auto transform_reduce_parallelly(UnaryF unary_f, BinaryF binary_f, const Acc& init, const Container& xs) { return reduce_parallelly(binary_f, init, transform_parallelly(unary_f, xs)); } // API search type: transform_reduce_parallelly_n_threads : (Int, (a -> b), ((b, b) -> b), b, [a]) -> b // fwd bind count: 4 // transform_reduce_parallelly_n_threads(2, square, add, 0, [1,2,3]) == 0+1+4+9 = 14 // Also known as map_reduce. // The set of binary_f, init and unary_f::output // should form a commutative monoid. template auto transform_reduce_parallelly_n_threads(std::size_t n, UnaryF unary_f, BinaryF binary_f, const Acc& init, const Container& xs) { return reduce_parallelly_n_threads( n, binary_f, init, transform_parallelly_n_threads(n, unary_f, xs)); } // API search type: transform_reduce_1_parallelly : ((a -> b), ((b, b) -> b), [a]) -> b // fwd bind count: 2 // transform_reduce_1_parallelly(square, add, [1,2,3]) == 0+1+4+9 = 14 // Also Known as map_reduce. // The set of binary_f, and unary_f::output // should form a commutative semigroup. // One thread per container element is spawned. // Check out transform_reduce_1_parallelly_n_threads to limit the number of threads. template auto transform_reduce_1_parallelly(UnaryF unary_f, BinaryF binary_f, const Container& xs) { return reduce_1_parallelly(binary_f, transform_parallelly(unary_f, xs)); } // API search type: transform_reduce_1_parallelly_n_threads : (Int, (a -> b), ((b, b) -> b), [a]) -> b // fwd bind count: 3 // transform_reduce_1_parallelly_n_threads(2, square, add, [1,2,3]) == 0+1+4+9 = 14 // Also Known as map_reduce. // The set of binary_f, and unary_f::output // should form a commutative semigroup. template auto transform_reduce_1_parallelly_n_threads(std::size_t n, UnaryF unary_f, BinaryF binary_f, const Container& xs) { return reduce_1_parallelly_n_threads( n, binary_f, transform_parallelly_n_threads(n, unary_f, xs)); } } // namespace fplus libfplus-0.2.13/include/fplus/tree.hpp000066400000000000000000000141431376322245400176540ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include namespace fplus { template struct tree { tree (const T& value, const std::vector>& children) : value_(value), children_(children) {} T value_; std::vector> children_; }; namespace internal { template tree make_singleton_tree(const T& x) { return {x, {}}; } } // namespace internal namespace internal { template std::vector> presort_trees(BinaryPredicate tree_is_child_of, std::vector> xs_orig) { auto xs = fplus::convert_container>>(xs_orig); std::vector> result; while (!xs.empty()) { for (auto it = std::begin(xs); it != std::end(xs);) { bool has_children = false; for (auto it_rest = std::begin(xs); it_rest != std::end(xs); ++it_rest) { if (it_rest != it && tree_is_child_of(*it_rest, *it)) { has_children = true; } } if (!has_children) { result.push_back(*it); it = xs.erase(it); } else { ++it; } } } return result; } template // todo: name? TreeCont trees_from_sequence_helper( BinaryPredicate tree_is_child_of, TreeCont xs_unsorted) { TreeCont result; auto xs = presort_trees(tree_is_child_of, xs_unsorted); for (auto it = std::begin(xs); it != std::end(xs); ++it) { const auto find_pred = bind_1st_of_2(tree_is_child_of, *it); auto it_find_begin = it; internal::advance_iterator(it_find_begin, 1); auto parent_it = std::find_if(it_find_begin, std::end(xs), find_pred); if (parent_it != std::end(xs)) { parent_it->children_.push_back(*it); } else { result.push_back(*it); } } return result; } } // namespace internal // API search type: trees_from_sequence : (((a, a) -> Bool), [a]) -> [Tree a] // fwd bind count: 1 // Converts the sequence into a tree considering the given binary predicate. template // todo: name? std::vector> trees_from_sequence( BinaryPredicate is_child_of, const Container& xs) { internal::check_binary_predicate_for_container(); typedef typename Container::value_type T; typedef tree Tree; const auto singletons = transform_convert>( internal::make_singleton_tree, xs); const auto tree_is_child_of = [is_child_of](const tree& a, const tree& b) -> bool { return is_child_of(a.value_, b.value_); }; return internal::trees_from_sequence_helper( tree_is_child_of, std::move(singletons)); } namespace internal { // -1 = a < b // 0 = a == b // 1 = b < a template int tree_cmp(const tree& a, const tree& b) { if(a.value_ < b.value_) { return -1; } else if(b.value_ < a.value_) { return 1; } else { const auto results = zip_with(tree_cmp, sort_by(tree_cmp, a.children_), sort_by(tree_cmp, b.children_)); return just_with_default(0, find_first_by( bind_1st_of_2(is_not_equal, 0), results)); } } template bool tree_less(const tree& a, const tree& b) { return tree_cmp(a, b) < 0; } } // namespace internal namespace internal { template bool are_normalized_trees_equal(const tree& a, const tree& b) { if (a.value_ != b.value_ || a.children_.size() != b.children_.size()) { return false; } else { return all(zip_with(are_normalized_trees_equal, a.children_, b.children_)); } } template tree normalize_tree(tree x) { x.children_ = sort_by( internal::tree_less, transform(normalize_tree, x.children_)); return x; } } // namespace internal // API search type: are_trees_equal : (Tree a, Tree a) -> Bool // fwd bind count: 1 template bool are_trees_equal(const tree& a, const tree& b) { return internal::are_normalized_trees_equal( internal::normalize_tree(a), internal::normalize_tree(b)); } // API search type: tree_size : Tree a -> Int // fwd bind count: 0 // A tree with only one element (root) has size 1. template std::size_t tree_size(const tree& x) { return 1 + sum(transform(tree_size, x.children_)); } // API search type: tree_depth : Tree a -> Int // fwd bind count: 0 // A tree with only one element (root) has depth 1. template std::size_t tree_depth(const tree& x) { return 1 + just_with_default(0, maximum_maybe(transform(tree_depth, x.children_))); } // API search type: flatten_tree_depth_first : Tree a -> [a] // fwd bind count: 0 template std::vector flatten_tree_depth_first(const tree& x) { return prepend_elem(x.value_, transform_and_concat(flatten_tree_depth_first, x.children_)); } // API search type: flatten_tree_breadth_first : Tree a -> [a] // fwd bind count: 0 template std::vector flatten_tree_breadth_first(const tree& x) { std::vector result; result.reserve(tree_size(x)); std::queue*> q; q.push(&x); while (!q.empty()) { const auto current = q.front(); q.pop(); result.push_back(current->value_); for (const auto& c : current->children_) { q.push(&c); } } return result; } } // namespace fplus libfplus-0.2.13/include/fplus/variant.hpp000066400000000000000000000250331376322245400203610ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include namespace fplus { namespace internal { // http://stackoverflow.com/a/18987405/1866775 template struct is_one_of; template struct is_one_of { static constexpr bool value = false; }; template struct is_one_of { static constexpr bool value = std::is_same::value || is_one_of::value; }; template struct is_one_of> { static constexpr bool value = is_one_of::value; }; template struct is_unique; template <> struct is_unique<> { static constexpr bool value = true; }; template struct is_unique { static constexpr bool value = is_unique::value && !is_one_of::value; }; template struct is_unique> { static constexpr bool value = is_unique::value; }; template struct are_same; template <> struct are_same<> { static constexpr bool value = true; }; template struct are_same { static constexpr bool value = std::is_same::value; }; template struct are_same { static constexpr bool value = are_same::value && std::is_same::value; }; template struct are_same> { static constexpr bool value = are_same::value; }; // http://stackoverflow.com/a/3273571/1866775 template class List, template class Mod, typename ...Args> struct transform_parameter_pack { typedef List::type...> type; }; template struct as_shared_pointer { typedef std::shared_ptr type; }; // http://stackoverflow.com/a/27588263/1866775 template struct get_index; template struct get_index : std::integral_constant {}; template struct get_index : std::integral_constant::value> {}; template struct get_index { // condition is always false, but should be dependant of T static_assert(sizeof(T) == 0, "element not found"); }; template struct parameter_pack_head { typedef T type; }; template struct function_first_input_type { typedef typename std::remove_const< typename std::remove_reference< typename utils::function_traits< F>::template arg<0>::type>::type>::type type; }; template struct unary_function_result_type { static_assert(utils::function_traits::arity == 1, "Wrong arity."); typedef typename function_first_input_type::type T; typedef std::decay_t> type; }; // http://stackoverflow.com/a/42493805/1866775 template struct tag { }; template struct type_set_eq_helper: tag... { }; template struct type_set_eq: std::false_type { }; template struct bool_pack { }; template using my_and = std::is_same, bool_pack>; template struct type_set_eq, std::tuple, typename std::enable_if< (sizeof...(Ts1) == sizeof...(Ts2)) && my_and< std::is_base_of, type_set_eq_helper>::value... >::value >::type >: std::true_type { }; // http://stackoverflow.com/a/42581257/1866775 template struct is_superset_of; template struct is_superset_of> { static const bool value = is_one_of::value && is_superset_of>::value; }; template struct is_superset_of> { static const bool value = true; }; // http://stackoverflow.com/a/36934374/1866775 template using all_true = std::is_same, bool_pack>; } // namespace internal template struct variant { static_assert(internal::is_unique::value, "Types must be unique."); static_assert(internal::all_true<(!std::is_reference::value)...>::value, "No reference types allowed."); static_assert(internal::all_true<(!std::is_const::value)...>::value, "No const types allowed."); static_assert(sizeof...(Types) >= 1, "Please provide at least one type."); template variant(const T& val) : shared_ptrs_({}) { std::get::value>(shared_ptrs_) = std::make_shared(val); } template bool is() const { static_assert( internal::is_one_of::value , "Type must match one possible variant type."); const auto ptr = std::get::value>(shared_ptrs_); return static_cast(ptr); } friend bool operator== ( const variant& a, const variant& b) { return a.shared_ptrs_ == b.shared_ptrs_; } friend bool operator!= ( const variant& a, const variant& b) { return a.shared_ptrs_ != b.shared_ptrs_; } template auto visit_one(F f) const { using T = typename internal::function_first_input_type::type; using Ret = internal::invoke_result_t; internal::trigger_static_asserts(); static_assert( internal::is_one_of< typename internal::function_first_input_type::type, Types...>::value , "Function input must match one variant type."); static_assert(!std::is_same, void>::value, "Function must return non-void type."); const auto ptr = std::get::value>(shared_ptrs_); if (ptr) { return just(internal::invoke(f, *ptr)); } return nothing>(); } template auto visit(Fs ... fs) const -> typename internal::unary_function_result_type< typename internal::parameter_pack_head::type>::type { typedef typename internal::unary_function_result_type< typename internal::parameter_pack_head::type>::type Res; static_assert( sizeof...(Fs) >= std::tuple_size::value, "Too few functions provided."); static_assert( sizeof...(Fs) <= std::tuple_size::value, "Too many functions provided."); typedef typename internal::transform_parameter_pack< std::tuple, internal::unary_function_result_type, Fs... >::type return_types_tuple; typedef typename internal::transform_parameter_pack< std::tuple, internal::function_first_input_type, Fs... >::type function_first_input_types_tuple; static_assert( internal::is_unique::value, "Only one function per input type allowed."); static_assert( internal::are_same::value, "All Functions must return the same type."); static_assert( internal::type_set_eq>::value, "Functions do not cover all possible types."); const auto results = justs(visit_helper(fs...)); assert(size_of_cont(results) == 1); return head(results); } template variant transform(Fs ... fs) const { static_assert( sizeof...(Fs) >= std::tuple_size::value, "Too few functions provided."); static_assert( sizeof...(Fs) <= std::tuple_size::value, "Too many functions provided."); typedef typename internal::transform_parameter_pack< std::tuple, internal::unary_function_result_type, Fs... >::type return_types_tuple; typedef typename internal::transform_parameter_pack< std::tuple, internal::function_first_input_type, Fs... >::type function_first_input_types_tuple; static_assert( internal::type_set_eq>::value, "Functions do not cover all possible types."); static_assert( internal::is_superset_of, return_types_tuple>::value, "All Functions must return a possible variant type."); return visit(fs...); } private: template std::vector> visit_helper(F f) const { return {visit_one(f)}; } template std::vector> visit_helper(F f, Fs ... fs) const { return fplus::append(visit_helper(f), visit_helper(fs...)); } typedef typename internal::transform_parameter_pack< std::tuple, internal::as_shared_pointer, Types... >::type shared_ptr_pack; shared_ptr_pack shared_ptrs_; }; } // namespace fplus libfplus-0.2.13/include_all_in_one/000077500000000000000000000000001376322245400172275ustar00rootroot00000000000000libfplus-0.2.13/include_all_in_one/CMakeLists.txt000066400000000000000000000004711376322245400217710ustar00rootroot00000000000000find_package (Python COMPONENTS Interpreter QUIET) if (Python_FOUND) add_custom_target( include_all_in_one ALL COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/make_all_in_one.py ) else() message(STATUS "Not generating include_all_in_one, python was not found.") endif() libfplus-0.2.13/include_all_in_one/include/000077500000000000000000000000001376322245400206525ustar00rootroot00000000000000libfplus-0.2.13/include_all_in_one/include/fplus/000077500000000000000000000000001376322245400220035ustar00rootroot00000000000000libfplus-0.2.13/include_all_in_one/include/fplus/fplus.hpp000066400000000000000000020167041376322245400236570ustar00rootroot00000000000000#pragma once // // fplus.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // compare.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // function_traits.hpp // //-------------------------------------- // utils/traits: Additional type traits //-------------------------------------- // // Copyright kennytm (auraHT Ltd.) 2011. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) /** ```` --- Additional type traits ================================================= This module provides additional type traits and related functions, missing from the standard library. */ #ifndef TRAITS_HPP_9ALQFEFX7TO #define TRAITS_HPP_9ALQFEFX7TO 1 #include #include #include #include // // internal/meta.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include namespace fplus { namespace internal { // C++14 compatible void_t (http://en.cppreference.com/w/cpp/types/void_t) template struct make_void { using type = void; }; template using void_t = typename make_void::type; // Sometimes you don't want to use std::decay_t, and the temptation of short // writing can be huge... template using uncvref_t = std::remove_cv_t>; // disjunction/conjunction/negation, useful to short circuit SFINAE checks // Use with parsimony, MSVC 2015 can have ICEs quite easily template struct disjunction : std::false_type { }; template struct disjunction : B1 { }; template struct disjunction : std::conditional>::type { }; template struct conjunction : std::true_type { }; template struct conjunction : B1 { }; template struct conjunction : std::conditional, B1>::type { }; template struct negation : std::integral_constant { }; // non short-circuiting meta functions // source: https://stackoverflow.com/a/27221517/4116453 template struct bool_pack; template struct all_of : std::is_same, bool_pack> { }; // there seems to be a bug in libc++'s std::is_function // provide our own (cppreference one) // (the MSVC implementation seems correct) #ifndef _MSC_VER #define PROVIDE_IS_FUNCTION_POLYFILL #endif #ifndef PROVIDE_IS_FUNCTION_POLYFILL template using is_function = std::is_function; #else //PROVIDE_IS_FUNCTION_POLYFILL // primary template template struct is_function : std::false_type { }; // specialization for regular functions template struct is_function : std::true_type {}; // specialization for variadic functions such as std::printf template struct is_function : std::true_type {}; // specialization for function types that have cv-qualifiers template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; // specialization for function types that have ref-qualifiers template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; #endif //PROVIDE_IS_FUNCTION_POLYFILL template struct reverse_integer_sequence_impl; template struct reverse_integer_sequence_impl> : std::integer_sequence { }; template struct reverse_integer_sequence_impl> : std::integer_sequence { }; template using reverse_integer_sequence = reverse_integer_sequence_impl; template using make_reverse_integer_sequence = reverse_integer_sequence>; template using reverse_index_sequence = reverse_integer_sequence>; template using make_reverse_index_sequence = make_reverse_integer_sequence; } } namespace fplus { // source: https://github.com/kennytm/utils namespace utils { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #endif /** .. macro:: DECLARE_HAS_TYPE_MEMBER(member_name) This macro declares a template ``has_member_name`` which will check whether a type member ``member_name`` exists in a particular type. Example:: DECLARE_HAS_TYPE_MEMBER(result_type) ... printf("%d\n", has_result_type< std::plus >::value); // ^ prints '1' (true) printf("%d\n", has_result_type< double(*)() >::value); // ^ prints '0' (false) */ #define DECLARE_HAS_TYPE_MEMBER(member_name) \ template \ struct has_##member_name \ { enum { value = false }; }; \ template \ struct has_##member_name::type> \ { enum { value = true }; }; /** .. type:: struct utils::function_traits Obtain compile-time information about a function object *F*. This template currently supports the following types: * Normal function types (``R(T...)``), function pointers (``R(*)(T...)``) and function references (``R(&)(T...)`` and ``R(&&)(T...)``). * Member functions (``R(C::*)(T...)``) * ``std::function`` * Type of lambda functions, and any other types that has a unique ``operator()``. * Type of ``std::mem_fn`` (only for GCC's libstdc++ and LLVM's libc++). Following the C++ spec, the first argument will be a raw pointer. */ template struct function_traits : public function_traits {}; namespace xx_impl { template struct memfn_type { typedef typename std::conditional< std::is_const::value, typename std::conditional< std::is_volatile::value, R (C::*)(A...) const volatile, R (C::*)(A...) const >::type, typename std::conditional< std::is_volatile::value, R (C::*)(A...) volatile, R (C::*)(A...) >::type >::type type; }; } template struct function_traits { /** .. type:: type result_type The type returned by calling an instance of the function object type *F*. */ typedef ReturnType result_type; /** .. type:: type function_type The function type (``R(T...)``). */ typedef ReturnType function_type(Args...); /** .. type:: type member_function_type The member function type for an *OwnerType* (``R(OwnerType::*)(T...)``). */ template using member_function_type = typename xx_impl::memfn_type< typename std::remove_pointer::type>::type, ReturnType, Args... >::type; /** .. data:: static const size_t arity Number of arguments the function object will take. */ enum { arity = sizeof...(Args) }; /** .. type:: type arg::type The type of the *n*-th argument. */ template struct arg { typedef typename std::tuple_element>::type type; }; }; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits { typedef ClassType& owner_type; }; template struct function_traits : public function_traits { typedef const ClassType& owner_type; }; template struct function_traits : public function_traits { typedef volatile ClassType& owner_type; }; template struct function_traits : public function_traits { typedef const volatile ClassType& owner_type; }; template struct function_traits> : public function_traits {}; #if defined(_GLIBCXX_FUNCTIONAL) #define MEM_FN_SYMBOL_XX0SL7G4Z0J std::_Mem_fn #elif defined(_LIBCPP_FUNCTIONAL) #define MEM_FN_SYMBOL_XX0SL7G4Z0J std::__mem_fn #endif #ifdef MEM_FN_SYMBOL_XX0SL7G4Z0J template struct function_traits> : public function_traits {}; template struct function_traits> : public function_traits {}; template struct function_traits> : public function_traits {}; template struct function_traits> : public function_traits {}; template struct function_traits> : public function_traits {}; #undef MEM_FN_SYMBOL_XX0SL7G4Z0J #endif template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; template struct function_traits : public function_traits {}; #define FORWARD_RES_8QR485JMSBT \ typename std::conditional< \ std::is_lvalue_reference::value, \ T&, \ typename std::remove_reference::type&& \ >::type /** .. function:: auto utils::forward_like(T&& t) noexcept Forward the reference *t* like the type of *Like*. That means, if *Like* is an lvalue (reference), this function will return an lvalue reference of *t*. Otherwise, if *Like* is an rvalue, this function will return an rvalue reference of *t*. This is mainly used to propagate the expression category (lvalue/rvalue) of a member of *Like*, generalizing ``std::forward``. */ template FORWARD_RES_8QR485JMSBT forward_like(T&& input) noexcept { return static_cast(input); } #undef FORWARD_RES_8QR485JMSBT /** .. type:: struct utils::copy_cv Copy the CV qualifier between the two types. For example, ``utils::copy_cv::type`` will become ``const double``. */ template struct copy_cv { private: typedef typename std::remove_cv::type raw_To; typedef typename std::conditional::value, const raw_To, raw_To>::type const_raw_To; public: /** .. type:: type type Result of cv-copying. */ typedef typename std::conditional::value, volatile const_raw_To, const_raw_To>::type type; }; /** .. type:: struct utils::pointee Returns the type by derefering an instance of *T*. This is a generalization of ``std::remove_pointer``, that it also works with iterators. */ template struct pointee { /** .. type:: type type Result of dereferencing. */ typedef typename std::remove_reference())>::type type; }; /** .. function:: std::add_rvalue_reference::type utils::rt_val() noexcept Returns a value of type *T*. It is guaranteed to do nothing and will not throw a compile-time error, but using the returned result will cause undefined behavior. */ template typename std::add_rvalue_reference::type rt_val() noexcept { return std::move(*static_cast(nullptr)); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif } } namespace fplus { namespace internal { template struct is_std_function : std::false_type { }; template struct is_std_function> : std::true_type { }; // Those traits are needed to not perform arity checks on a generic-lambd // or a templated/overloaded operator() template struct has_function_traits : std::false_type { }; // There is a bug with GCC 7 when a std::function is passed as T. // It produces an ambiguous call between this one and the std::function overload // It's related to our void_t implementation, the C++14 compatible version does not // work, whereas the C++17 one does... // // So, help GCC a bit with is_std_function template struct has_function_traits::value, void_t>> : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits : std::true_type { }; template struct has_function_traits> : std::true_type { }; } } #endif // // composition.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // internal/apply.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include // // internal/invoke.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include // borrowed to libc++ #define FPLUS_INVOKE_RETURN(...) \ ->decltype(__VA_ARGS__) \ { \ return __VA_ARGS__; \ } namespace fplus { namespace internal { // We need std::invoke to detect callable objects // // source: // http://en.cppreference.com/mwiki/index.php?title=cpp/utility/functional/invoke&oldid=82514 template static std::true_type is_refwrap_test(const std::reference_wrapper&); template static std::false_type is_refwrap_test(const U&); template struct is_reference_wrapper : decltype(is_refwrap_test(std::declval())) { }; template ::type> struct unwrap_reference_wrapper { using type = T; }; template struct unwrap_reference_wrapper> { using type = U&; }; template using unwrap_reference_wrapper_t = typename unwrap_reference_wrapper::type; // note: clang only triggers the second static_assert // - static_assert(is_invocable<&base_class::non_const_method, const derived_class&>::value, ""); // - static_assert(is_invocable<&base_class::non_const_method, const base_class&>::value, ""); // GCC triggers both. To workaround this clang bug, we have to manage cv correctness ourselves template struct is_const_member_function : std::false_type { }; // decay doesn't add pointer to abominable functions, don't bother writing them template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_volatile_member_function : std::false_type { }; // decay doesn't add pointer to abominable functions, don't bother writing them template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct has_correct_cv { // if object has no cv, every method can be called // else the method must have the same cv than the object static constexpr bool value = std::is_same::type, Object>::value || ((is_volatile_member_function::value == std::is_volatile::value) && (is_const_member_function::value == std::is_const::value)); }; // pointer to member function - reference to object template < typename Base, typename T, typename Derived, typename... Args, typename Unwrapped = unwrap_reference_wrapper_t, typename std::enable_if< is_function::value && has_correct_cv::type, T>::value && std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmf, Derived&& ref, Args&&... args) FPLUS_INVOKE_RETURN((std::forward(ref).* pmf)(std::forward(args)...)) // pointer to member function - pointer to object template < typename Base, typename T, typename Pointer, typename... Args, typename std::enable_if< is_function::value && has_correct_cv::type>::type, T>::value && !std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmf, Pointer&& ptr, Args&&... args) FPLUS_INVOKE_RETURN(((*std::forward(ptr)).* pmf)(std::forward(args)...)) // pointer to non-static data member - reference to object template < typename Base, typename T, typename Derived, typename Unwrapped = unwrap_reference_wrapper_t, typename std::enable_if< !is_function::value && std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmd, Derived&& ref) FPLUS_INVOKE_RETURN((std::forward(ref).*pmd)) // pointer to non-static data member - pointer to object template < typename Base, typename T, typename Pointer, typename std::enable_if< !is_function::value && !std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmd, Pointer&& ptr) FPLUS_INVOKE_RETURN((*std::forward(ptr)).*pmd) // normal case - functions, lambdas, function objects template ::type>::value, int>::type = 0> inline auto invoke_impl(F&& f, Args&&... args) FPLUS_INVOKE_RETURN((std::forward(f)(std::forward(args)...))) template struct invoke_result_impl { }; template struct invoke_result_impl(), std::declval()...))), F, Args...> { using type = decltype(invoke_impl(std::declval(), std::declval()...)); }; template struct invoke_result : invoke_result_impl { }; template using invoke_result_t = typename invoke_result::type; // noexcept omitted on purpose, cannot be implemented without C++17. // GCC 7.1 works with libstdc++, but clang fails, even with latest build, // on both libstdc++/libc++, I suspect an internal compiler trait is at // play to make GCC work. // // We could detect if C++17 is used and use std::invoke directly. template invoke_result_t invoke(F&& f, ArgTypes&&... args) { return invoke_impl(std::forward(f), std::forward(args)...); } // Invoke useful traits (libstdc++ 7.1.0's implementation, ugly-case removed) template struct is_invocable_impl : std::false_type { }; template struct is_invocable_impl> : disjunction, std::is_convertible>::type { }; template struct is_invocable : is_invocable_impl, void>::type { }; template struct is_invocable_r : is_invocable_impl, ReturnType>::type { }; } } #undef FPLUS_INVOKE_RETURN namespace fplus { namespace internal { // C++17 std::apply (http://en.cppreference.com/w/cpp/utility/apply) template constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence) { return internal::invoke(std::forward(f), std::get(std::forward(t))...); } template constexpr decltype(auto) apply(F&& f, Tuple&& t) { return internal::apply_impl( std::forward(f), std::forward(t), std::make_index_sequence< std::tuple_size>::value>{}); } } } // // internal/asserts/functions.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // internal/function_traits_asserts.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) namespace fplus { namespace internal { template struct function_traits_asserts; template < typename, typename F, typename... Args, typename std::enable_if::value, int>::type = 0> constexpr void trigger_static_asserts() { } // Marks a variable as unused. Prevents the compiler warning // for set but unused variables. template inline void unused(T&&) { } template ::value && !is_invocable::value, int>::type = 0> constexpr void trigger_static_asserts() { // don't perform checks if function_traits doesn't exist unused(function_traits_asserts{}); } template ::value && !is_invocable::value, int>::type = 0> constexpr void trigger_static_asserts() { static_assert(sizeof(F) == 0, "F is not a Callable, or its definition is ill-formed"); } } } namespace fplus { namespace internal { struct nullary_function_tag { }; struct unary_function_tag { }; struct binary_function_tag { }; struct binary_predicate_tag { }; struct check_arity_tag { }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 0, "Function must take no parameters."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 1, "Function must take one parameter."); typedef typename utils::function_traits::template arg<0>::type FIn0; static_assert(std::is_convertible::value, "Invalid argument type for function"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; static_assert(std::is_convertible::value, "Invalid first argument type for function"); typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Invalid second argument type for function"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_same::value, "Both parameters must have the same type."); static_assert(std::is_same>, bool>::value, "Predicate must return bool."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == sizeof...(Args), "Wrong arity."); }; } } // // internal/asserts/composition.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) namespace fplus { namespace internal { struct bind_1st_of_2_tag { }; struct bind_2nd_of_2_tag { }; struct bind_1st_of_3_tag { }; struct bind_1st_and_2nd_of_3_tag { }; struct bind_2nd_and_3rd_of_3_tag { }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function can not take bound parameter type"); static_assert(std::is_convertible::value, "Function can not take provided parameter type"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function can not take provided parameter type"); static_assert(std::is_convertible::value, "Function can not take bound parameter type"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 3, "Function must take three parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; typedef typename utils::function_traits::template arg<2>::type FIn2; static_assert(std::is_convertible::value, "Function can not take bound parameter type"); static_assert(std::is_convertible::value, "Function can not take provided first parameter type"); static_assert(std::is_convertible::value, "Function can not take provided second parameter type"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 3, "Function must take three parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; typedef typename utils::function_traits::template arg<2>::type FIn2; static_assert(std::is_convertible::value, "Function can not take first bound parameter type"); static_assert(std::is_convertible::value, "Function can not take second bound parameter type"); static_assert(std::is_convertible::value, "Function can not take provided parameter type"); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 3, "Function must take three parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; typedef typename utils::function_traits::template arg<2>::type FIn2; static_assert(std::is_convertible::value, "Function can not take provided parameter type"); static_assert(std::is_convertible::value, "Function can not take second bound parameter type"); static_assert(std::is_convertible::value, "Function can not take first bound parameter type"); }; } } // // internal/composition.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include namespace fplus { namespace internal { // source: https://codereview.stackexchange.com/a/63893 // note: the code in the link above is called with the arguments in reverse order template class compose_impl { static constexpr std::size_t size = sizeof...(Fs); static_assert(size > 1, "Invalid number of functions to compose, minimum is two."); public: compose_impl(Fs&&... fs) : _functionTuple(std::forward(fs)...) { } template auto operator()(Ts&&... ts) const { return _apply(std::integral_constant{}, std::forward(ts)...); } private: template auto _apply(std::integral_constant, Ts&&... ts) const { return _apply(std::integral_constant{}, std::get(_functionTuple)(std::forward(ts)...)); } template auto _apply(std::integral_constant, Ts&&... ts) const { return internal::invoke(std::get(_functionTuple), std::forward(ts)...); } std::tuple _functionTuple; }; // Is BinaryLift really correct? template auto compose_binary_lift_impl(std::integral_constant, const Tuple& tup, const BinaryLift& lifter) { return lifter(std::get<0>(tup), std::get<1>(tup)); } template auto compose_binary_lift_impl(std::integral_constant, const Tuple& tup, const BinaryLift& lifter) { return lifter( compose_binary_lift_impl( std::integral_constant{}, tup, lifter), std::get(tup)); } template auto compose_binary_lift(const BinaryLift& lifter, Callables&&... args) { static_assert(sizeof...(Callables) > 1, "Invalid number of functions to compose, minimum is two."); const auto tup = std::forward_as_tuple(std::forward(args)...); return compose_binary_lift_impl( std::integral_constant{}, tup, lifter); } // concentrate asserts in this method. Lambda is provided by the library. template auto logical_binary_op(Lambda op, F f, G g) { // Perfect-forwarding might move twice, if we add a requirement on F and G, // that might not be an issue. return [op, f, g](auto x) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); using FRes = std::decay_t>; using GRes = std::decay_t>; static_assert(std::is_same::value, "Must return bool."); static_assert(std::is_same::value, "Must return bool."); return op(f, g, x); }; } } } #include #include #include #include #include #include namespace fplus { // API search type: bind_1st_of_2 : (((a, b) -> c), a) -> (b -> c) // Bind first parameter of binary function. template auto bind_1st_of_2(F f, T x) { return [f, x](auto&& y) { internal::trigger_static_asserts(); return internal::invoke(f, x, std::forward(y)); }; } // API search type: bind_2nd_of_2 : (((a, b) -> c), b) -> (a -> c) // Bind second parameter of binary function. template auto bind_2nd_of_2(F f, T y) { return [f, y](auto&& x) { internal::trigger_static_asserts(); return internal::invoke(f, std::forward(x), y); }; } // API search type: bind_1st_of_3 : (((a, b, c) -> d), a) -> ((b, c) -> d) // Bind first parameter of ternary function. template auto bind_1st_of_3(F f, X x) { return [f, x](auto&& y, auto&& z) { internal::trigger_static_asserts(); return internal::invoke( f, x, std::forward(y), std::forward(z)); }; } // API search type: bind_1st_and_2nd_of_3 : (((a, b, c) -> d), a, b) -> (c -> d) // Bind first and second parameter of ternary function. template auto bind_1st_and_2nd_of_3(F f, X x, Y y) { return [f, x, y](auto&& z) { internal::trigger_static_asserts(); return internal::invoke(f, x, y, std::forward(z)); }; } // API search type: bind_2nd_and_3rd_of_3 : (((a, b, c) -> d), b, c) -> (a -> d) // Bind first and second parameter of ternary function. template auto bind_2nd_and_3rd_of_3(F f, Y y, Z z) { return [f, y, z](auto&& x) { internal::trigger_static_asserts(); return internal::invoke(f, std::forward(x), y, z); }; } // API search type: flip : (a -> b) -> (b -> a) // Flips the arguments of a binary function // Note: The callable can take a variadic number of arguments template auto flip(F f) { return [f](auto&&... args) { return internal::apply_impl( f, std::forward_as_tuple(std::forward(args)...), internal::make_reverse_index_sequence{}); }; } // API search type: forward_apply : (a, (a -> b)) -> b // Forward application. // Returns the result of applying the function f to the value x. template auto forward_apply(X&& x, F f) { internal::trigger_static_asserts(); return internal::invoke(f, std::forward(x)); } // API search type: lazy : ((a -> b), a) -> (() -> b) // Lazy evaluation. // Returns a function evaluating f with the given arguments when called. // Also known as defer. // Note: f can take a variadic number of parameters template auto lazy(F f, Args ... args) { return [f, args...] { internal::trigger_static_asserts(); return internal::invoke(f, args...); }; } // API search type: fixed : a -> (() -> a) // Identity as a nullary function. // Returns a function returning x when called. // Like lazy with identity as f. template auto fixed(T x) { return [x]() -> T { return x; }; } // API search type: compose : ((a -> b), (b -> c)) -> (a -> c) // Forward function composition. // compose(f, g)(x) = g(f(x)) // It is possible to compose a variadic number of callables. // The first callable can also take a variadic number of parameters. // compose(f, g, h)(x, y, z) = h(g(f(x, y, z))) template auto compose(Fs&&... fs) { return internal::compose_impl(std::forward(fs)...); } // API search type: logical_not : (a -> Bool) -> (a -> Bool) // Converts a predicate p into a new one, // always returning the exact opposite of p. // logical_not(f) = \x -> !x // Note: F can take a variadic number of parameters. // Equivalent to std::not_fn (C++17) template auto logical_not(Predicate f) { return [f](auto&&... args) { internal::trigger_static_asserts(); using Res = std::decay_t>; static_assert(std::is_same::value, "Function must return bool."); return !internal::invoke(f, std::forward(args)...); }; } // API search type: logical_or : ((a -> Bool), (a -> Bool)) -> (a -> Bool) // logical_or(f, g) = \x -> f(x) or g(x) // Combines to unary predicates into a single one // that holds true if at least one of the original predicated is true. template auto logical_or(UnaryPredicateF f, UnaryPredicateG g) { auto op = [](auto f1, auto f2, auto x) { return internal::invoke(f1, x) || internal::invoke(f2, x); }; return internal::logical_binary_op(op, f, g); } // API search type: logical_and : ((a -> Bool), (a -> Bool)) -> (a -> Bool) // logical_and(f, g) = \x -> f(x) and g(x) // Combines to unary predicates into a single one // that holds true if both original predicated are true. template auto logical_and(UnaryPredicateF f, UnaryPredicateG g) { auto op = [](auto f1, auto f2, auto x) { return internal::invoke(f1, x) && internal::invoke(f2, x); }; return internal::logical_binary_op(op, f, g); } // API search type: logical_xor : ((a -> Bool), (a -> Bool)) -> (a -> Bool) // logical_xor(f, g) = \x -> f(x) xor g(x) // Combines to unary predicates into a single one // that holds true if exactly one of the original predicated is true. template auto logical_xor(UnaryPredicateF f, UnaryPredicateG g) { auto op = [](auto f1, auto f2, auto x) { return internal::invoke(f1, x) != internal::invoke(f2, x); }; return internal::logical_binary_op(op, f, g); } // API search type: memoize : (a -> b) -> (a -> b) // Provides Memoization for a given (referentially transparent) // unary function. // Returns a closure mutating an internally held dictionary // mapping input values to output values. template ::template arg<0>::type, typename FOut = typename std::result_of::type, typename MemoMap = std::unordered_map< typename std::remove_reference::type>::type, FOut>> std::function memoize(F f) { static_assert(utils::function_traits::arity == 1, "Wrong arity."); MemoMap storage; return [=](FIn x) mutable -> FOut { const auto it = storage.find(x); if (it == storage.end()) { return storage.emplace(x, internal::invoke(f, x)).first->second; } else { return it->second; } }; } namespace internal { template ::template arg<0>::type, typename FIn2 = typename utils::function_traits::template arg<1>::type, typename FOut = typename std::result_of::type, typename ResultF = std::function> ResultF memoize_recursive_helper(const F f, std::shared_ptr storage) { return [f, storage](FIn2 x) { const auto it = storage->find(x); if (it == storage->end()) { const auto g = memoize_recursive_helper(f, storage); (*storage)[x] = f(g, x); } return (*storage)[x]; }; } } // namespace internal // API search type: memoize_recursive : (a -> b) -> (a -> b) // Provides Memoization for a given (referentially transparent) // recursive binary function that takes a continuation as first argument. // e.g. // uint64_t fibo_cont(const std::function& cont, uint64_t n) // { // if (n < 2) return n; // else return cont(n-1) + cont(n-2); // } // Returns a closure mutating an internally held dictionary // mapping input values to output values. template ::template arg<0>::type, typename FIn2 = typename utils::function_traits::template arg<1>::type, typename FOut = typename std::result_of::type, typename MemoMap = std::unordered_map< typename std::remove_reference::type>::type, FOut>> std::function memoize_recursive(F f) { std::shared_ptr storage = std::make_shared(); return internal::memoize_recursive_helper(f, storage); } // API search type: memoize_binary : ((a, b) -> c) -> ((a, b) -> c) // Provides Memoization for a given (referentially transparent) // binary function. // Returns a closure mutating an internally held dictionary // mapping input values to output values. template ::template arg<0>::type, typename FIn2 = typename utils::function_traits::template arg<1>::type, typename FOut = typename std::result_of::type, typename ParamPair = std::pair< typename std::remove_reference::type>::type, typename std::remove_reference::type>::type>, typename MemoMap = std::unordered_map> std::function memoize_binary(F f) { const auto unary_f = [f](const ParamPair& params) -> FOut { return internal::invoke(f, params.first, params.second); }; auto unary_f_memoized = memoize>(unary_f); return [unary_f_memoized](FIn1 a, FIn2 b) mutable -> FOut { return unary_f_memoized(std::make_pair(a, b)); }; } // API search type: constructor_as_function : a -> b // struct foo // { // foo(int a, int b) : a_(a), b_(2*b) {} // int a_; // int b_; // }; // const auto create_foo = constructor_as_function; // create_foo(1,2) == foo(1, 2); template T constructor_as_function(Types ... args) { return T(args...); } } // namespace fplus #define fplus_get_mem(fplus_get_mem_name) \ [](const auto& fplus_get_mem_x) \ { \ return fplus_get_mem_x.fplus_get_mem_name; \ } #define fplus_get_ptr_mem(fplus_get_ptr_mem_name) \ [](const auto& fplus_get_ptr_mem_x) \ { \ return fplus_get_ptr_mem_x->fplus_get_ptr_mem_name; \ } #define fplus_get_c_mem_t(fplus_get_c_mem_t_c, fplus_get_c_mem_t_name, fplus_get_c_mem_t_t) \ [](const fplus_get_c_mem_t_c& fplus_get_c_mem_t_x) -> fplus_get_c_mem_t_t \ { \ return fplus_get_c_mem_t_x.fplus_get_c_mem_t_name; \ } #define fplus_get_c_ptr_mem_t(fplus_get_c_ptr_mem_t_c, fplus_get_c_ptr_mem_t_name, fplus_get_c_ptr_mem_t_t) \ [](const fplus_get_c_ptr_mem_t_c& fplus_get_c_ptr_mem_t_x) -> fplus_get_c_ptr_mem_t_t \ { \ return fplus_get_c_ptr_mem_t_x->fplus_get_c_ptr_mem_t_name; \ } #define fplus_mem_fn(fplus_mem_fn_name) \ [](const auto& fplus_mem_fn_x) \ { \ return fplus_mem_fn_x.fplus_mem_fn_name(); \ } #define fplus_ptr_mem_fn(fplus_ptr_mem_fn_name) \ [](const auto& fplus_ptr_mem_fn_x) \ { \ return fplus_ptr_mem_fn_x->fplus_ptr_mem_fn_name(); \ } #define fplus_c_mem_fn_t(fplus_c_mem_fn_t_c, fplus_c_mem_fn_t_name, fplus_c_mem_fn_t_t) \ [](const fplus_c_mem_fn_t_c& fplus_c_mem_fn_t_x) -> fplus_c_mem_fn_t_t \ { \ return fplus_c_mem_fn_t_x.fplus_c_mem_fn_t_name(); \ } #define fplus_c_ptr_mem_fn_t(fplus_c_ptr_mem_fn_t_c, fplus_c_ptr_mem_fn_t_name, fplus_c_ptr_mem_fn_t_t) \ [](const fplus_c_ptr_mem_fn_t_c& fplus_c_ptr_mem_fn_t_x) -> fplus_c_ptr_mem_fn_t_t \ { \ return fplus_c_ptr_mem_fn_t_x->fplus_c_ptr_mem_fn_t_name(); \ } // // internal/compare.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include namespace fplus { namespace internal { template auto ord_to_impl(Compare comp) { return [comp](auto x, auto y) { static_assert(std::is_same::value, "Argument types must be the same"); using In = decltype(x); internal::trigger_static_asserts(); using CompareOut = std::decay_t>; static_assert(std::is_same::value, "Function must return bool."); return std::make_pair(internal::invoke(comp, x, y), internal::invoke(comp, y, x)); }; } } } namespace fplus { namespace internal { template void check_unary_predicate_for_type() { internal::trigger_static_asserts(); static_assert(std::is_convertible< internal::invoke_result_t, bool>::value, "Predicate must return bool."); } template void check_compare_preprocessors_for_types() { internal::trigger_static_asserts(); internal::trigger_static_asserts(); static_assert(std::is_same< std::decay_t>, std::decay_t>>::value, "Both functions must return the same type."); } } // namespace internal // API search type: identity : a -> a // fwd bind count: 0 // identity(x) == x template T identity(const T& x) { return x; } // API search type: is_equal : (a, a) -> Bool // fwd bind count: 1 // x == y // Equality check. template bool is_equal(const T& x, const T& y) { return x == y; } // API search type: always : a -> (b -> a) // always(x)(y) == x template auto always(const X& x) { return [x](const auto&) { return x; }; } // API search type: always_arg_1_of_2 : (a, b) -> a // always_arg_1_of_2(x, y) == x template X always_arg_1_of_2(const X& x, const Y&) { return x; } // API search type: always_arg_2_of_2 : (a, b) -> a // always_arg_2_of_2(x, y) == x template Y always_arg_2_of_2(const X&, const Y& y) { return y; } // API search type: is_equal_by_and_by : ((a -> b), (c -> b)) -> ((a, c) -> Bool) // f(x) == g(y) // Provides an equality check of two values // after applying a transformation function each. template auto is_equal_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); return is_equal(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_equal_by : (a -> b) -> (a -> Bool) // f(x) == f(y) // Provides an equality check of two values // after applying the same transformation function to both. template auto is_equal_by(F f) { return is_equal_by_and_by(f, f); } // API search type: is_equal_by_to : ((b -> a), a) -> (b -> Bool) // f(y) == x // Provides an equality check to a fixed value // after applying a transformation function. template auto is_equal_by_to(F f, const X& x) { return [f, x](const auto& y) { internal::trigger_static_asserts(); return is_equal(internal::invoke(f, y), x); }; } // API search type: is_equal_to : a -> (a -> Bool) // x == y // curried version of is_equal // Provides an equality check with a fixed value. template auto is_equal_to(const X& x) { return is_equal_by_to(identity, x); } // API search type: is_not_equal : (a, a) -> Bool // fwd bind count: 1 // x != y // Unequally check. template bool is_not_equal(const T& x, const T& y) { return x != y; } // API search type: is_not_equal_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) != g(y) // Provides an unequality check of two values // after applying a transformation function eac template auto is_not_equal_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); using FOut = std::decay_t>; using GOut = std::decay_t>; static_assert(std::is_same::value, "Functions must return the same type."); return is_not_equal(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_not_equal_by : (a -> b) -> ((a, a) -> Bool) // f(x) != f(y) // Provides an unequality check of two values // after applying the same transformation function to both. template auto is_not_equal_by(F f) { return is_not_equal_by_and_by(f, f); } // API search type: is_not_equal_by_to : ((a -> b), b) -> (a -> Bool) // f(y) != x // Provides an unequality check to a fixed value // after applying a transformation function. template auto is_not_equal_by_to(F f, const X& x) { return [f, x](const auto& y) { internal::trigger_static_asserts(); return is_not_equal(internal::invoke(f, y), x); }; } // API search type: is_not_equal_to : a -> (a -> Bool) // y != x // curried version of is_not_equal // Provides an unequality check with a fixed value. template auto is_not_equal_to(const X& x) { return is_not_equal_by_to(identity, x); } // API search type: is_less : (a, a) -> Bool // fwd bind count: 1 // x < y // Less check. template bool is_less(const T& x, const T& y) { return x < y; } // API search type: is_less_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) < g(y) // Provides a less check of two values // after applying a transformation function each. template auto is_less_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); using FOut = std::decay_t>; using GOut = std::decay_t>; static_assert(std::is_same::value, "Functions must return the same type."); return is_less(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_less_by : (a -> b) -> ((a, a) -> Bool) // f(x) < f(y) // Provides a less check of two values // after applying the same transformation function to both. template auto is_less_by(F f) { return is_less_by_and_by(f, f); } // API search type: is_less_by_than : ((a -> b), b) -> (a -> Bool) // f(y) < x // Provides a less check to a fixed value // after applying a transformation function. template auto is_less_by_than(F f, const X& x) { return [f, x](const auto& y) { internal::trigger_static_asserts(); return is_less(internal::invoke(f, y), x); }; } // API search type: is_less_than : a -> (a -> Bool) // y < x // curried version of is_less // Provides a less check with a fixed value. template auto is_less_than(const X& x) { return is_less_by_than(identity, x); } // API search type: is_less_or_equal : (a, a) -> Bool // fwd bind count: 1 // x <= y // Less-or-equal check. template bool is_less_or_equal(const T& x, const T& y) { return x <= y; } // API search type: is_less_or_equal_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) <= g(y) // Provides a less-or-equal check of two values // after applying a transformation function each. template auto is_less_or_equal_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { using FIn = decltype(x); using GIn = decltype(y); internal::check_compare_preprocessors_for_types(); return is_less_or_equal(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_less_or_equal_by : (a -> b) -> ((a, a) -> Bool) // f(x) <= f(y) // Provides a less-or-equal check of two values // after applying the same transformation function to both. template auto is_less_or_equal_by(F f) { return is_less_or_equal_by_and_by(f, f); } // API search type: is_less_or_equal_by_than : ((a -> b), b) -> (a -> Bool) // f(y) <= x // Provides a less-or-equal check to a fixed value // after applying a transformation function. template auto is_less_or_equal_by_than(F f, const X& x) { return [f, x](const auto& y) { internal:: trigger_static_asserts(); return is_less_or_equal(internal::invoke(f, y), x); }; } // API search type: is_less_or_equal_than : a -> (a -> Bool) // y <= x // curried version of is_less_or_equal // Provides a less-or-equal check with a fixed value template auto is_less_or_equal_than(const X& x) { return is_less_or_equal_by_than(identity, x); } // API search type: is_greater : a -> a -> Bool // fwd bind count: 1 // x > y // Greater check. template bool is_greater(const T& x, const T& y) { return x > y; } // API search type: is_greater_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) > g(y) // Provides a greater check of two values // after applying a transformation function each. template auto is_greater_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { using FIn = decltype(x); using GIn = decltype(y); internal::check_compare_preprocessors_for_types(); return is_greater(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_greater_by : (a -> b) -> ((a, a) -> Bool) // f(x) > f(y) // Provides a greater check of two values // after applying the same transformation function to both. template auto is_greater_by(F f) { return is_greater_by_and_by(f, f); } // API search type: is_greater_by_than : ((a -> b), b) -> (a -> Bool) // f(y) > x // Provides a greater check to a fixed value // after applying a transformation function. template auto is_greater_by_than(F f, const X& x) { return [f, x](const auto& y) { return is_greater(internal::invoke(f, y), x); }; } // API search type: is_greater_than : a -> (a -> Bool) // y > x // curried version of is_greater // Provides a greater check with a fixed value. template auto is_greater_than(const X& x) { return is_greater_by_than(identity, x); } // API search type: is_greater_or_equal : (a, a) -> Bool // fwd bind count: 1 // x >= y // Greater-or-equal check. template bool is_greater_or_equal(const T& x, const T& y) { return x >= y; } // API search type: is_greater_or_equal_by_and_by : ((a -> c), (b -> c)) -> ((a, b) -> Bool) // f(x) >= g(y) // Provides a greater-or-equal check of two values // after applying a transformation function each. template auto is_greater_or_equal_by_and_by(F f, G g) { return [f, g](const auto& x, const auto& y) { using FIn = decltype(x); using GIn = decltype(y); internal::check_compare_preprocessors_for_types(); return is_greater_or_equal(internal::invoke(f, x), internal::invoke(g, y)); }; } // API search type: is_greater_or_equal_by : (a -> b) -> ((a, a) -> Bool) // f(x) >= f(y) // Provides a greater-or-equal check of two values // after applying the same transformation function to both. template auto is_greater_or_equal_by(F f) { return is_greater_or_equal_by_and_by(f, f); } // API search type: is_greater_or_equal_by_than : ((a -> b), b) -> (a -> Bool) // f(y) >= x // Provides a greater-or-equal check to a fixed value // after applying a transformation function. template auto is_greater_or_equal_by_than(F f, const X& x) { return [f, x](const auto& y) { internal::trigger_static_asserts(); return is_greater_or_equal(internal::invoke(f, y), x); }; } // API search type: is_greater_or_equal_than : a -> (a -> Bool) // y >= x // curried version of is_less_or_equal // Provides a greater-or-equal check with a fixed valu template auto is_greater_or_equal_than(const X& x) { return is_greater_or_equal_by_than(identity, x); } // API search type: xor_bools : (Bool, Bool) -> Bool // fwd bind count: 1 // Exclusive or. template bool xor_bools(const T& x, const T& y) { static_assert(std::is_convertible::value, "Type must be convertible to bool."); return (x && !y) || (!x && y); } // API search type: ord_to_eq : ((a, a) -> Bool) -> ((a, a) -> Bool) // ord_to_eq((<)) == (==) // Takes a less-than function and converts it // into an equality check function // which considers two values as equal if none are lesser than the other one. template auto ord_to_eq(Compare comp) { return [comp](auto x, auto y) { static_assert(std::is_same::value, "Argument types must be the same"); auto p = internal::ord_to_impl(comp)(x, y); return !p.first && !p.second; }; } // API search type: ord_to_not_eq : ((a, a) -> Bool) -> ((a, a) -> Bool) // ord_to_not_eq((<)) == (!=) // Takes a less-than function and converts it // into an inequality check function // which considers to values as unequal if one is less than the other one. template auto ord_to_not_eq(Compare comp) { return logical_not(ord_to_eq(comp)); } // API search type: ord_eq_to_eq : ((a, a) -> Bool) -> ((a, a) -> Bool) // ord_eq_to_eq((<=)) == (==) // ord_to_eq((<)) == (==) // Takes a less-or-equal-than function and converts it // into an equality check function // which considers to values as equal if a <= b and b <= a. template auto ord_eq_to_eq(Compare comp) { return [comp](auto x, auto y) { static_assert(std::is_same::value, "Argument types must be the same"); auto p = internal::ord_to_impl(comp)(x, y); return p.first && p.second; }; } // API search type: ord_eq_to_not_eq : ((a, a) -> Bool) -> ((a, a) -> Bool) // ord_eq_to_not_eq((<=)) == (!=) // Takes a less-or-equal-than function and converts it // into an inequality check function // which considers to values as equal if not a <= b and not b <= a. template auto ord_eq_to_not_eq(Compare comp) { return logical_not(ord_eq_to_eq(comp)); } } // namespace fplus // // container_common.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // container_traits.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fplus { namespace internal { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #endif template struct has_order : public std::false_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::false_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::true_type {}; template struct has_order> : public std::false_type {}; template struct has_order> : public std::true_type {}; // http://stackoverflow.com/a/33828321/1866775 template::lowest()> struct same_cont_new_t : public std::false_type{}; template struct same_cont_new_t, NewT, SizeOffset> { static_assert(SizeOffset != std::numeric_limits::lowest(), "Size of std::array must be known at compile-time."); typedef typename std::array(static_cast(N) + SizeOffset)> type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::vector type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::deque type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::forward_list type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::list type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::set type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::stack type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::queue type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::priority_queue type; }; template struct same_cont_new_t, NewT, SizeOffset> { typedef typename std::basic_string type; }; template struct SameMapTypeNewTypes : public std::false_type {}; template struct SameMapTypeNewTypes, NewKey, NewVal> { typedef typename std::map type; }; template struct SameMapTypeNewTypes, NewKey, NewVal> { typedef typename std::unordered_map type; }; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif template< typename ContIn, typename F, int SizeOffset = std::numeric_limits::lowest(), typename T = typename ContIn::value_type, typename ContOut = typename same_cont_new_t>, SizeOffset>::type> struct same_cont_new_t_from_unary_f { typedef ContOut type; }; template< typename ContIn, typename F, typename T1, typename T2, int SizeOffset = std::numeric_limits::lowest(), typename ContOut = typename same_cont_new_t>, SizeOffset>::type> struct same_cont_new_t_from_binary_f { typedef ContOut type; }; // https://stackoverflow.com/a/44549820/1866775 template struct can_self_assign { using type = std::is_assignable; }; template using can_self_assign_t = typename can_self_assign::type; template struct can_self_assign> { enum { t0 = can_self_assign_t::value, t1 = can_self_assign_t::value, x = t0&&t1 }; using type = std::integral_constant; }; template<> struct can_self_assign> { using type = std::integral_constant; }; template struct can_self_assign> { using type = std::integral_constant::value && can_self_assign_t>::value >; }; template struct reuse_container_bool_t { }; using create_new_container_t = reuse_container_bool_t; using reuse_container_t = reuse_container_bool_t; template struct can_reuse { using dContainer = typename std::decay::type; using can_assign = can_self_assign_t; using cannot_reuse = std::is_lvalue_reference; using value = reuse_container_bool_t; }; template using can_reuse_v = typename can_reuse::value; template struct remove_const_and_ref { using type = typename std::remove_const::type>::type; }; template using remove_const_and_ref_t = typename remove_const_and_ref::type; } // namespace internal } // namespace fplus // // maybe.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include namespace fplus { // Can hold a value of type T or nothing. template class maybe { public: bool is_just() const { return is_present_; } bool is_nothing() const { return !is_just(); } const T& unsafe_get_just() const { assert(is_just()); return *reinterpret_cast(&value_); } T& unsafe_get_just() { assert(is_just()); return *reinterpret_cast(&value_); } typedef T type; maybe() : is_present_(false), value_() {}; ~maybe() { destruct_content(); } maybe(const T& val_just) : is_present_(true), value_() { new (&value_) T(val_just); } maybe(T&& val_just) : is_present_(true), value_() { new (&value_) T(std::move(val_just)); } maybe(const maybe& other) : is_present_(other.is_just()), value_() { if (is_present_) { new (&value_) T(other.unsafe_get_just()); } } maybe(maybe&& other) : is_present_(std::move(other.is_present_)), value_() { if (is_present_) { new (&value_) T(std::move(other.unsafe_get_just())); } } maybe& operator = (const T& other) { destruct_content(); is_present_ = true; new (&value_) T(other); return *this; } maybe& operator = (T&& other) { destruct_content(); is_present_ = true; new (&value_) T(std::move(other)); return *this; } maybe& operator = (const maybe& other) { destruct_content(); if (other.is_just()) { is_present_ = true; new (&value_) T(other.unsafe_get_just()); } return *this; } maybe& operator = (maybe&& other) { destruct_content(); is_present_ = std::move(other.is_present_); if (is_present_) { new (&value_) T(std::move(other.unsafe_get_just())); } return *this; } private: void destruct_content() { if (is_present_) { is_present_ = false; (*reinterpret_cast(&value_)).~T(); } } bool is_present_; typename std::aligned_storage::type value_; }; namespace internal { template struct is_maybe : std::false_type { }; template struct is_maybe> : std::true_type { }; } // API search type: is_just : Maybe a -> Bool // fwd bind count: 0 // Is not nothing? template bool is_just(const maybe& maybe) { return maybe.is_just(); } // API search type: is_nothing : Maybe a -> Bool // fwd bind count: 0 // Has no value? template bool is_nothing(const maybe& maybe) { return !is_just(maybe); } // API search type: unsafe_get_just : Maybe a -> a // fwd bind count: 0 // Crashes if maybe is nothing! template T unsafe_get_just(const maybe& maybe) { return maybe.unsafe_get_just(); } // API search type: just_with_default : (a, Maybe a) -> a // fwd bind count: 0 // Get the value from a maybe or the default in case it is nothing. template T just_with_default(const T& defaultValue, const maybe& maybe) { if (is_just(maybe)) return unsafe_get_just(maybe); return defaultValue; } // API search type: throw_on_nothing : (e, Maybe a) -> a // fwd bind count: 1 // Throw exception if nothing. Return value if just. template T throw_on_nothing(const E& e, const maybe& maybe) { if (is_nothing(maybe)) throw e; return unsafe_get_just(maybe); } // API search type: just : a -> Maybe a // fwd bind count: 0 // Wrap a value in a Maybe as a Just. template maybe just(const T& val) { return val; } // API search type: as_just_if : ((a -> bool), a) -> Maybe a // fwd bind count: 1 // Wrap a value in a Maybe as a Just if the given predicate is fulfilled. // Otherwise a nothing is returned. template maybe as_just_if(Pred pred, const T& val) { internal::check_unary_predicate_for_type(); if (pred(val)) return val; else return {}; } // API search type: maybe_to_seq : Maybe a -> [a] // fwd bind count: 0 // Converts a maybe to a sequence. // singleton_seq(Just 3) == [3] // singleton_seq(Nothing) == [] template > ContainerOut maybe_to_seq(const maybe& maybe) { if (is_just(maybe)) return ContainerOut(1, unsafe_get_just(maybe)); return {}; } // API search type: singleton_seq_as_maybe : [a] -> Maybe a // fwd bind count: 0 // Converts a sequence to a maybe. // singleton_seq([]) == Nothing // singleton_seq([3]) == Just 3 // singleton_seq([3,4]) == Nothing template maybe singleton_seq_as_maybe(const Container& xs) { if (xs.size() == 1) return xs.front(); return {}; } // API search type: nothing : () -> Maybe a // Construct a nothing of a certain Maybe type. template maybe nothing() { return {}; } // True if just values are the same or if both are nothing. template bool operator == (const maybe& x, const maybe& y) { if (is_just(x) && is_just(y)) return unsafe_get_just(x) == unsafe_get_just(y); return is_just(x) == is_just(y); } // False if just values are the same or if both are nothing. template bool operator != (const maybe& x, const maybe& y) { return !(x == y); } // API search type: lift_maybe : ((a -> b), Maybe a) -> Maybe b // fwd bind count: 1 // Lifts a function into the maybe functor. // A function that for example was able to convert and int into a string, // now can convert a Maybe into a Maybe. // A nothing remains a nothing, regardless of the conversion. template auto lift_maybe(F f, const maybe& m) { internal::trigger_static_asserts(); using B = std::decay_t>; if (is_just(m)) return just(internal::invoke(f, unsafe_get_just(m))); return nothing(); } // API search type: lift_maybe_def : (b, (a -> b), Maybe a) -> b // fwd bind count: 2 // lift_maybe_def takes a default value and a function. // It returns a function taking a Maybe value. // This function returns the default value if the Maybe value is nothing. // Otherwise it applies the function to the value inside the Just // of the Maybe value and returns the result of this application. template auto lift_maybe_def(const Default& def, F f, const maybe& m) { internal::trigger_static_asserts(); using B = std::decay_t>; static_assert( std::is_convertible::value, "Default value must be convertible to Function's return type"); if (is_just(m)) return B(internal::invoke(f, unsafe_get_just(m))); return B(def); } // API search type: lift_maybe_2 : (((a, b) -> c), Maybe a, Maybe b) -> Maybe c // fwd bind count: 2 // Lifts a binary function into the maybe functor. // Applies the function only if both arguments are justs. // Otherwise returns a nothing. template auto lift_maybe_2(F f, const maybe& m_a, const maybe& m_b) { internal::trigger_static_asserts(); using FOut = std::decay_t>; if (is_just(m_a) && is_just(m_b)) { return just( internal::invoke(f, unsafe_get_just(m_a), unsafe_get_just(m_b))); } return nothing(); } // API search type: lift_maybe_2_def : (c, ((a, b) -> c), Maybe a, Maybe b) -> c // fwd bind count: 3 // lift_maybe_2_def takes a default value and a binary function. // It returns a function taking a two Maybe values. // This function returns the default value at least one of the // Maybe values is nothing. // Otherwise it applies the function to the two values inside the Justs // and returns the result of this application. template auto lift_maybe_2_def(const Default& def, F f, const maybe& m_a, const maybe& m_b) { internal::trigger_static_asserts(); using C = std::decay_t>; static_assert( std::is_convertible::value, "Default value must be convertible to Function's return type"); if (is_just(m_a) && is_just(m_b)) return C(internal::invoke(f, unsafe_get_just(m_a), unsafe_get_just(m_b))); return C(def); } // API search type: join_maybe : Maybe Maybe a -> Maybe a // Flattens a nested maybe. // join_maybe(Just Just x) == Just x // join_maybe(Just Nothing) == Nothing // join_maybe(Nothing) == Nothing template maybe join_maybe(const maybe>& m) { if (is_just(m)) return unsafe_get_just(m); else return nothing(); } // API search type: and_then_maybe : ((a -> Maybe b), (Maybe a)) -> Maybe b // fwd bind count: 1 // Monadic bind. // Returns nothing if the maybe already is nothing. // Otherwise return the result of applying // the function to the just value of the maybe. template auto and_then_maybe(F f, const maybe& m) { internal::trigger_static_asserts(); using FOut = std::decay_t>; static_assert(internal::is_maybe::value, "Function must return a maybe<> type"); if (is_just(m)) return internal::invoke(f, unsafe_get_just(m)); else return nothing(); } // API search type: compose_maybe : ((a -> Maybe b), (b -> Maybe c)) -> (a -> Maybe c) // Left-to-right Kleisli composition of monads. // Composes multiple callables taking a value and returning Maybe. // If the first callable returns a just, the value from the just // is extracted and shoved into the next callable. // If the first callable returns a nothing, it remains a nothing. // The first callable can take a variadic number of parameters. template auto compose_maybe(Callables&&... callables) { auto bind_maybe = [](auto f, auto g) { // next step would be to perfectly forward callables, as shown here: // https://vittorioromeo.info/index/blog/capturing_perfectly_forwarded_objects_in_lambdas.html return [f = std::move(f), g = std::move(g)](auto&&... args) { using FOut = std::decay_t< internal::invoke_result_t>; static_assert(internal::is_maybe::value, "Functions must return a maybe<> type"); using GOut = std::decay_t< internal::invoke_result_t>; static_assert(internal::is_maybe::value, "Functions must return a maybe<> type"); auto maybeB = internal::invoke(f, std::forward(args)...); if (is_just(maybeB)) return internal::invoke(g, unsafe_get_just(maybeB)); return GOut{}; }; }; return internal::compose_binary_lift(bind_maybe, std::forward(callables)...); } // API search type: flatten_maybe : (Maybe (Maybe a)) -> Maybe a // fwd bind count: 0 // Also known as join. template maybe flatten_maybe(const maybe>& maybe_maybe) { if (is_nothing(maybe_maybe)) return nothing(); return unsafe_get_just(maybe_maybe); } } // namespace fplus // // internal/container_common.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include namespace fplus { namespace internal { template T accumulate(InputIt first, InputIt last, T init) { for (; first != last; ++first) { init = std::move(init) + *first; } return init; } template T accumulate(InputIt first, InputIt last, T init, BinaryOperation op) { for (; first != last; ++first) { init = op(std::move(init), *first); } return init; } template void scan_impl(F f, const Acc& init, OutputIterator itOut, InputIterator begin, InputIterator end) { *itOut = init; auto g = [itOut, f](auto acc, auto x) mutable { acc = internal::invoke(f, acc, x); *itOut = acc; return acc; }; internal::accumulate(begin, end, init, g); } } } #include #include #include #include #include #include namespace fplus { namespace internal { template void check_unary_predicate_for_container() { internal::check_unary_predicate_for_type(); } template void check_index_with_type_predicate_for_container() { typedef typename Container::value_type T; internal::trigger_static_asserts(); static_assert(std::is_convertible< internal::invoke_result_t, bool>::value, "Function must return bool."); } template void check_compare_for_container() { typedef typename Container::value_type T; internal::trigger_static_asserts(); } template void check_binary_predicate_for_container() { typedef typename Container::value_type T; internal::trigger_static_asserts(); } // PrepareContainer and BackInserter are overloaded // to increase performance on std::vector and std::string // by using std::vector::reserve // and std::back_inserter instead of std::back_inserter. // In VC2015, release mode, Celsius W520 Xeon // this leads to an increase in performance of about a factor of 3 // for transform. template void prepare_container(const std::basic_string, std::allocator>& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::vector& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::array&, std::size_t size) { assert(size == N); unused(size); } template void prepare_container(std::unordered_set& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::unordered_map& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::unordered_multiset& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(std::unordered_multimap& ys, std::size_t size) { ys.reserve(size); } template void prepare_container(Container&, std::size_t) { } template std::back_insert_iterator get_back_inserter(std::string& ys) { return std::back_inserter(ys); } template std::back_insert_iterator get_back_inserter(std::vector& ys) { return std::back_inserter(ys); } template std::back_insert_iterator get_back_inserter(std::list& ys) { return std::back_inserter(ys); } template std::back_insert_iterator get_back_inserter(std::deque& ys) { return std::back_inserter(ys); } template struct array_back_insert_iterator : public std::back_insert_iterator> { typedef std::back_insert_iterator> base_type; explicit array_back_insert_iterator(std::array& arr) : base_type(arr), arr_ptr_(&arr), pos_(0) {} array_back_insert_iterator(const array_back_insert_iterator& other) : base_type(*other.arr_ptr_), arr_ptr_(other.arr_ptr_), pos_(other.pos_) {} array_back_insert_iterator& operator=(const array_back_insert_iterator& other) { arr_ptr_ = other.arr_ptr_; pos_ = other.pos_; return *this; } ~array_back_insert_iterator() { assert(pos_ == 0 || pos_ == N); } array_back_insert_iterator& operator=(const T& x) { assert(pos_ < N); (*arr_ptr_)[pos_] = x; ++pos_; return *this; } array_back_insert_iterator& operator=(T&& x) { assert(pos_ < N); (*arr_ptr_)[pos_] = std::move(x); ++pos_; return *this; } array_back_insert_iterator& operator*() { return *this; } array_back_insert_iterator& operator++() { return *this; } array_back_insert_iterator operator++(int) { return *this; } private: std::array* arr_ptr_; std::size_t pos_; }; #if defined(_MSC_VER) && _MSC_VER >= 1900 template struct std::_Is_checked_helper> : public true_type { // mark array_back_insert_iterator as checked }; #endif template array_back_insert_iterator get_back_inserter(std::array& ys) { return array_back_insert_iterator(ys); } template std::insert_iterator get_back_inserter(Container& ys) { return std::inserter(ys, std::end(ys)); } template void advance_iterator(Iterator& it, std::size_t distance) { std::advance(it, static_cast(distance)); } template void advance_iterator(T*& it, std::size_t distance) { it += static_cast(distance); } template Iterator add_to_iterator(Iterator it, std::size_t distance = 1) { return std::next(it, static_cast(distance)); } } // namespace internal // API search type: is_empty : [a] -> Bool // fwd bind count: 0 // Returns true if the container holds no elements. // is_empty([1, 2]) == false // is_empty([]) == true template bool is_empty(const Container& xs) { return xs.empty(); } // API search type: is_not_empty : [a] -> Bool // fwd bind count: 0 // Returns true if the container holds at least one element. // is_not_empty([1, 2]) == true template bool is_not_empty(const Container& xs) { return !is_empty(xs); } // API search type: size_of_cont : [a] -> Int // fwd bind count: 0 // Returns the number of elements in the given container. // size_of_cont([3, 4]) == 2 template std::size_t size_of_cont(const Container& xs) { return xs.size(); } // API search type: convert : a -> b // fwd bind count: 0 // Converts one type of element into another. template Dest convert(const Source& x) { return Dest(x); } // API search type: convert_elems : [a] -> [b] // fwd bind count: 0 // Converts all elements in a sequence to a different type. // convert_elems([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)] template ::type> ContainerOut convert_elems(const ContainerIn& xs) { static_assert(std::is_constructible::value, "Elements not convertible."); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); // using 'for (const auto& x ...)' is even for ints as fast as // using 'for (int x ...)' (GCC, O3), so there is no need to // check if the type is fundamental and then dispatch accordingly. for (const auto& x : xs) { *it = convert(x); } return ys; } // API search type: convert_container : [a] -> [a] // fwd bind count: 0 // Change the type of the container // while keeping the elements in the sequence the same. // convert_container([1, 2, 3]) == [1, 2, 3] // Useful for example if you want to convert an std::list to an std::vector. template ContainerOut convert_container(const ContainerIn& xs) { typedef typename ContainerIn::value_type SourceElem; typedef typename ContainerOut::value_type DestElem; static_assert(std::is_same::value, "Source and dest container must have the same value_type"); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto itOut = internal::get_back_inserter(ys); std::copy(std::begin(xs), std::end(xs), itOut); return ys; } // API search type: convert_container_and_elems : [a] -> [b] // fwd bind count: 0 // Converts between different containers and elements. // Dest elements are allowed to have explicit constructors. // convert([1, 2, 3]) == [1, 2, 3] template ContainerOut convert_container_and_elems(const ContainerIn& xs) { static_assert(std::is_convertible::value, "Elements not convertible."); typedef typename ContainerOut::value_type DestElem; ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); for (const auto& x : xs) { *it = convert(x); } return ys; } namespace internal { template Container get_segment(internal::reuse_container_t, std::size_t idx_begin, std::size_t idx_end, Container&& xs) { idx_end = std::min(idx_end, size_of_cont(xs)); if (idx_end <= idx_begin) { xs.clear(); return std::forward(xs); } auto itBegin = std::begin(xs); internal::advance_iterator(itBegin, idx_begin); auto itEnd = itBegin; internal::advance_iterator(itEnd, idx_end - idx_begin); xs.erase(std::copy(itBegin, itEnd, std::begin(xs)), std::end(xs)); return std::forward(xs); } template Container get_segment(internal::create_new_container_t, std::size_t idx_begin, std::size_t idx_end, const Container& xs) { idx_end = std::min(idx_end, size_of_cont(xs)); if (idx_end <= idx_begin) { return {}; } Container result; auto itBegin = std::begin(xs); internal::advance_iterator(itBegin, idx_begin); auto itEnd = itBegin; internal::advance_iterator(itEnd, idx_end - idx_begin); std::copy(itBegin, itEnd, internal::get_back_inserter(result)); return result; } } // namespace internal // API search type: get_segment : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Return a defined segment from the sequence. // get_segment(2, 5, [0,1,2,3,4,5,6,7,8]) == [2,3,4] // get_segment(2, 15, [0,1,2,3,4,5,6,7,8]) == [2,3,4,5,6,7,8] // get_segment(5, 2, [0,1,2,3,4,5,6,7,8]) == [] // Also known as slice. template > ContainerOut get_segment (std::size_t idx_begin, std::size_t idx_end, Container&& xs) { return internal::get_segment(internal::can_reuse_v{}, idx_begin, idx_end, std::forward(xs)); } namespace internal { template Container set_segment(internal::reuse_container_t, std::size_t idx_begin, const ContainerToken& token, Container&& xs) { assert(idx_begin + size_of_cont(token) < size_of_cont(xs)); auto itBegin = std::begin(xs); internal::advance_iterator(itBegin, idx_begin); std::copy(std::begin(token), std::end(token), itBegin); return std::forward(xs); } template Container set_segment(internal::create_new_container_t, std::size_t idx_begin, const ContainerToken& token, const Container& xs) { Container result = xs; return set_segment(internal::reuse_container_t(), idx_begin, token, std::move(result)); } } // namespace internal // API search type: set_segment : (Int, [a], [a]) -> [a] // fwd bind count: 2 // Replace part of a sequence with a token. // set_segment(2, [9,9,9], [0,1,2,3,4,5,6,7,8]) == [0,1,9,9,9,5,6,7,8] // Crashes on invalid indices. // Also known as replace_segment. template > ContainerOut set_segment (std::size_t idx_begin, const ContainerToken& token, Container&& xs) { return internal::set_segment(internal::can_reuse_v{}, idx_begin, token, std::forward(xs)); } namespace internal { template Container remove_segment(internal::reuse_container_t, std::size_t idx_begin, std::size_t idx_end, Container&& xs) { assert(idx_begin <= idx_end); assert(idx_end <= size_of_cont(xs)); auto firstBreakIt = std::begin(xs); internal::advance_iterator(firstBreakIt, idx_begin); auto secondBreakIt = std::begin(xs); internal::advance_iterator(secondBreakIt, idx_end); xs.erase( std::copy(secondBreakIt, std::end(xs), firstBreakIt), std::end(xs)); return std::forward(xs); } template Container remove_segment(internal::create_new_container_t, std::size_t idx_begin, std::size_t idx_end, const Container& xs) { assert(idx_begin <= idx_end); assert(idx_end <= size_of_cont(xs)); Container result; std::size_t length = idx_end - idx_begin; internal::prepare_container(result, size_of_cont(xs) - length); auto firstBreakIt = std::begin(xs); internal::advance_iterator(firstBreakIt, idx_begin); std::copy(std::begin(xs), firstBreakIt, internal::get_back_inserter(result)); auto secondBreakIt = std::begin(xs); internal::advance_iterator(secondBreakIt, idx_end); std::copy(secondBreakIt, std::end(xs), internal::get_back_inserter(result)); return result; } } // namespace internal // API search type: remove_segment : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Cuts our a defined segment from the sequence. // remove_segment(2, 5, [0,1,2,3,4,5,6,7]) == [0,1,5,6,7] // crashes on invalid indices template > ContainerOut remove_segment( std::size_t idx_begin, std::size_t idx_end, Container&& xs) { return internal::remove_segment(internal::can_reuse_v{}, idx_begin, idx_end, std::forward(xs)); } // API search type: insert_at : (Int, [a], [a]) -> [a] // fwd bind count: 2 // Inserts a token into a sequence at a specific position. // insert_at(2, [8,9], [0,1,2,3,4]) == [0,1,8,9,2,3,4] // Unsafe! Crashes on invalid index. template Container insert_at(std::size_t idx_begin, const Container& token, const Container& xs) { assert(idx_begin <= size_of_cont(xs)); Container result; internal::prepare_container(result, size_of_cont(xs) + size_of_cont(token)); auto breakIt = std::begin(xs); internal::advance_iterator(breakIt, idx_begin); std::copy(std::begin(xs), breakIt, internal::get_back_inserter(result)); std::copy(std::begin(token), std::end(token), internal::get_back_inserter(result)); std::copy(breakIt, std::end(xs), internal::get_back_inserter(result)); return result; } // API search type: elem_at_idx : (Int, [a]) -> a // fwd bind count: 1 // Return the nth element of a sequence. // elem_at_idx(2, [7,6,5,4,3]) == 5 // Unsafe! Crashes on invalid index. template auto elem_at_idx(std::size_t idx, const Container& xs) { assert(idx < size_of_cont(xs)); auto it = std::begin(xs); internal::advance_iterator(it, idx); return *it; } // API search type: elem_at_idx_maybe : (Int, [a]) -> Maybe a // fwd bind count: 1 // Return the nth element of a sequence if existing. // elem_at_idx_maybe(2, [7,6,5,4,3]) == Just 5 // elem_at_idx_maybe(9, [7,6,5,4,3]) == Nothing // Use elem_at_idx_or_nothing if you want to provide a signed index type. template maybe elem_at_idx_maybe(std::size_t idx, const Container& xs) { if (size_of_cont(xs) < idx) { return {}; } auto it = std::begin(xs); internal::advance_iterator(it, idx); return *it; } // API search type: elems_at_idxs : ([Int], [a]) -> [a] // fwd bind count: 1 // Construct a subsequence from the elements with the given indices. // elem_at_idxs([1, 3], [7,6,5,4,3]) == [6, 4] template > std::vector elems_at_idxs(const ContainerIdxs& idxs, const Container& xs) { static_assert(std::is_same::value, "Indices must be std::size_t"); ContainerOut result; internal::prepare_container(result, size_of_cont(idxs)); auto itOut = internal::get_back_inserter(result); for (std::size_t idx : idxs) { *itOut = elem_at_idx(idx, xs); } return result; } namespace internal { template Container transform(internal::reuse_container_t, F f, Container&& xs) { internal::trigger_static_asserts(); std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); return std::forward(xs); } template ContainerOut transform(internal::create_new_container_t, F f, const ContainerIn& xs) { internal::trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); std::transform(std::begin(xs), std::end(xs), it, f); return ys; } } // namespace internal // API search type: transform : ((a -> b), [a]) -> [b] // fwd bind count: 1 // Apply a function to every element in a sequence. // transform((*2), [1, 3, 4]) == [2, 6, 8] // Also known as map or fmap. template , F, 0>::type> ContainerOut transform(F f, ContainerIn&& xs) { using reuse_t = typename std::conditional< std::is_same< internal::can_reuse_v, internal::reuse_container_t>::value && std::is_base_of< std::true_type, internal::has_order>::value && std::is_same< internal::remove_const_and_ref_t, ContainerOut>::value, internal::reuse_container_t, internal::create_new_container_t>::type; return internal::transform( reuse_t{}, f, std::forward(xs)); } // API search type: transform_convert : ((a -> b), [a]) -> [b] // fwd bind count: 1 // transform_convert((*2), [1, 3, 4]) == [2, 6, 8] // Same as transform, but makes it easy to // use an output container type different from the input container type. template ContainerOut transform_convert(F f, const ContainerIn& xs) { internal::trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); std::transform(std::begin(xs), std::end(xs), it, f); return ys; } // API search type: transform_inner : ((a -> b), [[a]]) -> [[b]] // fwd bind count: 1 // Applies a function to the elements of the inner containers // of a nested sequence. // transform_inner((*2), [[1, 3, 4], [1, 2]]) == [[2, 6, 8], [2, 4]] // Also known as transform_nested, map_nested or map_inner. template ::type, 0 >::type> ContainerOut transform_inner(F f, const ContainerIn& xs) { internal::trigger_static_asserts(); return fplus::transform( fplus::bind_1st_of_2( fplus::transform, f), xs); } namespace internal { template Container reverse(internal::reuse_container_t, Container&& xs) { static_assert(internal::has_order::value, "Reverse: Container has no order."); std::reverse(std::begin(xs), std::end(xs)); return std::forward(xs); } template Container reverse(internal::create_new_container_t, const Container& xs) { static_assert(internal::has_order::value, "Reverse: Container has no order."); Container ys = xs; std::reverse(std::begin(ys), std::end(ys)); return ys; } } // namespace internal // API search type: reverse : [a] -> [a] // fwd bind count: 0 // Reverse a sequence. // reverse([0,4,2,6]) == [6,2,4,0] template > ContainerOut reverse(Container&& xs) { return internal::reverse(internal::can_reuse_v{}, std::forward(xs)); } // API search type: take : (Int, [a]) -> [a] // fwd bind count: 1 // Return the first n elements of a sequence xs. // In case n >= length(xs), xs is returned. // take(3, [0,1,2,3,4,5,6,7]) == [0,1,2] // take(10, [0,1,2]) == [0,1,2] template > ContainerOut take(std::size_t amount, Container&& xs) { if (amount >= size_of_cont(xs)) return xs; return get_segment(0, amount, std::forward(xs)); } // API search type: take_exact : (Int, [a]) -> [a] // fwd bind count: 1 // Return exactly the first n elements of a sequence xs. // Unsafe! Crashes then sequence is too short. // take_exact(3, [0,1,2,3,4,5,6,7]) == [0,1,2] // take_exact(10, [0,1,2]) == crash template > ContainerOut take_exact(std::size_t amount, Container&& xs) { return get_segment(0, amount, std::forward(xs)); } // API search type: take_cyclic : (Int, [a]) -> [a] // fwd bind count: 1 // Takes n elements from a sequence considering it as cyclic. // take_cyclic(5, [0,1,2,3]) == [0,1,2,3,0] // take_cyclic(7, [0,1,2,3]) == [0,1,2,3,0,1,2] // take_cyclic(7, [0,1]) == [0,1,0,1,0,1,0] // take_cyclic(2, [0,1,2,3]) == [0,1] // take_cyclic(3, [0]) == [0,0,0] // take_cyclic(3, []) == crash! // Also known as take_wrap. // xs must be non-empty. template Container take_cyclic(std::size_t amount, const Container& xs) { assert(!xs.empty()); Container ys; internal::prepare_container(ys, size_of_cont(xs)); auto it_out = internal::get_back_inserter(ys); auto it_in = std::begin(xs); while (amount != 0) { *it_out = *it_in; --amount; ++it_in; if (it_in == std::end(xs)) { it_in = std::begin(xs); } } return ys; } // API search type: drop : (Int, [a]) -> [a] // fwd bind count: 1 // Skip the first n elements of a sequence xs. // If n > length(xs) an empty sequence is returned. // drop(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] // Also known as skip. template > ContainerOut drop(std::size_t amount, Container&& xs) { if (amount >= size_of_cont(xs)) return ContainerOut(); return get_segment(amount, size_of_cont(xs), std::forward(xs)); } // API search type: take_last : (Int, [a]) -> [a] // fwd bind count: 1 // Return the last n elements of a sequence xs. // In case n >= length(xs), xs is returned. // take_last(3, [0,1,2,3,4,5,6,7]) == [5,6,7] // take_last(10, [0,1,2]) == [0,1,2] template > ContainerOut take_last(std::size_t amount, Container&& xs) { if (amount >= size_of_cont(xs)) return xs; return drop(size_of_cont(xs) - amount, std::forward(xs)); } // API search type: drop_last : (Int, [a]) -> [a] // fwd bind count: 1 // Skip the last n elements of a sequence xs. // If n > length(xs) an empty sequence is returned. // drop_last(3, [0,1,2,3,4,5,6,7]) == [0,1,2,3,4] template > ContainerOut drop_last(std::size_t amount, Container&& xs) { if (amount >= size_of_cont(xs)) return ContainerOut(); return take(size_of_cont(xs) - amount, std::forward(xs)); } // API search type: drop_exact : (Int, [a]) -> [a] // fwd bind count: 1 // Skip exactly the first n elements of a sequence xs. // Unsafe! Crashes when xs is too short. // drop_exact(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] // drop_exact(10, [0,1,2,3,4,5,6,7]) == crash template > ContainerOut drop_exact(std::size_t amount, Container&& xs) { return get_segment(amount, size_of_cont(xs), std::forward(xs)); } // API search type: take_while : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Take elements from the beginning of a sequence // as long as they are fulfilling a predicate. // take_while(is_even, [0,2,4,5,6,7,8]) == [0,2,4] template Container take_while(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto itFirst = std::find_if( std::begin(xs), std::end(xs), logical_not(pred)); if (itFirst == std::end(xs)) return xs; return Container(std::begin(xs), itFirst); } // API search type: drop_while : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Remove elements from the beginning of a sequence // as long as they are fulfilling a predicate. // drop_while(is_even, [0,2,4,5,6,7,8]) == [5,6,7,8] // Also known as trim_left_by. template Container drop_while(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto itFirstNot = std::find_if_not(std::begin(xs), std::end(xs), pred); if (itFirstNot == std::end(xs)) return Container(); return Container(itFirstNot, std::end(xs)); } // API search type: fold_left : (((a, b) -> a), a, [b]) -> a // fwd bind count: 2 // fold_left((+), 0, [1, 2, 3]) == ((0+1)+2)+3 == 6 // Takes the second argument and the first item of the list // and applies the function to them, // then feeds the function with this result and the second argument and so on. template Acc fold_left(F f, const Acc& init, const Container& xs) { using std::begin; using std::end; return internal::accumulate(begin(xs), end(xs), init, f); } // API search type: reduce : (((a, a) -> a), a, [a]) -> a // fwd bind count: 2 // reduce((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 // Combines the initial value and all elements of the sequence // using the given function. // The set of f, init and value_type should form a monoid. template typename Container::value_type reduce( F f, const typename Container::value_type& init, const Container& xs) { return fold_left(f, init, xs); } // API search type: fold_left_1 : (((a, a) -> a), [a]) -> a // fwd bind count: 1 // fold_left_1((+), [1, 2, 3]) == (1+2)+3 == 6 // Takes the first 2 items of the list and applies the function to them, // then feeds the function with this result and the third argument and so on. // xs must be non-empty. template auto fold_left_1(F f, const Container& xs) { assert(!xs.empty()); using std::begin; using std::end; const auto it = begin(xs); return internal::accumulate(std::next(it), end(xs), *it, f); } // API search type: reduce_1 : (((a, a) -> a), [a]) -> a // fwd bind count: 1 // reduce_1((+), [1, 2, 3]) == (1+2+3) == 6 // Joins all elements of the sequence using the given function. // The set of f and value_type should form a semigroup. template typename Container::value_type reduce_1(F f, const Container& xs) { assert(!xs.empty()); return fold_left_1(f, xs); } // API search type: fold_right : (((a, b) -> b), b) -> [a] -> b // fwd bind count: 2 // fold_right((+), 0, [1, 2, 3]) == 1+(2+(3+0)) == 6 // Takes the second argument and the last item of the list // and applies the function, // then it takes the penultimate item from the end and the result, and so on. template Acc fold_right(F f, const Acc& init, const Container& xs) { return internal::accumulate(xs.rbegin(), xs.rend(), init, flip(f)); } // API search type: fold_right_1 : (((a, a) -> a), [a]) -> a // fwd bind count: 1 // fold_right_1((+), [1, 2, 3]) == 1+(2+3)) == 6 // Takes the last two items of the list and applies the function, // then it takes the third item from the end and the result, and so on. template auto fold_right_1(F f, const Container& xs) { assert(!xs.empty()); const auto it = xs.rbegin(); return internal::accumulate(std::next(it), xs.rend(), *it, flip(f)); } // API search type: scan_left : (((a, b) -> a), a, [b]) -> [a] // fwd bind count: 2 // scan_left((+), 0, [1, 2, 3]) == [0, 1, 3, 6] // Takes the second argument and the first item of the list // and applies the function to them, // then feeds the function with this result and the second argument and so on. // It returns the list of intermediate and final results. template auto scan_left(F f, const Acc& init, const ContainerIn& xs) { using ContainerOut = typename internal::same_cont_new_t::type; ContainerOut result; internal::prepare_container(result, size_of_cont(xs) + 1); using std::begin; using std::end; internal::scan_impl( f, init, internal::get_back_inserter(result), begin(xs), end(xs)); return result; } // API search type: scan_left_1 : (((a, a) -> a), [a]) -> [a] // fwd bind count: 1 // scan_left_1((+), [1, 2, 3]) == [1, 3, 6] // Takes the first 2 items of the list and applies the function to them, // then feeds the function with this result and the third argument and so on. // It returns the list of intermediate and final results. // xs must be non-empty. template auto scan_left_1(F f, const ContainerIn& xs) { assert(!xs.empty()); using std::begin; using std::end; const auto beginIt = begin(xs); using ContainerOut = typename internal::same_cont_new_t< ContainerIn, internal::uncvref_t, 0>::type; ContainerOut result; internal::prepare_container(result, size_of_cont(xs)); internal::scan_impl(f, *beginIt, internal::get_back_inserter(result), std::next(beginIt), end(xs)); return result; } // API search type: scan_right : (((a, b) -> b), b, [a]) -> [b] // fwd bind count: 2 // scan_right((+), 0, [1, 2, 3]) == [6, 5, 3, 0] // Takes the second argument and the last item of the list // and applies the function, // then it takes the penultimate item from the end and the result, and so on. // It returns the list of intermediate and final results. template ::template arg<1>::type, typename ContainerOut = typename internal::same_cont_new_t::type> ContainerOut scan_right(F f, const Acc& init, const ContainerIn& xs) { return reverse(scan_left(flip(f), init, reverse(xs))); } // API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a] // fwd bind count: 1 // scan_right_1((+), [1, 2, 3]) == [6, 5, 3] // Takes the last two items of the list and applies the function, // then it takes the third item from the end and the result, and so on. // It returns the list of inntermediate and final results. template ::type> ContainerOut scan_right_1(F f, const ContainerIn& xs) { return reverse(scan_left_1(flip(f), reverse(xs))); } // API search type: sum : [a] -> a // fwd bind count: 0 // Adds up all values in a sequence. // sum([0,3,1]) == 4 // sum([]) == 0 template T sum(const Container& xs) { T result = T(); for (const auto& x : xs) { result = result + x; } return result; } // API search type: product : [a] -> a // fwd bind count: 0 // Returns the product of all values in a sequence. // product([3,1,2]) == 6 // product([]) == 1 template T product(const Container& xs) { T result{1}; for (const auto& x : xs) { result = result * x; } return result; } namespace internal { template Container append_elem(internal::reuse_container_t, const T& y, Container&& xs) { *internal::get_back_inserter(xs) = y; return std::forward(xs); } template Container append_elem(internal::create_new_container_t, const T& y, const Container& xs) { Container result; internal::prepare_container(result, size_of_cont(xs) + 1); std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); *internal::get_back_inserter(result) = y; return result; } } // namespace internal // API search type: append_elem : (a, [a]) -> [a] // fwd bind count: 1 // Extends a sequence with one element at the back. // append_elem([1, 2], 3) == [1, 2, 3] template , typename T = typename ContainerOut::value_type> ContainerOut append_elem(const T& y, Container&& xs) { return internal::append_elem(internal::can_reuse_v{}, y, std::forward(xs)); } namespace internal { template std::list prepend_elem(internal::reuse_container_t, const T& y, std::list&& xs) { xs.push_front(y); return std::forward>(xs); } template Container prepend_elem(internal::reuse_container_t, const T& y, Container&& xs) { xs.resize(size_of_cont(xs) + 1); std::copy(++xs.rbegin(), xs.rend(), xs.rbegin()); *std::begin(xs) = y; return std::forward(xs); } template Container prepend_elem(internal::create_new_container_t, const T& y, const Container& xs) { Container result; internal::prepare_container(result, size_of_cont(xs) + 1); *internal::get_back_inserter(result) = y; std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); return result; } } // namespace internal // API search type: prepend_elem : (a, [a]) -> [a] // fwd bind count: 1 // Extends a sequence with one element in the front. // prepend_elem([2, 3], 1) == [1, 2, 3] template , typename T = typename ContainerOut::value_type> ContainerOut prepend_elem(const T& y, Container&& xs) { return internal::prepend_elem(internal::can_reuse_v{}, y, std::forward(xs)); } // API search type: append : ([a], [a]) -> [a] // fwd bind count: 1 // Concatenates two sequences. // append([1, 2], [3, 4, 5]) == [1, 2, 3, 4, 5] template ContainerOut append(const ContainerIn1& xs, const ContainerIn2& ys) { ContainerOut result; internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); std::copy(std::begin(ys), std::end(ys), internal::get_back_inserter(result)); return result; } // API search type: append_convert : ([a], [a]) -> [a] // fwd bind count: 1 // Same as append, but makes it easier to // use an output container type different from the input container type. template ContainerOut append_convert(const ContainerIn1& xs, const ContainerIn2& ys) { return append(xs, ys); } // API search type: concat : [[a]] -> [a] // fwd bind count: 0 // Concatenates multiple sequences. // concat([[1, 2], [], [3]]) == [1, 2, 3] // Also known as flatten. template ContainerOut concat(const ContainerIn& xss) { std::size_t length = sum( transform(size_of_cont, xss)); ContainerOut result; internal::prepare_container(result, length); using std::begin; using std::end; for(const auto& xs : xss) { result.insert(end(result), begin(xs), end(xs)); } return result; } // API search type: interweave : ([a], [a]) -> [a] // fwd bind count: 1 // Return a sequence that contains elements from the two provided sequences // in alternating order. If one list runs out of items, // appends the items from the remaining list. // interweave([1,3], [2,4]) == [1,2,3,4] // interweave([1,3,5,7], [2,4]) == [1,2,3,4,5,7] // See interleave for interweaving more than two sequences. template Container interweave(const Container& xs, const Container& ys) { Container result; internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); auto it = internal::get_back_inserter(result); auto it_xs = std::begin(xs); auto it_ys = std::begin(ys); while (it_xs != std::end(xs) || it_ys != std::end(ys)) { if (it_xs != std::end(xs)) *it = *(it_xs++); if (it_ys != std::end(ys)) *it = *(it_ys++); } return result; } // API search type: unweave : [a] -> ([a], [a]) // fwd bind count: 0 // Puts the elements with an even index into the first list, // and the elements with an odd index into the second list. // Inverse of interweave. // unweave([0,1,2,3]) == ([0,2], [1,3]) // unweave([0,1,2,3,4]) == ([0,2,4], [1,3]) template std::pair unweave(const Container& xs) { std::pair result; if (size_of_cont(xs) % 2 == 0) internal::prepare_container(result.first, size_of_cont(xs) / 2); else internal::prepare_container(result.first, size_of_cont(xs) / 2 + 1); internal::prepare_container(result.second, size_of_cont(xs) / 2); auto it_even = internal::get_back_inserter(result.first); auto it_odd = internal::get_back_inserter(result.second); std::size_t counter = 0; for (const auto& x : xs) { if (counter++ % 2 == 0) *it_even = x; else *it_odd = x; } return result; } namespace internal { template std::list sort_by(internal::reuse_container_t, Compare comp, std::list&& xs) { xs.sort(comp); return std::forward>(xs); } template std::list sort_by(internal::create_new_container_t, Compare comp, const std::list& xs) { auto result = xs; result.sort(comp); return result; } template Container sort_by(internal::reuse_container_t, Compare comp, Container&& xs) { std::sort(std::begin(xs), std::end(xs), comp); return std::forward(xs); } template Container sort_by(internal::create_new_container_t, Compare comp, const Container& xs) { auto result = xs; std::sort(std::begin(result), std::end(result), comp); return result; } } // namespace internal // API search type: sort_by : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Sort a sequence by given less comparator. template > ContainerOut sort_by(Compare comp, Container&& xs) { return internal::sort_by(internal::can_reuse_v{}, comp, std::forward(xs)); } namespace internal { // workarounds for clang bug 24115 // (std::sort and std::unique with std::function as comp) // https://llvm.org/bugs/show_bug.cgi?id=24115 template struct is_less_by_struct { is_less_by_struct(F f) : f_(f) {}; template bool operator()(const T& x, const T& y) { return f_(x) < f_(y); } private: F f_; }; template struct is_equal_by_struct { is_equal_by_struct(F f) : f_(f) {}; template bool operator()(const T& x, const T& y) { return f_(x) == f_(y); } private: F f_; }; } // API search type: sort_on : ((a -> b), [a]) -> [a] // fwd bind count: 1 // Sort a sequence by a given transformer. template > ContainerOut sort_on(F f, Container&& xs) { return sort_by(internal::is_less_by_struct(f), std::forward(xs)); } // API search type: sort : [a] -> [a] // fwd bind count: 0 // Sort a sequence to ascending order using std::less. template > ContainerOut sort(Container&& xs) { typedef typename std::remove_reference::type::value_type T; return sort_by(std::less(), std::forward(xs)); } namespace internal { template std::list stable_sort_by(internal::reuse_container_t, Compare comp, std::list&& xs) { xs.sort(comp); // std::list::sort ist already stable. return std::forward>(xs); } template std::list stable_sort_by(internal::create_new_container_t, Compare comp, const std::list& xs) { auto result = xs; result.sort(comp); // std::list::sort ist already stable. return result; } template Container stable_sort_by(internal::reuse_container_t, Compare comp, Container&& xs) { std::sort(std::begin(xs), std::end(xs), comp); return std::forward(xs); } template Container stable_sort_by(internal::create_new_container_t, Compare comp, const Container& xs) { auto result = xs; std::sort(std::begin(result), std::end(result), comp); return result; } } // namespace internal // API search type: stable_sort_by : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Sort a sequence stably by given less comparator. template > ContainerOut stable_sort_by(Compare comp, Container&& xs) { return internal::stable_sort_by(internal::can_reuse_v{}, comp, std::forward(xs)); } // API search type: stable_sort_on : ((a -> b), [a]) -> [a] // fwd bind count: 1 // Sort a sequence stably by given transformer. template > ContainerOut stable_sort_on(F f, Container&& xs) { return stable_sort_by(internal::is_less_by_struct(f), std::forward(xs)); } // API search type: stable_sort : [a] -> [a] // fwd bind count: 0 // Sort a sequence stably to ascending order using std::less. template > ContainerOut stable_sort(Container&& xs) { typedef typename std::remove_reference::type::value_type T; return stable_sort_by(std::less(), std::forward(xs)); } namespace internal { template Container partial_sort_by(internal::reuse_container_t, Compare comp, std::size_t count, Container&& xs) { if (count > xs.size()) { count = xs.size(); } auto middle = std::begin(xs); internal::advance_iterator(middle, count); std::partial_sort(std::begin(xs), middle, std::end(xs), comp); return std::forward(get_segment(internal::reuse_container_t(), 0, count, xs)); } template Container partial_sort_by(internal::create_new_container_t, Compare comp, std::size_t count, const Container& xs) { auto result = xs; return partial_sort_by( internal::reuse_container_t(), comp, count, std::move(result)); } } // namespace internal // API search type: partial_sort_by : (((a, a) -> Bool), Int, [a]) -> [a] // fwd bind count: 2 // Partially sort a sequence by a given less comparator. // Returns only the sorted segment. template > ContainerOut partial_sort_by(Compare comp, std::size_t count, Container&& xs) { return internal::partial_sort_by(internal::can_reuse_v{}, comp, count, std::forward(xs)); } // API search type: partial_sort_on : ((a -> b), Int, [a]) -> [a] // fwd bind count: 2 // Partially sort a sequence by a given transformer. // Returns only the sorted segment. template > ContainerOut partial_sort_on(F f, std::size_t count, Container&& xs) { return partial_sort_by(internal::is_less_by_struct(f), count, std::forward(xs)); } // API search type: partial_sort : (Int, [a]) -> [a] // fwd bind count: 1 // Partially sort a sequence in ascending order using std::less. // Returns only the sorted segment. template > ContainerOut partial_sort(std::size_t count, Container&& xs) { typedef typename std::remove_reference::type::value_type T; return partial_sort_by(std::less(), count, std::forward(xs)); } // API search type: nth_element_by : (((a, a) -> Bool), Int, [a]) -> a // fwd bind count: 2 // Return the nth largest element of a sequence by a given less comparator. template T nth_element_by(Compare comp, std::size_t n, const Container& xs) { assert(n < xs.size()); auto result = xs; auto middle = std::begin(result); internal::advance_iterator(middle, n); std::nth_element(std::begin(result), middle, std::end(result), comp); return *middle; } // API search type: nth_element_on : ((a -> b), Int, [a]) -> a // fwd bind count: 2 // Return the nth largest element of a sequence by a given transformer. template T nth_element_on(F f, std::size_t n, const Container& xs) { return nth_element_by(internal::is_less_by_struct(f), n, xs); } // API search type: nth_element : (Int, [a]) -> a // fwd bind count: 1 // Return the nth largest element of a sequence using std::less. template T nth_element(std::size_t n, const Container& xs) { return nth_element_by(std::less(), n, xs); } namespace internal { template Container unique_by(internal::reuse_container_t, BinaryPredicate pred, Container&& xs) { internal::check_binary_predicate_for_container(); const auto it_end = std::unique(std::begin(xs), std::end(xs), pred); xs.erase(it_end, std::end(xs)); return std::forward(xs); } template Container unique_by(internal::create_new_container_t, BinaryPredicate pred, const Container& xs) { auto result = xs; return unique_by(internal::reuse_container_t(), pred, std::move(result)); } } // namespace internal // API search type: unique_by : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Like unique, eliminates all but the first element // from every consecutive group of equivalent elements from the sequence; // but with a user supplied equality predicate. // See nub_by for making elements globally unique in a sequence. // O(n) template > ContainerOut unique_by(BinaryPredicate pred, Container&& xs) { return internal::unique_by(internal::can_reuse_v{}, pred, std::forward(xs)); } // API search type: unique_on : ((a -> b), [a]) -> [a] // fwd bind count: 1 // Like unique, eliminates all but the first element // from every consecutive group of equivalent elements from the sequence; // but with a user supplied transformation (e.g. getter). // See nub_on for making elements globally unique in a sequence. // O(n) // Also known as drop_repeats. template > ContainerOut unique_on(F f, Container&& xs) { return unique_by(internal::is_equal_by_struct(f), std::forward(xs)); } // API search type: unique : [a] -> [a] // fwd bind count: 0 // Eliminates all but the first element // from every consecutive group of equivalent elements from the sequence. // unique([1,2,2,3,2]) == [1,2,3,2] // See nub for making elements globally unique in a sequence. // O(n) template > ContainerOut unique(Container&& xs) { typedef typename std::remove_reference::type::value_type T; return unique_on(identity, std::forward(xs)); } // API search type: intersperse : (a, [a]) -> [a] // fwd bind count: 1 // Insert a value between all adjacent values in a sequence. // intersperse(0, [1, 2, 3]) == [1, 0, 2, 0, 3] // Also known as interpose. template Container intersperse(const X& value, const Container& xs) { if (xs.empty()) return Container(); if (size_of_cont(xs) == 1) return xs; Container result; internal::prepare_container(result, std::max(0, size_of_cont(xs)*2-1)); auto it = internal::get_back_inserter(result); for_each(std::begin(xs), --std::end(xs), [&value, &it](const X& x) { *it = x; *it = value; }); *it = xs.back(); return result; } // API search type: join : ([a], [[a]]) -> [a] // fwd bind count: 1 // Inserts a separator sequence in between the elements // of a sequence of sequences and concatenates the result. // Also known as intercalate or implode. // join(", ", ["a", "bee", "cee"]) == "a, bee, cee" // join([0, 0], [[1], [2], [3, 4]]) == [1, 0, 0, 2, 0, 0, 3, 4] template X join(const X& separator, const Container& xs) { return concat(intersperse(separator, xs)); } // API search type: join_elem : (a, [[a]]) -> [a] // fwd bind count: 1 // Inserts a separator in between the elements // of a sequence of sequences and concatenates the result. // Also known as intercalate_elem. // join_elem(';', ["a", "bee", "cee"]) == "a;bee;cee" // join_elem(0, [[1], [2], [3, 4]]) == [1, 0, 2, 0, 3, 4]] template Inner join_elem(const X& separator, const Container& xs) { return join(Inner(1, separator), xs); } // API search type: is_elem_of_by : ((a -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if at least one element of the container fulfils a predicate. // is_elem_of_by((==), [1,2,3]) == true template bool is_elem_of_by(UnaryPredicate pred, const Container& xs) { return std::find_if(std::begin(xs), std::end(xs), pred) != std::end(xs); } // API search type: is_elem_of : (a, [a]) -> Bool // fwd bind count: 1 // Checks if an element is a member of a container. // is_elem_of(2, [1,2,3]) == true // Also known as flip(contains). template bool is_elem_of(const typename Container::value_type& x, const Container& xs) { return is_elem_of_by(is_equal_to(x), xs); } // API search type: nub_by : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Makes the elements in a container unique with respect to a predicate // nub_by((==), [1,2,2,3,2]) == [1,2,3] // O(n^2) template Container nub_by(BinaryPredicate p, const Container& xs) { Container result; auto itOut = internal::get_back_inserter(result); for (const auto &x : xs) { auto eqToX = bind_1st_of_2(p, x); if (!is_elem_of_by(eqToX, result)) { *itOut = x; } } return result; } // API search type: nub_on : ((a -> b), [a]) -> [a] // fwd bind count: 1 // Makes the elements in a container unique // with respect to their function value. // nub_on((mod 10), [12,32,15]) == [12,15] // O(n^2) template Container nub_on(F f, const Container& xs) { return nub_by(is_equal_by(f), xs); } // API search type: nub : [a] -> [a] // fwd bind count: 0 // Makes the elements in a container unique. // nub([1,2,2,3,2]) == [1,2,3] // O(n^2) // Also known as distinct. template Container nub(const Container& xs) { typedef typename Container::value_type T; return nub_by(std::equal_to(), xs); } // API search type: all_unique_by_eq : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if all elements in a container are unique // with respect to a predicate. // Returns true for empty containers. // O(n^2) template bool all_unique_by_eq(BinaryPredicate p, const Container& xs) { internal::check_binary_predicate_for_container(); return size_of_cont(nub_by(p, xs)) == size_of_cont(xs); } // API search type: all_unique_on : ((a -> b), [a]) -> Bool // fwd bind count: 1 // Checks if all elements in a container are unique // with respect to their function values. // Returns true for empty containers. // O(n^2) template bool all_unique_on(F f, const Container& xs) { return all_unique_by_eq(is_equal_by(f), xs); } // API search type: all_unique : [a] -> Bool // fwd bind count: 0 // Checks if all elements in a container are unique. // Returns true for empty containers. // O(n^2) template bool all_unique(const Container& xs) { typedef typename Container::value_type T; auto comp = std::equal_to(); return all_unique_by_eq(comp, xs); } // API search type: is_strictly_sorted_by : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if a container already is strictly sorted using a predicate. // comp(a, b) must return true only if a < b. // O(n) template bool is_strictly_sorted_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); if (size_of_cont(xs) < 2) return true; auto it1 = std::begin(xs); for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) if (!internal::invoke(comp, *it1, *it2)) return false; return true; } // API search type: is_strictly_sorted_on : ((a -> b), [a]) -> Bool // fwd bind count: 1 // Checks if a container already is strictly sorted using a transformer. // O(n) template bool is_strictly_sorted_on(F f, const Container& xs) { return is_strictly_sorted_by(is_less_by(f), xs); } // API search type: is_strictly_sorted : [a] -> Bool // fwd bind count: 0 // Checks if a container already is strictly sorted // in ascending order using std::less. // O(n) template bool is_strictly_sorted(const Container& xs) { typedef typename Container::value_type T; auto comp = std::less(); return is_strictly_sorted_by(comp, xs); } // API search type: is_sorted_by : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if a container already is sorted using a predicate. // comp(a, b) must return true only if a < b. // O(n) template bool is_sorted_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); if (size_of_cont(xs) < 2) return true; auto it1 = std::begin(xs); for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) if (internal::invoke(comp, *it2, *it1)) return false; return true; } // API search type: is_sorted_on : ((a -> b), [a]) -> Bool // fwd bind count: 1 // Checks if a container already is strictly sorted using a transformer. // O(n) template bool is_sorted_on(F f, const Container& xs) { return is_sorted_by(is_less_by(f), xs); } // API search type: is_sorted : [a] -> Bool // fwd bind count: 0 // Checks if a container already is sorted // in ascending order using std::less. // O(n) template bool is_sorted(const Container& xs) { typedef typename Container::value_type T; auto comp = std::less(); return is_sorted_by(comp, xs); } // API search type: is_prefix_of : ([a], [a]) -> Bool // fwd bind count: 1 // Checks if a containers starts with a token. // is_prefix_of("Fun", "FunctionalPlus") == true template bool is_prefix_of(const Container& token, const Container& xs) { if (size_of_cont(token) > size_of_cont(xs)) return false; return get_segment(0, size_of_cont(token), xs) == token; } // API search type: is_suffix_of : ([a], [a]) -> Bool // fwd bind count: 1 // Checks if a containers contains a token as a segment. // is_suffix_of("us", "FunctionalPlus") == true template bool is_suffix_of(const Container& token, const Container& xs) { if (size_of_cont(token) > size_of_cont(xs)) return false; return get_segment(size_of_cont(xs) - size_of_cont(token), size_of_cont(xs), xs) == token; } // API search type: all_by : ((a -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if a containers contains a token as a segment. // all_by(is_even, [2, 4, 6]) == true // Returns true for empty containers. template bool all_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return std::all_of(std::begin(xs), std::end(xs), p); } // API search type: all : [Bool] -> Bool // fwd bind count: 0 // Checks if all values in a container evaluate to true. // all([true, false, true]) == false // Returns true for empty containers. template bool all(const Container& xs) { typedef typename Container::value_type T; return all_by(identity, xs); } // API search type: all_the_same_by : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Checks if all values in a container are equal using a binary predicate. // Returns true for empty containers. template bool all_the_same_by(BinaryPredicate p, const Container& xs) { internal::check_binary_predicate_for_container(); if (size_of_cont(xs) < 2) return true; auto unaryPredicate = bind_1st_of_2(p, xs.front()); return all_by(unaryPredicate, xs); } // API search type: all_the_same_on : ((a -> b), [a]) -> Bool // fwd bind count: 1 // Checks if all values in a container are equal using a transformer. // Returns true for empty containers. template bool all_the_same_on(F f, const Container& xs) { if (size_of_cont(xs) < 2) return true; auto unaryPredicate = is_equal_by_to(f, f(xs.front())); return all_by(unaryPredicate, xs); } // API search type: all_the_same : [a] -> Bool // fwd bind count: 0 // Checks if all values in a container are equal. // Returns true for empty containers. template bool all_the_same(const Container& xs) { typedef typename Container::value_type T; auto binaryPredicate = std::equal_to(); return all_the_same_by(binaryPredicate, xs); } // API search type: numbers_step : (a, a, a) -> [a] // fwd bind count: 2 // Return a sequence of numbers using a specific step. // numbers_step(2, 9, 2) == [2, 4, 6, 8] template > ContainerOut numbers_step (const T start, const T end, const T step) { ContainerOut result; if ((step > 0 && start >= end) || (step < 0 && start <= end) || step == 0) { return result; } std::size_t size = static_cast((end - start) / step); internal::prepare_container(result, size); auto it = internal::get_back_inserter(result); for (T x = start; x < end; x += step) *it = x; return result; } // API search type: numbers : (a, a) -> [a] // fwd bind count: 1 // Return an ascending sequence of numbers.. // Also known as range. // numbers(2, 9) == [2, 3, 4, 5, 6, 7, 8] template > ContainerOut numbers(const T start, const T end) { return numbers_step(start, end, 1); } // API search type: singleton_seq : a -> [a] // fwd bind count: 0 // Construct a sequence containing a single value. // singleton_seq(3) == [3]. template > ContainerOut singleton_seq(const T& x) { return ContainerOut(1, x); } // API search type: all_idxs : [a] -> [Int] // fwd bind count: 0 // Returns a vector containing all valid indices of sequence xs. // all_idxs([6,4,7,6]) == [0,1,2,3] template std::vector all_idxs(const Container& xs) { return numbers(0, size_of_cont(xs)); } // API search type: init : [a] -> [a] // fwd bind count: 0 // init([0,1,2,3]) == [0,1,2] // Unsafe! xs must be non-empty. template > ContainerOut init(Container&& xs) { assert(!is_empty(xs)); return get_segment(0, size_of_cont(std::forward(xs)) - 1, xs); } // API search type: tail : [a] -> [a] // fwd bind count: 0 // Drops the first element of a container, keeps the rest. Unsafe! // tail([0,1,2,3]) == [1,2,3] // Unsafe! xs must be non-empty. template > ContainerOut tail(Container&& xs) { assert(!is_empty(xs)); return get_segment(1, size_of_cont(std::forward(xs)), xs); } // API search type: head : [a] -> a // fwd bind count: 0 // Return the first element of a container. // head([0,1,2,3]) == 0 // Unsafe! xs must be non-empty. template typename Container::value_type head(const Container& xs) { assert(!is_empty(xs)); return xs.front(); } // API search type: last : [a] -> a // fwd bind count: 0 // Return the last element of a container. // last([0,1,2,3]) == 3 // Unsafe! xs must be non-empty. template typename Container::value_type last(const Container& xs) { assert(!is_empty(xs)); return xs.back(); } // API search type: mean_stddev : [a] -> (a, a) // fwd bind count: 0 // Calculates the mean and the population standard deviation. // mean_stddev([4, 8]) == (6, 2) // mean_stddev([1, 3, 7, 4]) == (3.75, 2.5) // xs must be non-empty. template std::pair mean_stddev(const Container& xs) { assert(size_of_cont(xs) != 0); // http://stackoverflow.com/a/7616783/1866775 Result sum = static_cast( internal::accumulate(xs.begin(), xs.end(), static_cast(0))); Result mean = sum / static_cast(xs.size()); std::vector diff(xs.size()); std::transform(xs.begin(), xs.end(), diff.begin(), [mean](Result x) { return x - mean; }); Result sq_sum = std::inner_product( diff.begin(), diff.end(), diff.begin(), static_cast(0)); Result stddev = std::sqrt(sq_sum / static_cast(xs.size())); return std::make_pair(mean, stddev); } // API search type: count_occurrences_by : ((a -> b), [a]) -> Map b Int // fwd bind count: 1 // Returns a discrete frequency distribution of the elements in a container // applying a specific transformer. // count_occurrences_by(floor, [1.1, 2.3, 2.7, 3.6, 2.4]) == [(1, 1), (2, 3), (3, 1)] // O(n) template auto count_occurrences_by(F f, const ContainerIn& xs) { using In = typename ContainerIn::value_type; using MapOut = std::map>, std::size_t>; internal::trigger_static_asserts(); MapOut result; for (const auto& x : xs) { ++result[internal::invoke(f, x)]; } return result; } // API search type: count_occurrences : [a] -> Map a Int // fwd bind count: 0 // Returns a discrete frequency distribution of the elements in a container // applying a specific transformer. // Can be used to create a histogram. // count_occurrences([1,2,2,3,2]) == [(1, 1), (2, 3), (3, 1)] // O(n) template > MapOut count_occurrences(const ContainerIn& xs) { return count_occurrences_by(identity, xs); } // API search type: lexicographical_less_by : (((a, a) -> Bool), [a], [a]) -> Bool // fwd bind count: 2 // Lexicographical less-than comparison using a specific predicate. // lexicographical_less_by((<), [0,1,2,2,4,5], [0,1,2,3,4,5]) == true // lexicographical_less_by((<), "012245", "012345") == true // lexicographical_less_by((<), "01234", "012345") == true // lexicographical_less_by((<), "012345", "01234") == false // lexicographical_less_by((<), "012345", "012345") == false template bool lexicographical_less_by(BinaryPredicate p, const Container& xs, const Container& ys) { internal::check_binary_predicate_for_container(); auto itXs = std::begin(xs); auto itYs = std::begin(ys); while (itXs != std::end(xs) && itYs != std::end(ys)) { if (internal::invoke(p, *itXs, *itYs)) { return true; } if (internal::invoke(p, *itYs, *itXs)) { return false; } ++itXs; ++itYs; } if (size_of_cont(xs) < size_of_cont(ys)) { return true; } return false; } // API search type: lexicographical_less : ([a], [a]) -> Bool // fwd bind count: 1 // Lexicographical less-than comparison. // lexicographical_less([0,1,2,2,4,5], [0,1,2,3,4,5]) == true // lexicographical_less("012245", "012345") == true // lexicographical_less("01234", "012345") == true // lexicographical_less("012345", "01234") == false // lexicographical_less("012345", "012345") == false template bool lexicographical_less(const Container& xs, const Container& ys) { return lexicographical_less_by( is_less, xs, ys); } // API search type: lexicographical_sort : [[a]] -> [[a]] // fwd bind count: 0 // sort by lexicographical_less template > ContainerOut lexicographical_sort(Container&& xs) { typedef typename std::remove_reference::type::value_type T; return sort_by(lexicographical_less, std::forward(xs)); } // API search type: replicate : (Int, a) -> [a] // fwd bind count: 1 // Create a sequence containing x n times. // replicate(3, 1) == [1, 1, 1] template > ContainerOut replicate(std::size_t n, const T& x) { return ContainerOut(n, x); } namespace internal { template T instead_of_if(internal::reuse_container_t, UnaryPredicate pred, const T& alt, T&& x) { if (internal::invoke(pred, x)) return alt; else return std::forward(x); } template T instead_of_if(internal::create_new_container_t, UnaryPredicate pred, const T& alt, const T& x) { if (internal::invoke(pred, x)) return alt; else return x; } } // namespace internal // API search type: instead_of_if : ((a -> Bool), a, a) -> a // fwd bind count: 2 // Return alt if pred(x), otherwise x itself. template auto instead_of_if(UnaryPredicate pred, const TAlt& alt, T&& x) { return internal::instead_of_if(internal::can_reuse_v{}, pred, alt, std::forward(x)); } // API search type: instead_of_if_empty : ((a -> Bool), [a], [a]) -> [a] // fwd bind count: 2 // Return alt if xs is empty, otherwise xs itself. template > ContainerOut instead_of_if_empty(const ContainerAlt& alt, Container&& xs) { return instead_of_if( is_empty>, alt, std::forward(xs)); } } // namespace fplus // // container_properties.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // generate.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // filter.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // result.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include namespace fplus { template class result; template result ok(const Ok& val); template result error(const Error& error); // Can hold a value of type Ok or an error of type Error. template class result { public: bool is_ok() const { return static_cast(ptr_ok_); } bool is_error() const { return static_cast(ptr_error_); } const Ok& unsafe_get_ok() const { check_either_or_invariant(); assert(is_ok()); return *ptr_ok_; } const Error& unsafe_get_error() const { check_either_or_invariant(); assert(is_error()); return *ptr_error_; } typedef Ok ok_t; typedef Error error_t; result(const result& other) : ptr_ok_(other.is_ok() ? ptr_ok(new Ok(other.unsafe_get_ok())) : ptr_ok()), ptr_error_(other.is_error() ? ptr_error(new Error(other.unsafe_get_error())) : ptr_error()) { check_either_or_invariant(); } result& operator = (const result& other) { ptr_ok_ = other.is_ok() ? ptr_ok(new Ok(other.unsafe_get_ok())) : ptr_ok(); ptr_error_ = other.is_error() ? ptr_error(new Error(other.unsafe_get_error())) : ptr_error(); return *this; } private: void check_either_or_invariant() const { assert(is_ok() != is_error()); } result() : ptr_ok_(ptr_ok()), ptr_error_(ptr_error()) {} typedef std::unique_ptr ptr_ok; typedef std::unique_ptr ptr_error; friend result ok(const Ok& ok); friend result error(const Error& error); ptr_ok ptr_ok_; ptr_error ptr_error_; }; // API search type: is_ok : Result a b -> Bool // fwd bind count: 0 // Is not error? template bool is_ok(const result& result) { return result.is_ok(); } // API search type: is_error : Result a b -> Bool // fwd bind count: 0 // Is not OK? template bool is_error(const result& result) { return !is_ok(result); } // API search type: unsafe_get_ok : Result a b -> a // fwd bind count: 0 // Crashes if result is error! template Ok unsafe_get_ok(const result& result) { return result.unsafe_get_ok(); } // API search type: unsafe_get_error : Result a b -> b // fwd bind count: 0 // Crashes if result is ok! template Error unsafe_get_error(const result& result) { return result.unsafe_get_error(); } // API search type: ok_with_default : (a, Result a b) -> a // fwd bind count: 1 // Get the value from a result or the default in case it is error. template Ok ok_with_default(const Ok& defaultValue, const result& result) { if (is_ok(result)) return unsafe_get_ok(result); return defaultValue; } // API search type: ok : a -> Result a b // fwd bind count: 0 // Wrap a value in a result as a Ok. template result ok(const Ok& val) { result x; x.ptr_ok_.reset(new Ok(val)); return x; } // API search type: error : b -> Result a b // fwd bind count: 0 // Construct an error of a certain result type. template result error(const Error& error) { result x; x.ptr_error_.reset(new Error(error)); return x; } // API search type: to_maybe : Result a b -> Maybe a // fwd bind count: 0 // Convert ok to just, error to nothing. template maybe to_maybe(const result& result) { if (is_ok(result)) return just(unsafe_get_ok(result)); else return nothing(); } // API search type: from_maybe : (b, Maybe a) -> Result a b // fwd bind count: 1 // Convert just to ok, nothing to error. template result from_maybe(const Error& err, const maybe& maybe) { if (is_just(maybe)) return ok(unsafe_get_just(maybe)); else return error(err); } // API search type: throw_on_error : (e, Result a b) -> a // fwd bind count: 1 // Throws the given exception in case of error. // Return ok value if ok. template Ok throw_on_error(const E& e, const result& result) { if (is_error(result)) throw e; return unsafe_get_ok(result); } // API search type: throw_type_on_error : Result a b -> a // Throws the given exception type constructed with error value if error. // Return ok value if ok. template Ok throw_type_on_error(const result& result) { if (is_error(result)) throw E(unsafe_get_error(result)); return unsafe_get_ok(result); } // True if ok values are the same or if errors are the same. template bool operator == (const result& x, const result& y) { if (is_ok(x) && is_ok(y)) return unsafe_get_ok(x) == unsafe_get_ok(y); if (is_error(x) && is_error(y)) return unsafe_get_error(x) == unsafe_get_error(y); return false; } // False if ok values are the same or if both errors are the same. template bool operator != (const result& x, const result& y) { return !(x == y); } // API search type: lift_result : ((a -> b), Result a c) -> Result b c // fwd bind count: 1 // Lifts a function into the result functor. // A function that for example was able to convert and int into a string, // now can convert a result into a result. // An error stays the same error, regardless of the conversion. template auto lift_result(F f, const result& r) { internal::trigger_static_asserts(); using B = std::decay_t>; if (is_ok(r)) return ok(internal::invoke(f, unsafe_get_ok(r))); return error(unsafe_get_error(r)); } // API search type: lift_result_both : ((a -> c), (b -> d), Result a b) -> Result c d // fwd bind count: 2 // Lifts two functions into the result functor. template auto lift_result_both(F f, G g, const result& r) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); using C = std::decay_t>; using D = std::decay_t>; if (is_ok(r)) return ok(internal::invoke(f, unsafe_get_ok(r))); return error(internal::invoke(g, unsafe_get_error(r))); } // API search type: unify_result : ((a -> c), (b -> c), Result a b) -> c // fwd bind count: 2 // Extracts the value (Ok or Error) from a Result // as defined by the two given functions. template auto unify_result(F f, G g, const result& r) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); static_assert(std::is_same, internal::invoke_result_t>::value, "Both functions must return the same type."); if (is_ok(r)) return internal::invoke(f, unsafe_get_ok(r)); return internal::invoke(g, unsafe_get_error(r)); } // API search type: join_result : Result (Result a b) b -> Result a b // Flattens a nested result. // join_result(Ok Ok x) == Ok x // join_result(Ok Error e) == Error e // join_result(Error e) == Error e template result join_result(const result, Error>& r) { if (is_ok(r)) return unsafe_get_ok(r); else return error(r.unsafe_get_error()); } // API search type: and_then_result : ((a -> Result c b), (Result a b)) -> Result c b // fwd bind count: 1 // Monadic bind. // Returns the error if the result is an error. // Otherwise return the result of applying // the function to the ok value of the result. template auto and_then_result(F f, const result& r) { internal::trigger_static_asserts(); using FOut = std::decay_t>; static_assert(std::is_same::value, "Error type must stay the same."); if (is_ok(r)) return internal::invoke(f, unsafe_get_ok(r)); else return error(r.unsafe_get_error()); } // API search type: compose_result : ((a -> Result b c), (b -> Result d c)) -> (a -> Result d c) // Left-to-right Kleisli composition of monads. // It is possible to compose a variadic number of callables. // The first callable can take a variadic number of parameters. template auto compose_result(Callables&&... callables) { auto bind_result = [](auto f, auto g) { return [f = std::move(f), g = std::move(g)](auto&&... args) { internal::trigger_static_asserts(); #if defined(_MSC_VER) && _MSC_VER >= 1920 // in VS2019, compilation with /permissive- breaks with 'using' syntax below struct FOut : std::decay_t< internal::invoke_result_t> {}; #else using FOut = std::decay_t< internal::invoke_result_t>; #endif internal::trigger_static_asserts(); #if defined(_MSC_VER) && _MSC_VER >= 1920 // in VS2019, compilation with /permissive- breaks with 'using' syntax below struct GOut : std::decay_t< internal::invoke_result_t> {}; #else using GOut = std::decay_t< internal::invoke_result_t>; #endif static_assert(std::is_same::value, "Error type must stay the same."); auto resultB = internal::invoke(f, std::forward(args)...); if (is_ok(resultB)) return internal::invoke(g, unsafe_get_ok(resultB)); return error( unsafe_get_error(resultB)); }; }; return internal::compose_binary_lift(bind_result, std::forward(callables)...); } } // namespace fplus #include namespace fplus { namespace internal { template Container keep_if(internal::reuse_container_t, Pred pred, Container&& xs) { internal::check_unary_predicate_for_container(); xs.erase(std::remove_if( std::begin(xs), std::end(xs), logical_not(pred)), std::end(xs)); return std::forward(xs); } template Container keep_if(internal::create_new_container_t, Pred pred, const Container& xs) { internal::check_unary_predicate_for_container(); Container result; auto it = internal::get_back_inserter(result); std::copy_if(std::begin(xs), std::end(xs), it, pred); return result; } } // namespace internal // API search type: keep_if : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Keep the elements of a sequence fulfilling a predicate. // keep_if(is_even, [1, 2, 3, 2, 4, 5]) == [2, 2, 4] // Also known as filter. template > ContainerOut keep_if(Pred pred, Container&& xs) { return internal::keep_if(internal::can_reuse_v{}, pred, std::forward(xs)); } // API search type: drop_if : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Drop the elements of a sequence fulfilling a predicate. // drop_if(is_even, [1, 2, 3, 2, 4, 5]) == [1, 3, 5] // Also known as reject. template > ContainerOut drop_if(Pred pred, Container&& xs) { return keep_if(logical_not(pred), std::forward(xs)); } // API search type: without : (a, [a]) -> [a] // fwd bind count: 1 // Keep all elements a sequence not equal to elem. // without(0, [1, 0, 0, 5, 3, 0, 1]) == [1, 5, 3, 1] template > ContainerOut without(T elem, Container&& xs) { return drop_if(is_equal_to(elem), std::forward(xs)); } // API search type: without_any : (a, [a]) -> [a] // fwd bind count: 1 // Keep all elements a sequence not present in elems. // without([0, 1], [1, 0, 0, 5, 3, 0, 1]) == [5, 3] template > ContainerOut without_any(const ContainerElems& elems, Container&& xs) { static_assert(std::is_same< typename ContainerElems::value_type, typename std::remove_reference::type::value_type>::value, "Container values must be of the same type."); const auto pred = bind_2nd_of_2(is_elem_of, elems); return drop_if(pred, std::forward(xs)); } // API search type: keep_if_with_idx : (((Int, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Keep the elements of a sequence fulfilling a predicate. // Predicate takes index and value. // All elements fulfilling the predicate are kept. template Container keep_if_with_idx(Pred pred, const Container& xs) { internal::check_index_with_type_predicate_for_container(); Container ys; auto it = internal::get_back_inserter(ys); std::size_t idx = 0; for (const auto& x : xs) { if (internal::invoke(pred, idx++, x)) *it = x; } return ys; } // API search type: drop_if_with_idx : (((Int, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // Drop the elements of a sequence fulfilling a predicate. // Predicate takes index and value. // All elements fulfilling the predicate are skipped. template Container drop_if_with_idx(Pred pred, const Container& xs) { internal::check_index_with_type_predicate_for_container(); const auto inverse_pred = [pred](auto idx, const auto& x) { return !internal::invoke(pred, idx, x); }; return keep_if_with_idx(inverse_pred, xs); } namespace internal { template Container keep_by_idx(internal::reuse_container_t, UnaryPredicate pred, Container&& xs) { auto itOut = std::begin(xs); std::size_t i = 0; for (auto it = std::begin(xs); it != std::end(xs); ++it) { if (internal::invoke(pred, i++)) *itOut++ = std::move(*it); } xs.erase(itOut, std::end(xs)); return std::forward(xs); } template Container keep_by_idx(internal::create_new_container_t, UnaryPredicate pred, const Container& xs) { Container ys = xs; return internal::keep_by_idx(internal::reuse_container_t(), pred, std::move(ys)); } } // namespace internal // API search type: keep_by_idx : ((Int -> Bool), [a]) -> [a] // fwd bind count: 1 // Keep the elements of a sequence with an index fulfilling a predicate. // Predicate takes an index and decides if an element is kept. template > ContainerOut keep_by_idx(UnaryPredicate pred, Container&& xs) { internal::check_unary_predicate_for_type(); return internal::keep_by_idx(internal::can_reuse_v{}, pred, std::forward(xs)); } // API search type: drop_by_idx : ((Int -> Bool), [a]) -> [a] // fwd bind count: 1 // Drop the elements of a sequence with an index fulfilling a predicate. // Predicate takes an index and decides if an element is dropped. template > ContainerOut drop_by_idx(UnaryPredicate pred, Container&& xs) { internal::check_unary_predicate_for_type(); return keep_by_idx(logical_not(pred), std::forward(xs)); } // API search type: keep_idxs : ([Int], [a]) -> [a] // fwd bind count: 1 // Keep the elements of a sequence with an index present in idxs_to_keep. // keep_idxs([2,5], [1,2,3,4,5,6,7]) == [3,6] template Container keep_idxs(const ContainerIdxs& idxs_to_keep, const Container& xs) { static_assert(std::is_same::value, "Indices must be std::size_t"); auto idxs_left = convert_container>( unique(sort(idxs_to_keep))); Container ys; auto it = internal::get_back_inserter(ys); std::size_t idx = 0; for (const auto& x : xs) { if (!idxs_left.empty() && idxs_left.front() == idx) { idxs_left.pop_front(); *it = x; } ++idx; } return ys; } // API search type: drop_idxs : ([Int], [a]) -> [a] // fwd bind count: 1 // Drop the elements of a sequence with an index present in idxs_to_keep. // drop_idxs([2,5], [1,2,3,4,5,6,7]) == [1,2,4,5,7] template Container drop_idxs(const ContainerIdxs& idxs_to_drop, const Container& xs) { static_assert(std::is_same::value, "Indices must be std::size_t"); auto idxs_left = convert_container>( unique(sort(idxs_to_drop))); Container ys; auto it = internal::get_back_inserter(ys); std::size_t idx = 0; for (const auto& x : xs) { if (idxs_left.empty() || idxs_left.front() != idx) { *it = x; } else { if (!idxs_left.empty()) { idxs_left.pop_front(); } } ++idx; } return ys; } // API search type: drop_idx : (Int, [a]) -> [a] // fwd bind count: 1 // Remove the element at a specific index from a sequence. // drop_idx(2, [1,2,3,4,5,6,7]) == [1,2,4,5,6,7] template Container drop_idx(std::size_t idx, const Container& xs) { return drop_by_idx(is_equal_to(idx), xs); } // API search type: justs : [Maybe a] -> [a] // fwd bind count: 0 // From a Container filled with Maybe the nothings are dropped // and the values inside the justs are returned in a new container. template ::type> ContainerOut justs(const ContainerIn& xs) { typedef typename ContainerIn::value_type::type T; auto justsInMaybes = keep_if(is_just, xs); ContainerOut ys; internal::prepare_container(ys, fplus::size_of_cont(justsInMaybes)); auto itOut = internal::get_back_inserter(ys); std::transform(std::begin(justsInMaybes), std::end(justsInMaybes), itOut, unsafe_get_just); return ys; } // API search type: oks : [Result a b] -> [a] // fwd bind count: 0 // From a Container filled with Result the errors are dropped // and the values inside the ok are returned in a new container. template ::type> ContainerOut oks(const ContainerIn& xs) { typedef typename ContainerIn::value_type::ok_t Ok; typedef typename ContainerIn::value_type::error_t Error; auto oksInResults = keep_if(is_ok, xs); ContainerOut ys; internal::prepare_container(ys, fplus::size_of_cont(oksInResults)); auto itOut = internal::get_back_inserter(ys); std::transform(std::begin(oksInResults), std::end(oksInResults), itOut, unsafe_get_ok); return ys; } // API search type: errors : [Result a b] -> [b] // fwd bind count: 0 // From a Container filled with Result the oks are dropped // and the values inside the errors are returned in a new container. template ::type> ContainerOut errors(const ContainerIn& xs) { typedef typename ContainerIn::value_type::ok_t Ok; typedef typename ContainerIn::value_type::error_t Error; auto errorsInResults = keep_if(is_error, xs); ContainerOut ys; internal::prepare_container(ys, fplus::size_of_cont(errorsInResults)); auto itOut = internal::get_back_inserter(ys); std::transform(std::begin(errorsInResults), std::end(errorsInResults), itOut, unsafe_get_error); return ys; } // API search type: trim_left : (a, [a]) -> [a] // fwd bind count: 1 // Remove elements from the left as long as they equal x. // trim_left('_', "___abc__") == "abc__" // trim_left(0, [0,0,0,5,6,7,8,6,4]) == [5,6,7,8,6,4] template Container trim_left(const T& x, const Container& xs) { return drop_while(is_equal_to(x), xs); } // API search type: trim_token_left : ([a], [a]) -> [a] // fwd bind count: 1 // Remove elements from the left as long as they match token. // trim_token_left([0,1,2], [0,1,2,0,1,2,7,5,9]) == [7,5,9] template Container trim_token_left(const Container& token, const Container& xs) { auto result = xs; while (is_prefix_of(token, result)) { result = get_segment(size_of_cont(token), size_of_cont(result), result); } return result; } // API search type: trim_right_by : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Remove elements from the left as long as p is fulfilled. // trim_right_by(is_even, [0,2,4,5,6,7,8,6,4]) == [0,2,4,5,6,7] template Container trim_right_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return reverse(drop_while(p, reverse(xs))); } // API search type: trim_right : (a, [a]) -> [a] // fwd bind count: 1 // Remove elements from the left as long as they equal x. // trim_right('_', "___abc__") == "___abc" // trim_right(4, [0,2,4,5,6,7,8,4,4]) == [0,2,4,5,6,7,8] template Container trim_right(const T& x, const Container& xs) { return trim_right_by(is_equal_to(x), xs); } // API search type: trim_token_right : ([a], [a]) -> [a] // fwd bind count: 1 // Remove elements from the right as long as they match token. // trim_token_right([0,1,2], [7,5,9,0,1,2,0,1,2]) == [7,5,9] template Container trim_token_right(const Container& token, const Container& xs) { return reverse(trim_token_left(reverse(token), reverse(xs))); } // API search type: trim_by : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Remove elements from the left and right as long as p is fulfilled. // trim_by(is_even, [0,2,4,5,6,7,8,6,4]) == [5,6,7] template Container trim_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return trim_right_by(p, drop_while(p, xs)); } // API search type: trim : (a, [a]) -> [a] // fwd bind count: 1 // Remove elements from the left and right as long as they equal x. // trim('_', "___abc__") == "abc" // trim(0, [0,2,4,5,6,7,8,0,0]) == [2,4,5,6,7,8] template Container trim(const T& x, const Container& xs) { return trim_right(x, trim_left(x, xs)); } // API search type: trim_token : ([a], [a]) -> [a] // fwd bind count: 1 // Remove elements from the left and right as long as they match token. // trim_token([0,1], [0,1,7,8,9,0,1]) == [7,8,9] template Container trim_token(const Container& token, const Container& xs) { return trim_token_right(token, trim_token_left(token, xs)); } // API search type: adjacent_keep_snd_if : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // For each pair of adjacent elements in a source sequence, // evaluate the specified binary predicate. // If the predicate evaluates to false, // the second element of the pair is removed from the result sequence; // otherwise, it is included. // The first element in the source sequence is always included. // Also known as adjacent_filter. template Container adjacent_keep_snd_if(BinaryPredicate p, const Container& xs) { if (is_empty(xs)) { return {}; } internal::check_binary_predicate_for_container(); Container result; auto it = internal::get_back_inserter(result); auto it_in = std::begin(xs); *it = *it_in; while (internal::add_to_iterator(it_in) != std::end(xs)) { if (p(*it_in, *internal::add_to_iterator(it_in))) { *it = *internal::add_to_iterator(it_in); } internal::advance_iterator(it_in, 1); } return result; } // API search type: adjacent_drop_fst_if : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // For each pair of adjacent elements in a source sequence, // evaluate the specified binary predicate. // If the predicate evaluates to true, // the first element of the pair is removed from the result sequence; // otherwise, it is included. // The last element in the source sequence is always included. // Also known as adjacent_remove_if. template Container adjacent_drop_fst_if(BinaryPredicate p, const Container& xs) { if (is_empty(xs)) { return {}; } internal::check_binary_predicate_for_container(); Container result; auto it = internal::get_back_inserter(result); auto it_in = std::begin(xs); while (internal::add_to_iterator(it_in) != std::end(xs)) { if (!internal::invoke(p, *it_in, *internal::add_to_iterator(it_in))) { *it = *it_in; } internal::advance_iterator(it_in, 1); } *it = *it_in; return result; } // API search type: adjacent_drop_snd_if : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // For each pair of adjacent elements in a source sequence, // evaluate the specified binary predicate. // If the predicate evaluates to true, // the second element of the pair is removed from the result sequence; // otherwise, it is included. // The first element in the source sequence is always included. template Container adjacent_drop_snd_if(BinaryPredicate p, const Container& xs) { typedef typename Container::value_type T; const auto not_p = [&p](const T& x, const T& y) -> bool { return !internal::invoke(p, x, y); }; return adjacent_keep_snd_if(not_p, xs); } // API search type: adjacent_keep_fst_if : (((a, a) -> Bool), [a]) -> [a] // fwd bind count: 1 // For each pair of adjacent elements in a source sequence, // evaluate the specified binary predicate. // If the predicate evaluates to false, // the first element of the pair is removed from the result sequence; // otherwise, it is included. // The last element in the source sequence is always included. template Container adjacent_keep_fst_if(BinaryPredicate p, const Container& xs) { typedef typename Container::value_type T; const auto not_p = [&p](const T& x, const T& y) -> bool { return !internal::invoke(p, x, y); }; return adjacent_drop_fst_if(not_p, xs); } } // namespace fplus // // numeric.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // pairs.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // internal/asserts/pairs.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) namespace fplus { namespace internal { struct apply_to_pair_tag { }; struct zip_with_tag { }; struct zip_with_3_tag { }; struct transform_fst_tag { }; struct transform_snd_tag { }; struct inner_product_with_tag { }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function does not take pair.first type as first Parameter."); static_assert(std::is_convertible::value, "Function does not take pair.second type as second Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function does not take elements from first Container as first Parameter."); static_assert(std::is_convertible::value, "Function does not take elements from second Container as second Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 3, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; typedef typename utils::function_traits::template arg<2>::type FIn2; static_assert(std::is_convertible::value, "Function does not take elements from first Container as first Parameter."); static_assert(std::is_convertible::value, "Function does not take elements from second Container as second Parameter."); static_assert(std::is_convertible::value, "Function does not take elements from third Container as third Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 1, "Function must take one parameter."); typedef typename utils::function_traits::template arg<0>::type FIn0; static_assert(std::is_convertible::value, "Function does not take pair.first type as first Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 1, "Function must take one parameter."); typedef typename utils::function_traits::template arg<0>::type FIn0; static_assert(std::is_convertible::value, "Function does not take pair.second type as first Parameter."); }; template struct function_traits_asserts { static_assert(utils::function_traits::arity == 2, "Function must take two parameters."); typedef typename utils::function_traits::template arg<0>::type FIn0; typedef typename utils::function_traits::template arg<1>::type FIn1; static_assert(std::is_convertible::value, "Function does not take elements from first Container as first Parameter."); static_assert(std::is_convertible::value, "Function does not take elements from second Container as second Parameter."); }; } } #include namespace fplus { // API search type: apply_to_pair : (((a, b) -> c), (a, b)) -> c // fwd bind count: 1 // Apply binary function to parts of a pair. template auto apply_to_pair(F f, const std::pair& p) { internal::trigger_static_asserts(); return internal::invoke(f, p.first, p.second); } // API search type: zip_with : (((a, b) -> c), [a], [b]) -> [c] // fwd bind count: 2 // Zip two sequences using a binary function. // zip_with((+), [1, 2, 3], [5, 6]) == [1+5, 2+6] == [6, 8] template >, typename ContainerOut = std::vector> ContainerOut zip_with(F f, const ContainerIn1& xs, const ContainerIn2& ys) { internal::trigger_static_asserts(); ContainerOut result; std::size_t resultSize = std::min(size_of_cont(xs), size_of_cont(ys)); internal::prepare_container(result, resultSize); auto itResult = internal::get_back_inserter(result); auto itXs = std::begin(xs); auto itYs = std::begin(ys); for (std::size_t i = 0; i < resultSize; ++i) { *itResult = internal::invoke(f, *itXs, *itYs); ++itXs; ++itYs; } return result; } // API search type: zip_with_3 : (((a, b, c) -> d), [a], [b], [c]) -> [c] // fwd bind count: 3 // Zip three sequences using a ternary function. // zip_with_3((+), [1, 2, 3], [5, 6], [1, 1]) == [7, 9] template < typename ContainerIn1, typename ContainerIn2, typename ContainerIn3, typename F, typename X = typename ContainerIn1::value_type, typename Y = typename ContainerIn2::value_type, typename Z = typename ContainerIn3::value_type, typename TOut = std::decay_t>, typename ContainerOut = typename std::vector> ContainerOut zip_with_3(F f, const ContainerIn1& xs, const ContainerIn2& ys, const ContainerIn3& zs) { internal::trigger_static_asserts(); static_assert(std::is_same< typename internal::same_cont_new_t::type, typename internal::same_cont_new_t::type>::value, "All three Containers must be of same outer type."); static_assert(std::is_same< typename internal::same_cont_new_t::type, typename internal::same_cont_new_t::type>::value, "All three Containers must be of same outer type."); ContainerOut result; std::size_t resultSize = std::min(size_of_cont(xs), size_of_cont(ys)); internal::prepare_container(result, resultSize); auto itResult = internal::get_back_inserter(result); auto itXs = std::begin(xs); auto itYs = std::begin(ys); auto itZs = std::begin(zs); for (std::size_t i = 0; i < resultSize; ++i) { *itResult = internal::invoke(f, *itXs, *itYs, *itZs); ++itXs; ++itYs; ++itZs; } return result; } // API search type: zip_with_defaults : (((a, b) -> c), a, b, [a], [b]) -> [c] // fwd bind count: 4 // Zip two sequences and using a binary function // and extrapolate the shorter sequence with a default value. // zip_with_defaults((+), 6, 7, [1,2,3], [1,2]) == [2,4,10] // zip_with_defaults((+), 6, 7, [1,2], [1,2,3]) == [2,4,9] template < typename ContainerIn1, typename ContainerIn2, typename F, typename X = typename ContainerIn1::value_type, typename Y = typename ContainerIn2::value_type> auto zip_with_defaults(F f, const X& default_x, const Y& default_y, const ContainerIn1& xs, const ContainerIn2& ys) { internal::trigger_static_asserts(); const auto size_xs = size_of_cont(xs); const auto size_ys = size_of_cont(ys); if (size_xs < size_ys) { const auto extended_xs = append( xs, replicate(size_ys - size_xs, default_x)); return zip_with(f, extended_xs, ys); } else if (size_xs > size_ys) { const auto extended_ys = append( ys, replicate(size_xs - size_ys, default_y)); return zip_with(f, xs, extended_ys); } return zip_with(f, xs, ys); } // API search type: zip : ([a], [b]) -> [(a, b)] // fwd bind count: 1 // Combine two sequences to one sequence of pairs. // zip([1, 2, 3], [5, 6]) == [(1, 5), (2, 6)] template auto zip(const ContainerIn1& xs, const ContainerIn2& ys) { auto MakePair = [](const X& x, const Y& y) { return std::make_pair(x, y); }; return zip_with(MakePair, xs, ys); } // API search type: unzip : [(a, b)] -> ([a], [b]) // fwd bind count: 0 // Split a sequence of pairs into two sequences. // unzip([(1, 5), (2, 6)]) == ([1, 2], [5, 6]) template ::type, typename ContainerOutY = typename internal::same_cont_new_t::type> std::pair unzip(const ContainerIn& pairs) { ContainerOutX firsts; ContainerOutY seconds; internal::prepare_container(firsts, size_of_cont(pairs)); internal::prepare_container(seconds, size_of_cont(pairs)); auto itFirsts = internal::get_back_inserter(firsts); auto itSeconds = internal::get_back_inserter(seconds); for (const auto& pair : pairs) { *itFirsts = pair.first; *itSeconds = pair.second; } return std::make_pair(firsts, seconds); } // API search type: fst : ((a, b)) -> a // fwd bind count: 0 // Return the first element of a pair. // fst((0, 1)) == 0 template X fst(const std::pair& pair) { return pair.first; } // API search type: snd : ((a, b)) -> b // fwd bind count: 0 // Return the second element of a pair. // snd((0, 1)) == 1 template Y snd(const std::pair& pair) { return pair.second; } // API search type: transform_fst : ((a -> c), (a, b)) -> (c, b) // fwd bind count: 1 // Apply a function to the first element of a pair. // transform_fst(square, (4, 5)) == (16, 5) template >> std::pair transform_fst(F f, const std::pair& pair) { internal::trigger_static_asserts(); return std::make_pair(internal::invoke(f, pair.first), pair.second); } // API search type: transform_snd : ((b -> c), (a, b)) -> (a, c) // fwd bind count: 1 // Apply a function to the second element of a pair. // transform_snd(square, (4, 5)) == (4, 25) template >> std::pair transform_snd(F f, const std::pair& pair) { internal::trigger_static_asserts(); return std::make_pair(pair.first, internal::invoke(f, pair.second)); } // API search type: transform_pair : ((a -> c), (b -> d), (a, b)) -> (c, d) // fwd bind count: 2 // Apply functions the both parts of a pair. // transform_pair(square, square, (4, 5)) == (16, 25) template < typename X, typename Y, typename F, typename G, typename ResultFirst = std::decay_t>, typename ResultSecond = std::decay_t>> std::pair transform_pair(F f, G g, const std::pair& pair) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); return std::make_pair(internal::invoke(f, pair.first), internal::invoke(g, pair.second)); } // API search type: swap_pair_elems : (a, b) -> (b, a) // fwd bind count: 0 // Swap the first and the second element of a pair. // swap_pair_elems((3,4)) == (4,3) template std::pair swap_pair_elems(const std::pair& pair) { return std::make_pair(pair.second, pair.first); } // API search type: swap_pairs_elems : [(a, b)] -> [(b, a)] // fwd bind count: 0 // Swap the first and the second element of every pair in a sequence. // swap_pairs_elems([(1,2), (3,4)]) == [(2,1), (4,3)] template auto swap_pairs_elems(const ContainerIn& xs) { return fplus::transform(swap_pair_elems, xs); } // API search type: adjacent_pairs : [a] -> [(a, a)] // fwd bind count: 0 // Split a sequence into pairs of elements. // adjacent_pairs([0,1,2,3,4]) == [(0,1), (2,3)] // Also known as zip_with_next. template >::type> ContainerOut adjacent_pairs(const Container& xs) { typedef typename Container::value_type T; static_assert(std::is_convertible< std::pair, typename ContainerOut::value_type>::value, "ContainerOut can not store pairs of elements from ContainerIn."); ContainerOut result; if (size_of_cont(xs) < 2) return result; const std::size_t out_size = size_of_cont(xs) / 2; internal::prepare_container(result, out_size); auto itOut = internal::get_back_inserter(result); auto it1 = std::begin(xs); auto it2 = it1; internal::advance_iterator(it2, 1); const auto it_source_end = internal::add_to_iterator(std::begin(xs), out_size + out_size); for (;;) { *itOut = std::make_pair(*it1, *it2); internal::advance_iterator(it1, 2); if (it1 == it_source_end) break; internal::advance_iterator(it2, 2); } return result; } // API search type: overlapping_pairs : [a] -> [(a, a)] // fwd bind count: 0 // Zip a sequence with itself shifted one element. // overlapping_pairs([0,1,2,3]) == [(0,1),(1,2),(2,3)] template , -1>::type> ContainerOut overlapping_pairs(const Container& xs) { typedef typename Container::value_type T; static_assert(std::is_convertible< std::pair, typename ContainerOut::value_type>::value, "ContainerOut can not store pairs of elements from ContainerIn."); ContainerOut result; if (size_of_cont(xs) < 2) return result; internal::prepare_container(result, size_of_cont(xs) - 1); auto itOut = internal::get_back_inserter(result); auto it1 = std::begin(xs); auto it2 = it1; internal::advance_iterator(it2, 1); for (; it2 != std::end(xs); ++it1, ++it2) { *itOut = std::make_pair(*it1, *it2); } return result; } // API search type: overlapping_pairs_cyclic : [a] -> [(a, a)] // fwd bind count: 0 // Zip a sequence with itself shifted one element, // finally zipping the last element with the first one. // overlapping_pairs_cyclic([0,1,2,3]) == [(0,1),(1,2),(2,3),(3,0)] template , 0>::type> ContainerOut overlapping_pairs_cyclic(const Container& xs) { typedef typename Container::value_type T; static_assert(std::is_convertible< std::pair, typename ContainerOut::value_type>::value, "ContainerOut can not store pairs of elements from ContainerIn."); ContainerOut result; if (size_of_cont(xs) < 2) return result; internal::prepare_container(result, size_of_cont(xs)); auto itOut = internal::get_back_inserter(result); auto it1 = std::begin(xs); auto it2 = it1; internal::advance_iterator(it2, 1); for (; it2 != std::end(xs); ++it1, ++it2) { *itOut = std::make_pair(*it1, *it2); } *itOut = std::make_pair(*it1, xs.front()); return result; } // API search type: enumerate : [a] -> [(Int, a)] // fwd bind count: 0 // Attach its index to every element of a sequence. // enumerate([6,4,7,6]) == [(0, 6), (1, 4), (2, 7), (3, 6)] template auto enumerate(const Container& xs) { return zip(all_idxs(xs), xs); } // API search type: inner_product_with : (((a, a) -> b), ((b, b) -> b), b, [a], [a]) -> b // fwd bind count: 4 // Calculate the inner product of two sequences using custom operations. // inner_product_with((+), (*), [1, 2, 3], [4, 5, 6]) == [32] template < typename ContainerIn1, typename ContainerIn2, typename OP1, typename OP2, typename Acc, typename X = typename ContainerIn1::value_type, typename Y = typename ContainerIn2::value_type, typename OP2Out = internal::invoke_result_t> auto inner_product_with(OP1 op1, OP2 op2, const Acc& value, const ContainerIn1& xs, const ContainerIn2& ys) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); assert(size_of_cont(xs) == size_of_cont(ys)); return std::inner_product( std::begin(xs), std::end(xs), std::begin(ys), value, op1, op2); } // API search type: inner_product : (a, [a], [a]) -> a // fwd bind count: 2 // Calculate the inner product of two sequences. // inner_product([1, 2, 3], [4, 5, 6]) == [32] template Z inner_product(const Z& value, const ContainerIn1& xs, const ContainerIn2& ys) { assert(size_of_cont(xs) == size_of_cont(ys)); return std::inner_product( std::begin(xs), std::end(xs), std::begin(ys), value); } // API search type: first_mismatch_idx_by : (((a, b) -> Bool), [a], [b]) -> Maybe Int // fwd bind count: 2 // Find the first index at which the two sequences differ // using a binary predicate. // first_mismatch_idx_by((==), [1, 2, 3], [1, 4, 3]) == Just 1 // first_mismatch_idx_by((==), [1, 2, 3], [1, 4]) == Just 1 // first_mismatch_idx_by((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch_idx_by((==), [], [1, 2]) == Nothing template maybe first_mismatch_idx_by(BinaryPredicate p, const ContainerIn1& xs, const ContainerIn2& ys) { auto itXs = std::begin(xs); auto itYs = std::begin(ys); std::size_t minSize = std::min(size_of_cont(xs), size_of_cont(ys)); for (std::size_t i = 0; i < minSize; ++i) { if (!internal::invoke(p, *itXs, *itYs)) { return just(i); } ++itXs; ++itYs; } return nothing(); } // API search type: first_mismatch_by : (((a, b) -> Bool), [a], [b]) -> Maybe (a, b) // fwd bind count: 2 // Find the first pair of elements differing in the two sequences // using a binary predicate. // first_mismatch_by((==), [1, 2, 3], [1, 4, 3]) == Just (2, 4) // first_mismatch_by((==), [1, 2, 3], [1, 4]) == Just (2, 4) // first_mismatch_by((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch_by((==), [], [1, 2]) == Nothing template > maybe first_mismatch_by(BinaryPredicate p, const ContainerIn1& xs, const ContainerIn2& ys) { const auto maybe_idx = first_mismatch_idx_by(p, xs, ys); if (is_nothing(maybe_idx)) { return nothing(); } else { const auto idx = maybe_idx.unsafe_get_just(); return just(std::make_pair( elem_at_idx(idx, xs), elem_at_idx(idx, ys))); } } // API search type: first_mismatch_idx_on : ((a -> b), [a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of elements differing in the two sequences // using a transformer. // first_mismatch_idx_on((mod 2), [1, 2, 3], [3, 5, 3]) == 1 // first_mismatch_idx_on((mod 2), [1, 2, 3], [1, 5]) == 1 // first_mismatch_idx_on((mod 2), [1, 2, 3], [1, 6]) == Nothing // first_mismatch_idx_on((mod 2), [], [1, 2]) == Nothing template > maybe first_mismatch_idx_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_idx_by(is_equal_by(f), xs, ys); } // API search type: first_mismatch_on : ((a -> b), [a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of elements differing in the two sequences // using a transformer. // first_mismatch_on((mod 2), [1, 2, 3], [3, 5, 3]) == Just (2, 5) // first_mismatch_on((mod 2), [1, 2, 3], [1, 5]) == Just (2, 5) // first_mismatch_on((mod 2), [1, 2, 3], [1, 6]) == Nothing // first_mismatch_on((mod 2), [], [1, 2]) == Nothing template > maybe first_mismatch_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_by(is_equal_by(f), xs, ys); } // API search type: first_mismatch_idx : ([a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of elements differing in the two sequences. // first_mismatch_idx((==), [1, 2, 3], [1, 4, 3]) == 1 // first_mismatch_idx((==), [1, 2, 3], [1, 4]) == 1 // first_mismatch_idx((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch_idx((==), [], [1, 2]) == Nothing template maybe first_mismatch_idx( const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_idx_by(std::equal_to(), xs, ys); } // API search type: first_mismatch : ([a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of elements differing in the two sequences // first_mismatch((==), [1, 2, 3], [1, 4, 3]) == Just (2, 4) // first_mismatch((==), [1, 2, 3], [1, 4]) == Just (2, 4) // first_mismatch((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch((==), [], [1, 2]) == Nothing template > maybe first_mismatch(const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_by(std::equal_to(), xs, ys); } // API search type: first_match_idx_by : (((a, b) -> Bool), [a], [b]) -> Maybe Int // fwd bind count: 2 // Find the first index at which the two sequences equal // using a binary predicate. // first_match_idx_by((==), [1, 2, 3], [3, 2, 3]) == Just 1 // first_match_idx_by((==), [], [1, 2]) == Nothing template maybe first_match_idx_by(F f, const ContainerIn1& xs, const ContainerIn2& ys) { auto itXs = std::begin(xs); auto itYs = std::begin(ys); std::size_t minSize = std::min(size_of_cont(xs), size_of_cont(ys)); for (std::size_t i = 0; i < minSize; ++i) { if (internal::invoke(f, *itXs, *itYs)) { return just(i); } ++itXs; ++itYs; } return nothing(); } // API search type: first_match_by : (((a, b) -> Bool), [a], [b]) -> Maybe (a, b) // fwd bind count: 2 // Find the first pair of equal elements in the two sequences // using a binary predicate. // first_match_by((==), [1, 2, 3], [3, 2, 3]) == Just (2, 2) // first_match_by((==), [], [1, 2]) == Nothing template > maybe first_match_by(F f, const ContainerIn1& xs, const ContainerIn2& ys) { const auto maybe_idx = first_match_idx_by(f, xs, ys); if (is_nothing(maybe_idx)) { return nothing(); } else { const auto idx = maybe_idx.unsafe_get_just(); return just(std::make_pair( elem_at_idx(idx, xs), elem_at_idx(idx, ys))); } } // API search type: first_match_idx_on : ((a -> b), [a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of equal elements in the two sequences // using a transformer. // first_match_idx_on((mod 2), [1, 2, 3], [2, 4, 3]) == 1 // first_match_idx_on((mod 2), [], [1, 2]) == Nothing template maybe first_match_idx_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_idx_by(is_equal_by(f), xs, ys); } // API search type: first_match_on : ((a -> b), [a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of equal elements in the two sequences // using a transformer. // first_match_on((mod 2), [1, 2, 3], [2, 4, 3]) == Just (2, 4) // first_match_on((mod 2), [], [1, 2]) == Nothing template > maybe first_match_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_by(is_equal_by(f), xs, ys); } // API search type: first_match_idx : ([a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of equal elements in the two sequences. // first_match_idx((==), [1, 2, 3], [5, 2, 3]) == 1 // first_match_idx((==), [], [1, 2]) == Nothing template maybe first_match_idx( const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_idx_by(std::equal_to(), xs, ys); } // API search type: first_match : ([a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of equal elements in the two sequences. // first_match((==), [1, 2, 3], [5, 2, 3]) == Just (2, 2) // first_match((==), [], [1, 2]) == Nothing template > maybe first_match(const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_by(std::equal_to(), xs, ys); } } // namespace fplus #include #include #include #include #include #include namespace fplus { // API search type: is_in_interval : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in [low, high), i.e. left-closed and right-open. template bool is_in_interval(const T& low, const T& high, const T& x) { return (low <= x) && (x < high); } // API search type: is_in_interval_around : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in [center - radius, center + radius), // i.e. left-closed and right-open. template bool is_in_interval_around(const T& radius, const T& center, const T& x) { return is_in_interval(center - radius, center + radius, x); } // API search type: is_in_open_interval : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in (low, high), i.e. left-open and right-open. template bool is_in_open_interval(const T& low, const T& high, const T& x) { return (low < x) && (x < high); } // API search type: is_in_open_interval_around : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in (center - radius, center + radius), // i.e. left-open and right-open. template bool is_in_open_interval_around(const T& radius, const T& center, const T& x) { return is_in_open_interval(center - radius, center + radius, x); } // API search type: is_in_closed_interval : (a, a, a) -> Bool // fwd bind count: 2 // Checks if x is in [low, high], i.e. left-closed and right-closed. template bool is_in_closed_interval(const T& low, const T& high, const T& x) { return (low <= x) && (x <= high); } // API search type: is_in_closed_interval_around : (a, a, a) -> Bool // Checks if x is in [center - radius, center + radius], // i.e. left-closed and right-closed. template bool is_in_closed_interval_around(const T& radius, const T& center, const T& x) { return is_in_closed_interval(center - radius, center + radius, x); } // API search type: reference_interval : (Float, Float, Float, Float, Float) -> Float // fwd bind count: 4 // Linearly projects a value // from [old_low, old_high] into [new_low, new_high]. // Does not clamp. // reference_interval(2, 6, 0, 4, 3) == 5 // reference_interval(2, 10, 0, 4, 3) == 8 // reference_interval(2, 6, 0, 4, -1) == 1 // reference_interval(2, 10, 0, 4, -1) == 0 template T reference_interval(const T& new_low, const T& new_high, const T& old_low, const T& old_high, const T& x) { const T scale = (new_high - new_low) / (old_high - old_low); return scale * (x - old_low) + new_low; } // API search type: clamp : (a, a, a) -> a // fwd bind count: 2 // Puts value into [low, high], i.e. left-closed and right-closed. template T clamp(const T& low, const T& high, const T& x) { return std::max(low, std::min(high, x)); } // API search type: is_negative : a -> Bool // fwd bind count: 0 // Checks if x < 0. template bool is_negative(X x) { return x < 0; } // API search type: is_positive : a -> Bool // fwd bind count: 0 // Checks if x is not negative. template bool is_positive(X x) { return !is_negative(x); } // API search type: is_even : Int -> Bool // fwd bind count: 0 // Checks if x is even. template bool is_even(X x) { static_assert(std::is_integral::value, "type must be integral"); return x % 2 == 0; } // API search type: is_odd : Int -> Bool // fwd bind count: 0 // Checks if x is odd. template bool is_odd(X x) { static_assert(std::is_integral::value, "type must be integral"); return x % 1 == 0; } namespace internal { template typename std::enable_if::value, X>::type abs_helper(X x) { return x; } template typename std::enable_if::value, X>::type abs_helper(X x) { return std::abs(x); } } // API search type: abs : a -> a // fwd bind count: 0 // Returns the absolute (always non-negative) value of x. template X abs(X x) { return internal::abs_helper(x); } // API search type: abs_diff : (a, a) -> a // fwd bind count: 1 // Returns the absolute difference of two values. template X abs_diff(X a, X b) { return a > b ? a - b : b - a; } // API search type: square : a -> a // fwd bind count: 0 // Returns the square (x*x) of a value x. template X square(X x) { return x * x; } // API search type: cube : a -> a // fwd bind count: 0 // Returns the cube (x*x*x) of a value x. template X cube(X x) { return x * x * x; } // API search type: sign : a -> Int // fwd bind count: 0 // Returns -1 for negative values, 1 otherwise. // sign(-3) == -1 // sign(0) == 1 // sign(16) == 1 template int sign(X x) { return is_negative(x) ? -1 : 1; } // API search type: sign_with_zero : a -> Int // fwd bind count: 0 // Returns -1 for negative values, 0 for zero, 1 for positive values. // sign_with_zero(-3) == -1 // sign_with_zero(0) == 0 // sign_with_zero(16) == 1 template int sign_with_zero(X x) { return x == 0 ? 0 : sign(x); } // API search type: integral_cast_throw : Int -> Int // fwd bind count: 0 // Converts one integer type into another. // Throws an std::underflow_error or std::overflow_error // if the value does not fit into the destination type. template Out integral_cast_throw(X x) { #ifdef _MSC_VER __pragma(warning(push)) __pragma(warning(disable:4127)) #endif static_assert(std::is_integral::value, "type must be integral"); static_assert(std::is_integral::value, "type must be integral"); if (std::is_signed::value && std::is_signed::value) { if (static_cast(x) < static_cast(std::numeric_limits::lowest())) { throw std::underflow_error(""); } if (static_cast(x) > static_cast(std::numeric_limits::max())) { throw std::overflow_error(""); } return static_cast(x); } else if (!std::is_signed::value && !std::is_signed::value) { if (static_cast(x) < static_cast(std::numeric_limits::lowest())) { throw std::underflow_error(""); } if (static_cast(x) > static_cast(std::numeric_limits::max())) { throw std::overflow_error(""); } return static_cast(x); } else if (std::is_signed::value && !std::is_signed::value) { if (x < 0) return 0; if (static_cast(x) > static_cast(std::numeric_limits::max())) { throw std::overflow_error(""); } return static_cast(x); } else if (!std::is_signed::value && std::is_signed::value) { if (static_cast(x) > static_cast(std::numeric_limits::max())) { throw std::overflow_error(""); } return static_cast(x); } else { assert(false); return static_cast(x); } #ifdef _MSC_VER __pragma(warning(pop)) #endif } // API search type: integral_cast_clamp : Int -> Int // fwd bind count: 0 // Converts one integer type into another. // If the value does not fit into the destination type, // the nearest possible value is used. // Also known as saturate_cast. template Out integral_cast_clamp(X x) { static_assert(std::is_integral::value, "type must be integral"); static_assert(std::is_integral::value, "type must be integral"); if (std::is_signed::value && std::is_signed::value) { if (static_cast(x) < static_cast(std::numeric_limits::lowest())) { return std::numeric_limits::lowest(); } if (static_cast(x) > static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(x); } else if (!std::is_signed::value && !std::is_signed::value) { if (static_cast(x) < static_cast(std::numeric_limits::lowest())) { return std::numeric_limits::lowest(); } if (static_cast(x) > static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(x); } else if (std::is_signed::value && !std::is_signed::value) { if (x < 0) return 0; if (static_cast(x) > static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(x); } else if (!std::is_signed::value && std::is_signed::value) { if (static_cast(x) > static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(x); } else { assert(false); return static_cast(x); } } // API search type: round : a -> Int // fwd bind count: 0 // Converts a value to the nearest integer. template Out round(X x) { static_assert(!std::is_integral::value, "type must be non-integral"); static_assert(std::is_integral::value, "type must be integral"); if (static_cast(x) < static_cast(std::numeric_limits::lowest())) return std::numeric_limits::lowest(); if (static_cast(x) > static_cast(std::numeric_limits::max())) return std::numeric_limits::max(); if (is_negative(x)) x -= 1; return static_cast(x + 0.5); } // API search type: floor : a -> b // fwd bind count: 0 // Converts a value to the nearest smaller integer. template Out floor(X x) { static_assert(!std::is_integral::value, "type must be non-integral"); static_assert(std::is_integral::value, "type must be integral"); if (is_negative(x)) x -= 1; return static_cast(x); } // API search type: floor_to_int_mult : (Int, Int) -> Int // fwd bind count: 1 // Rounds an integer down to the nearest smaller or equal multiple of n. // n may not be zero. template X floor_to_int_mult(X n, X x) { static_assert(std::is_integral::value, "type must be integral"); assert(n != 0); if (is_negative(n)) n = abs(n); if (is_negative(x) && n != 1) x = static_cast(x - 1); return static_cast((x / n) * n); } // API search type: ceil_to_int_mult : (Int, Int) -> Int // fwd bind count: 1 // Rounds an integer up to the nearest greater or equal multiple of n. // n may not be zero. template X ceil_to_int_mult(X n, X x) { return floor_to_int_mult(n, static_cast(x + abs(n) - 1)); } // API search type: ceil : a -> b // fwd bind count: 0 // Converts a value to the nearest greater integer. template Out ceil(X x) { static_assert(!std::is_integral::value, "type must be non-integral"); static_assert(std::is_integral::value, "type must be integral"); return floor(x) + 1; } // API search type: int_power : (Int, Int) -> Int // fwd bind count: 1 // integer power, only exponents >= 0 template X int_power(X base, X exp) { static_assert(std::is_integral::value, "type must be unsigned integral"); assert(!is_negative(exp)); if (exp == 0) return 1; if (exp == 1) return base; return base * int_power(base, exp - 1); } namespace internal { // minimum of x values after transformation // (has an overload for non-POD types) // min_on(mod2, 4, 3) == 4 // min_on(mod7, 3, 5, 7, 3) == 7 template auto helper_min_on(F f, const FirstT& first, const FIn&... v) -> typename std::common_type::type { using rettype = typename std::common_type::type; using f_rettype = std::decay_t>; rettype result = first; f_rettype result_trans = internal::invoke(f, first); f_rettype v_trans; unused(result_trans); unused(v_trans); (void)std::initializer_list{ ((v_trans = internal::invoke(f, v), v_trans < result_trans) ? (result = static_cast(v), result_trans = v_trans, 0) : 0)...}; return result; } template struct helper_min_on_t { helper_min_on_t(F _f) : f(_f) {} template auto operator()(T&& x, Ts&&... xs) -> typename std::common_type::type { return helper_min_on(std::forward(f), std::forward(x), std::forward(xs)...); } private: F f; }; } // API search type: min_on : ((a -> b), a, a) -> a // minimum of x values after transformation (curried version) // min_on(mod2)(4, 3) == 4 // min_on(mod7)(3, 5, 7, 3) == 7 template auto min_on(F f) -> internal::helper_min_on_t { return internal::helper_min_on_t{f}; } // API search type: min_2_on : ((a -> b), a, a) -> a // fwd bind count: 2 // minimum of 2 values after transformation // min_2_on(mod2, 4, 3) == 4 template T min_2_on(F f, const T& x, const T& y) { return internal::invoke(f, y) < internal::invoke(f, x) ? y : x; } namespace internal { // maximum of x values after transformation // (has an overload for non-POD types) // max_on(mod2, 4, 3) == 3 // max_on(mod7, 3, 5, 7, 3) == 5 template auto helper_max_on(F f, const FirstT& first, const FIn&... v) -> typename std::common_type::type { using rettype = typename std::common_type::type; using f_rettype = decltype(f(first)); rettype result = first; f_rettype result_trans = internal::invoke(f, first); f_rettype v_trans; unused(result_trans); unused(v_trans); (void)std::initializer_list{ ((v_trans = internal::invoke(f, v), v_trans > result_trans) ? (result = static_cast(v), result_trans = v_trans, 0) : 0)...}; return result; } template struct helper_max_on_t { helper_max_on_t(F _f) : f(_f) {} template auto operator()(T&& x, Ts&&... xs) -> typename std::common_type::type { return helper_max_on(std::forward(f), std::forward(x), std::forward(xs)...); } private: F f; }; } // API search type: max_on : (a -> b) -> ((a, a) -> a) // maximum of x values after transformation (curried version) // (has an overload for non POD types) // max_on(mod2)(4, 3) == 3 // max_on(mod7)(3, 5, 7, 3) == 5 template auto max_on(F f) -> internal::helper_max_on_t { return internal::helper_max_on_t{f}; } // API search type: max_2_on : ((a -> b), a, a) -> a // fwd bind count: 2 // maximum of 2 values after transformation // max_2_on(mod2, 4, 3) == 3 template T max_2_on(F f, const T& x, const T& y) { return internal::invoke(f, y) > internal::invoke(f, x) ? y : x; } // API search type: min : (a, a) -> a // Minimum of x number of values // min(4, 3) == 3 // min(4, 3, 6, 2, 3) == 2 template auto min(const U& u, const V&... v) -> typename std::common_type::type { using rettype = typename std::common_type::type; rettype result = static_cast(u); (void)std::initializer_list{((v < result) ? (result = static_cast(v), 0) : 0)...}; return result; } // API search type: min_2 : (a, a) -> a // fwd bind count: 1 // minimum of 2 values // min_2(4, 3) == 3 template T min_2(const T& x, const T& y) { return y < x ? y : x; } // API search type: max : (a, a) -> a // Maximum of x number of values. // max(4, 3) == 4 // max(4, 3, 6, 2, 3) == 6 template auto max(const U& u, const V&... v) -> typename std::common_type::type { using rettype = typename std::common_type::type; rettype result = static_cast(u); (void)std::initializer_list{((v > result) ? (result = static_cast(v), 0) : 0)...}; return result; } // API search type: max_2 : (a, a) -> a // fwd bind count: 1 // maximum of 2 values // max_2(4, 3) == 4 template T max_2(const T& x, const T& y) { return y > x ? y : x; } namespace internal { template typename std::enable_if::value, X>::type cyclic_value_helper_mod(X x, X y) { return std::fmod(x, y); } template typename std::enable_if::value, X>::type cyclic_value_helper_mod(X x, X y) { return x % y; } } // API search type: cyclic_value : a -> (a -> a) // Modulo for floating point values. // circumfence must be > 0 // cyclic_value(8)(3) == 3 // cyclic_value(8)(11) == 3 // cyclic_value(8)(19) == 3 // cyclic_value(8)(-2) == 6 // cyclic_value(8)(-5) == 3 // cyclic_value(8)(-13) == 3 // Can be useful to normalize an angle into [0, 360] // For positive values it behaves like std::fmod with flipped arguments. template std::function cyclic_value(X circumfence) { assert(circumfence > 0); return [circumfence](X x) -> X { if (sign(x) < 0) return circumfence - internal::cyclic_value_helper_mod( abs(x), abs(circumfence)); else return internal::cyclic_value_helper_mod( abs(x), abs(circumfence)); }; } // API search type: cyclic_difference : a -> ((a, a) -> a) // Returns the distance the first value has to advance forward on a circle // to reach the second value. // circumfence must be > 0 // cyclic_difference(100)(5, 2) == 3 // cyclic_difference(100)(2, 5) == 97 // cyclic_difference(100)(3, -2) == 5 // cyclic_difference(100)(-2, 3) == 95 // cyclic_difference(100)(90, 10) == 80 // cyclic_difference(100)(10, 90) == 20 template std::function cyclic_difference(X circumfence) { assert(circumfence > 0); return [circumfence](X a, X b) -> X { auto cyclic_value_f = cyclic_value(circumfence); const auto c_v_a = cyclic_value_f(a); const auto c_v_b = cyclic_value_f(b); return c_v_a > c_v_b ? c_v_a - c_v_b : circumfence + c_v_a - c_v_b; }; } // API search type: cyclic_shortest_difference : a -> ((a, a) -> a) // Returns displacement (shortest way) the first value has to move on a circle // to reach the second value. // circumfence must be > 0 // cyclic_shortest_difference(100)(5, 2) == 3 // cyclic_shortest_difference(100)(2, 5) == -3 // cyclic_shortest_difference(100)(3, -2) == 5 // cyclic_shortest_difference(100)(-2, 3) == -5 // cyclic_shortest_difference(100)(90, 10) == -20 // cyclic_shortest_difference(100)(10, 90) == 20 template std::function cyclic_shortest_difference(X circumfence) { assert(circumfence > 0); return [circumfence](X a, X b) -> X { auto diff_func = cyclic_difference(circumfence); auto a_minus_b = diff_func(a, b); auto b_minus_a = diff_func(b, a); return a_minus_b <= b_minus_a ? a_minus_b : -b_minus_a; }; } // API search type: cyclic_distance : a -> ((a, a) -> a) // Returns distance (shortest way) the first value has to move on a circle // to reach the second value. // circumfence must be > 0 // cyclic_distance(100)(2, 5) == 3 // cyclic_distance(100)(5, 2) == 3 // cyclic_distance(100)(-2, 3) == 5 // cyclic_distance(100)(3, -2) == 5 // cyclic_distance(100)(10, 90) == 20 // cyclic_distance(100)(90, 10) == 20 // Can be useful to calculate the difference of two angles; template std::function cyclic_distance(X circumfence) { assert(circumfence > 0); return [circumfence](X a, X b) -> X { auto diff_func = cyclic_difference(circumfence); auto a_minus_b = diff_func(a, b); auto b_minus_a = diff_func(b, a); return a_minus_b <= b_minus_a ? a_minus_b : b_minus_a; }; } // API search type: pi : () -> Float // Pi. constexpr inline double pi() { return 3.14159265358979323846; } // API search type: deg_to_rad : Float -> Float // fwd bind count: 0 // converts degrees to radians template T deg_to_rad(T x) { static_assert(std::is_floating_point::value, "Please use a floating-point type."); return static_cast(x * pi() / 180.0); } // API search type: rad_to_deg : Float -> Float // fwd bind count: 0 // converts radians to degrees template T rad_to_deg(T x) { static_assert(std::is_floating_point::value, "Please use a floating-point type."); return static_cast(x * 180.0 / pi()); } namespace internal { template Container normalize_min_max(internal::reuse_container_t, const T& lower, const T& upper, Container&& xs) { assert(size_of_cont(xs) != 0); assert(lower <= upper); const auto minmax_it_p = std::minmax_element(std::begin(xs), std::end(xs)); const T x_min = *minmax_it_p.first; const T x_max = *minmax_it_p.second; const auto f = [&](const T& x) -> T { return lower + (upper - lower) * (x - x_min) / (x_max - x_min); }; std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); return std::forward(xs); } template Container normalize_min_max(internal::create_new_container_t, const T& lower, const T& upper, const Container& xs) { auto ys = xs; return normalize_min_max(internal::reuse_container_t(), lower, upper, std::move(ys)); } } // namespace internal // API search type: normalize_min_max : (a, a, [a]) -> [a] // fwd bind count: 2 // Linearly scales the values into the given interval. // normalize_min_max(0, 10, [1, 3, 6]) == [0, 4, 10] // It is recommended to convert integers to double beforehand. template ::value_type> auto normalize_min_max(const T& lower, const T& upper, Container&& xs) { return internal::normalize_min_max(internal::can_reuse_v{}, lower, upper, std::forward(xs)); } namespace internal { template Container normalize_mean_stddev(internal::reuse_container_t, const T& mean, const T& stddev, Container&& xs) { assert(size_of_cont(xs) != 0); const auto mean_and_stddev = fplus::mean_stddev(xs); const auto f = [&](const T& x) -> T { return mean + stddev * (x - mean_and_stddev.first) / mean_and_stddev.second; }; std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); return std::forward(xs); } template Container normalize_mean_stddev(internal::create_new_container_t, const T& mean, const T& stddev, const Container& xs) { auto ys = xs; return normalize_mean_stddev(internal::reuse_container_t(), mean, stddev, std::move(ys)); } } // namespace internal // API search type: normalize_mean_stddev : (a, a, [a]) -> [a] // fwd bind count: 2 // Linearly scales the values // to match the given mean and population standard deviation. // normalize_mean_stddev(3, 2, [7, 8]) == [1, 5] template ::value_type> auto normalize_mean_stddev( const T& mean, const T& stddev, Container&& xs) { return internal::normalize_mean_stddev(internal::can_reuse_v{}, mean, stddev, std::forward(xs)); } // API search type: standardize : [a] -> [a] // fwd bind count: 0 // Linearly scales the values to zero mean and population standard deviation 1. // standardize([7, 8]) == [-1, 1] template auto standardize(Container&& xs) { typedef typename internal::remove_const_and_ref_t::value_type T; T mean(0); T stddev(1); return normalize_mean_stddev(mean, stddev, std::forward(xs)); } // API search type: add_to : a -> (a -> a) // Provide a function adding to a given constant. // add_to(3)(2) == 5 template std::function add_to(const X& x) { return [x](X y) -> X { return x + y; }; } // API search type: subtract_from : a -> (a -> a) // Provide a function subtracting from a given constant. // subtract_from(3)(2) == 1 template std::function subtract_from(const X& x) { return [x](X y) -> X { return x - y; }; } // API search type: subtract : a -> (a -> a) // Provide a function subtracting a given constant. // subtract(2)(3) == 1 template std::function subtract(const X& x) { return [x](X y) -> X { return y - x; }; } // API search type: multiply_with : a -> (a -> a) // Provide a function multiplying with a given constant. // multiply_with(3)(2) == 6 template std::function multiply_with(const X& x) { return [x](X y) -> X { return y * x; }; } // API search type: divide_by : a -> (a -> a) // Provide a function dividing by a given constant. // divide_by(2)(6) == 3 template std::function divide_by(const X& x) { return [x](X y) -> X { return y / x; }; } // API search type: histogram_using_intervals : ([(a, a)], [a]) -> [((a, a), Int)] // fwd bind count: 1 // Generate a histogram of a sequence with given bins. // histogram_using_intervals([(0,4), (4,5), (6,8)], [0,1,4,5,6,7,8,9]) == // [((0, 4), 2), ((4, 5), 1), ((6, 8), 2)] template >, typename T = typename ContainerIn::value_type> ContainerOut histogram_using_intervals( const ContainerIntervals& intervals, const ContainerIn& xs) { ContainerOut bins; internal::prepare_container(bins, size_of_cont(intervals)); auto itOut = internal::get_back_inserter(bins); for (const auto& interval : intervals) { *itOut = std::make_pair(interval, 0); } for (const auto& x : xs) { for (auto& bin : bins) { if (x >= bin.first.first && x < bin.first.second) { ++bin.second; } } } return bins; } // API search type: generate_consecutive_intervals : (a, a, a) -> [(a, a)] // fwd bind count: 2 // Return intervals of a given size adjacent to each other // generate_consecutive_intervals(0, 2, 4) == [(0,2), (2,4), (4,6), (6,8)] template std::vector> generate_consecutive_intervals( const T& first_lower_bound, const T& step, std::size_t count) { const auto count_as_T = static_cast(count); return zip( numbers_step( first_lower_bound, first_lower_bound + count_as_T * step, step), numbers_step( first_lower_bound + step, first_lower_bound + step + count_as_T * step, step)); } // API search type: histogram : (a, a, a, [a]) -> [((a, a), Int)] // fwd bind count: 3 // Calculate the histogram of a sequence using a given bin width. // histogram(1, 2, 4, [0,1,4,5,7,8,9]) == [(1, 2), (3, 0), (5, 2), (7, 1)] template >, typename T = typename ContainerIn::value_type> ContainerOut histogram( const T& first_center, const T& bin_width, std::size_t count, const ContainerIn& xs) { const auto interval_histogram = histogram_using_intervals( generate_consecutive_intervals( first_center - bin_width / 2, bin_width, count), xs); assert(size_of_cont(interval_histogram) == count); ContainerOut histo; internal::prepare_container(histo, count); auto itOut = internal::get_back_inserter(histo); for (const auto& bin : interval_histogram) { const auto current_center = (bin.first.first + bin.first.second) / 2; *itOut = std::make_pair(current_center, bin.second); } return histo; } // API search type: modulo_chain : ([Int], Int) -> [Int] // fwd bind count: 1 // For every factor (value % factor) is pushed into the result, // and value is divided by this factor for the next iteration. // Can be useful to convert a time in seconds // into hours, minutes and seconds and similar calculations. // modulo_chain([24, 60, 60], 7223) == [0, 2, 0, 23] template std::vector modulo_chain(const std::vector& factors, T val) { std::vector result; result.reserve(factors.size()); const auto factors_reversed = reverse(factors); for (const auto& factor : factors_reversed) { result.push_back(val % factor); val /= factor; } result.push_back(val); return reverse(result); } // API search type: line_equation : ((Float, Float), (Float, Float), Float) -> Float // fwd bind count: 2 // Can be used to interpolate and to extrapolate // based on two given two-dimensional points (x, y). // Using slope, return NaN if x_1 == x_2. // line_equation((0.0, 0.0), (2.0, 1.0), 3.0) == 1.5 // line_equation((-1.0, 1.0), (-2.0, 4.0), 0.0) == -2.0 template T line_equation(const std::pair& a, const std::pair& b, T x) { static_assert(std::is_floating_point::value, "Please use a floating-point type."); const double m = (b.second - a.second) / (b.first - a.first); return m * x + a.second - m * a.first; } } // namespace fplus namespace fplus { // API search type: generate : ((() -> a), Int) -> [a] // Grab values from executing a nullary function // to generate a sequence of amount values. // generate(f, 3) == [f(), f(), f()] // Can for example be used to generate a list of random numbers. template ContainerOut generate(F f, std::size_t amount) { internal::trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, amount); auto it = internal::get_back_inserter(ys); for (std::size_t i = 0; i < amount; ++i) { *it = internal::invoke(f); } return ys; } // API search type: generate_by_idx : ((Int -> a), Int) -> [a] // fwd bind count: 1 // Grab values from executing a unary function with an index // to generate a sequence of amount values. // generate_by_idx(f, 3) == [f(0), f(1), f(2)] template ContainerOut generate_by_idx(F f, std::size_t amount) { internal:: trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, amount); auto it = internal::get_back_inserter(ys); for (std::size_t i = 0; i < amount; ++i) { *it = internal::invoke(f, i); } return ys; } // API search type: repeat : (Int, [a]) -> [a] // fwd bind count: 1 // Create a sequence containing xs concatenated n times. // repeat(3, [1, 2]) == [1, 2, 1, 2, 1, 2] template Container repeat(std::size_t n, const Container& xs) { std::vector xss(n, xs); return concat(xss); } // API search type: infixes : (Int, [a]) -> [[a]] // fwd bind count: 1 // Return als possible infixed of xs with a given length. // infixes(3, [1,2,3,4,5,6]) == [[1,2,3], [2,3,4], [3,4,5], [4,5,6]] // length must be > 0 template > ContainerOut infixes(std::size_t length, const ContainerIn& xs) { assert(length > 0); static_assert(std::is_convertible::value, "ContainerOut can not take values of type ContainerIn as elements."); ContainerOut result; if (size_of_cont(xs) < length) return result; internal::prepare_container(result, size_of_cont(xs) - length); auto itOut = internal::get_back_inserter(result); for (std::size_t idx = 0; idx <= size_of_cont(xs) - length; ++idx) { *itOut = get_segment(idx, idx + length, xs); } return result; } // API search type: carthesian_product_with_where : (((a, b) -> c), ((a -> b), Bool), [a], [b]) -> [c] // fwd bind count: 3 // carthesian_product_with_where(make_pair, always(true), "ABC", "XY") // == [(A,X),(A,Y),(B,X),(B,Y),(C,X),(C,Y)] // same as (in Haskell): // [ f x y | x <- xs, y <- ys, pred x y ] // same as (in pseudo SQL): // SELECT f(xs.x, ys.y) // FROM xs, ys // WHERE pred(xs.x, ys.y); template auto carthesian_product_with_where(F f, Pred pred, const Container1& xs, const Container2& ys) { using X = typename Container1::value_type; using Y = typename Container2::value_type; using FOut = internal::invoke_result_t; using ContainerOut = std::vector>; ContainerOut result; auto itOut = internal::get_back_inserter(result); for (const auto& x : xs) { for (const auto& y : ys) { if (internal::invoke(pred, x, y)) { itOut = f(x, y); } } } return result; } // API search type: carthesian_product_with : (((a, b) -> c), [a], [b]) -> [c] // fwd bind count: 2 // carthesian_product_with(make_pair, "ABC", "XY") // == [(A,X),(A,Y),(B,X),(B,Y),(C,X),(C,Y)] // same as (in Haskell): // [ f x y | x <- xs, y <- ys ] // same as (in pseudo SQL): // SELECT f(xs.x, ys.y) // FROM xs, ys; template auto carthesian_product_with(F f, const Container1& xs, const Container2& ys) { auto always_true_x_y = [](const auto&, const auto&) { return true; }; return carthesian_product_with_where(f, always_true_x_y, xs, ys); } // API search type: carthesian_product_where : (((a, b) -> Bool), [a], [b]) -> [(a, b)] // fwd bind count: 2 // carthesian_product_where(always(true), "ABC", "XY") // == [(A,X),(A,Y),(B,X),(B,Y),(C,X),(C,Y)] // same as (in Haskell): // [ (x, y) | x <- xs, y <- ys, pred x y ] // same as (in pseudo SQL): // SELECT (xs.x, ys.y) // FROM xs, ys // WHERE pred(xs.x, ys.y); template auto carthesian_product_where(Pred pred, const Container1& xs, const Container2& ys) { auto make_res_pair = [](const auto& x, const auto& y) { return std::make_pair(x, y); }; return carthesian_product_with_where(make_res_pair, pred, xs, ys); } // API search type: carthesian_product : ([a], [b]) -> [(a, b)] // fwd bind count: 1 // carthesian_product("ABC", "XY") // == [(A,X),(A,Y),(B,X),(B,Y),(C,X),(C,Y)] // same as (in Haskell): // [ (x, y) | x <- xs, y <- ys ] // same as (in pseudo SQL): // SELECT (xs.x, ys.y) // FROM xs, ys; template auto carthesian_product(const Container1& xs, const Container2& ys) { auto make_res_pair = [](const auto& x, const auto& y) { return std::make_pair(x, y); }; auto always_true_x_y = [](const auto&, const auto&) { return true; }; return carthesian_product_with_where( make_res_pair, always_true_x_y, xs, ys); } namespace internal { // productN :: Int -> [a] -> [[a]] // productN n = foldr go [[]] . replicate n // where go elems acc = [x:xs | x <- elems, xs <- acc] template std::vector> helper_carthesian_product_n_idxs (std::size_t power, const std::vector& xs) { static_assert(std::is_same::value, "T must be std::size_t"); typedef std::vector Vec; typedef std::vector VecVec; if (power == 0) return VecVec(); auto go = [](const Vec& elems, const VecVec& acc) { VecVec result; for (const T& x : elems) { for (const Vec& tail : acc) { result.push_back(append(Vec(1, x), tail)); } } return result; }; return fold_right(go, VecVec(1), replicate(power, xs)); } } // API search type: carthesian_product_n : (Int, [a]) -> [[a]] // fwd bind count: 1 // Returns the product set with a given power. // carthesian_product_n(2, "ABCD") // == AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD template > ContainerOut carthesian_product_n(std::size_t power, const ContainerIn& xs_in) { if (power == 0) return ContainerOut(1); std::vector xs = convert_container>(xs_in); auto idxs = all_idxs(xs); auto result_idxss = internal::helper_carthesian_product_n_idxs(power, idxs); typedef typename ContainerOut::value_type ContainerOutInner; auto to_result_cont = [&](const std::vector& indices) { return convert_container_and_elems( elems_at_idxs(indices, xs)); }; return transform(to_result_cont, result_idxss); } // API search type: permutations : (Int, [a]) -> [[a]] // fwd bind count: 1 // Generate all possible permutations with a given power. // permutations(2, "ABCD") == AB AC AD BA BC BD CA CB CD DA DB DC template > ContainerOut permutations(std::size_t power, const ContainerIn& xs_in) { if (power == 0) return ContainerOut(1); std::vector xs = convert_container>(xs_in); auto idxs = all_idxs(xs); typedef std::vector idx_vec; auto result_idxss = keep_if(all_unique, internal::helper_carthesian_product_n_idxs(power, idxs)); typedef typename ContainerOut::value_type ContainerOutInner; auto to_result_cont = [&](const std::vector& indices) { return convert_container_and_elems( elems_at_idxs(indices, xs)); }; return transform(to_result_cont, result_idxss); } // API search type: combinations : (Int, [a]) -> [[a]] // fwd bind count: 1 // Generate all possible combinations with a given power. // combinations(2, "ABCD") == AB AC AD BC BD CD template > ContainerOut combinations(std::size_t power, const ContainerIn& xs_in) { if (power == 0) return ContainerOut(1); std::vector xs = convert_container>(xs_in); auto idxs = all_idxs(xs); typedef std::vector idx_vec; auto result_idxss = keep_if(is_strictly_sorted, internal::helper_carthesian_product_n_idxs(power, idxs)); typedef typename ContainerOut::value_type ContainerOutInner; auto to_result_cont = [&](const std::vector& indices) { return convert_container_and_elems( elems_at_idxs(indices, xs)); }; return transform(to_result_cont, result_idxss); } // API search type: combinations_with_replacement : (Int, [a]) -> [[a]] // fwd bind count: 1 // Generate all possible combinations using replacement with a given power. // combinations_with_replacement(2, "ABCD") == AA AB AC AD BB BC BD CC CD DD template > ContainerOut combinations_with_replacement(std::size_t power, const ContainerIn& xs_in) { if (power == 0) return ContainerOut(1); std::vector xs = convert_container>(xs_in); auto idxs = all_idxs(xs); typedef std::vector idx_vec; auto result_idxss = keep_if(is_sorted, internal::helper_carthesian_product_n_idxs(power, idxs)); typedef typename ContainerOut::value_type ContainerOutInner; auto to_result_cont = [&](const std::vector& indices) { return convert_container_and_elems( elems_at_idxs(indices, xs)); }; return transform(to_result_cont, result_idxss); } // API search type: power_set : [a] -> [[a]] // fwd bind count: 0 // Return the set of all subsets of xs_in, // including the empty set and xs_in itself. // power_set("xyz") == ["", "x", "y", "z", "xy", "xz", "yz", "xyz"] // Also known as subsequences. template > ContainerOut power_set(const ContainerIn& xs_in) { return concat( generate_by_idx>( bind_1st_of_2( flip(combinations), xs_in), size_of_cont(xs_in) + 1)); } // API search type: iterate : ((a -> a), Int, a) -> [a] // fwd bind count: 2 // Repeatedly apply a function (n times) to a value (starting with x) // and recording the outputs on its way. // iterate((*2), 5, 3) = [3, 6, 12, 24, 48] // = [3, f(3), f(f(3)), f(f(f(3))), f(f(f(f(3))))] template > ContainerOut iterate(F f, std::size_t size, const T& x) { ContainerOut result; if (size == 0) return result; internal::prepare_container(result, size + 1); auto it_out = internal::get_back_inserter(result); T current = x; *it_out = current; for (std::size_t i = 1; i < size; ++i) { current = internal::invoke(f, current); *it_out = current; } return result; } // API search type: iterate_maybe : ((a -> Maybe a), a) -> [a] // fwd bind count: 1 // Repeatedly apply a function to a value (starting with x) // and recording the outputs on its way. // Stops when the function returns nothing. // iterate_maybe(next_collats_val, 5) = [5, 16, 8, 4, 2, 1] template > ContainerOut iterate_maybe(F f, const T& x) { ContainerOut result; auto it_out = internal::get_back_inserter(result); maybe current(x); while (current.is_just()) { *it_out = current.unsafe_get_just(); current = internal::invoke(f, current.unsafe_get_just()); } return result; } // API search type: adjacent_difference_by : [a] -> [a] // fwd bind count: 1 // Computes the differences between the second // and the first of each adjacent pair of elements of the sequence // using a binary function. // adjacent_difference_by([0,4,1,2,5]) == [0,4,-3,1,3] template auto adjacent_difference_by(F f, const ContainerIn& xs) { using X = typename ContainerIn::value_type; using TOut = internal::invoke_result_t; using ContainerOut = std::vector>; ContainerOut result; using std::begin; using std::end; internal::prepare_container(result, size_of_cont(xs)); std::adjacent_difference(begin(xs), end(xs), back_inserter(result), f); return result; } // API search type: adjacent_difference : [a] -> [a] // fwd bind count: 0 // Computes the differences between the second // and the first of each adjacent pair of elements of the sequence. // adjacent_difference([0,4,1,2,5]) == [0,4,-3,1,3] template Container adjacent_difference(const Container& xs) { return adjacent_difference_by( std::minus(), xs); } // API search type: rotate_left : [a] -> [a] // fwd bind count: 0 // Removes the first element and appends it to the back. // rotate_left("xyz") == "yzx" template Container rotate_left(const Container& xs) { if (is_empty(xs)) return xs; Container ys; auto size = size_of_cont(xs); internal::prepare_container(ys, size); auto it = std::begin(xs); auto it_out = internal::get_back_inserter(ys); ++it; while (it != std::end(xs)) { *it_out = *it; ++it; } *it_out = xs.front(); return ys; } // API search type: rotate_right : [a] -> [a] // fwd bind count: 0 // Removes the last element and prepends it to the front. // rotate_right("xyz") == "zxy" template Container rotate_right(const Container& xs) { return reverse(rotate_left(reverse(xs))); } // API search type: rotations_left : [a] -> [[a]] // fwd bind count: 0 // Returns all possible rotations using rotate_left. // rotations_left("abcd") == ["abcd", "bcda", "cdab", "dabc"] template > ContainerOut rotations_left(const ContainerIn& xs_in) { return iterate(rotate_left, size_of_cont(xs_in), xs_in); } // API search type: rotations_right : [a] -> [[a]] // fwd bind count: 0 // Returns all possible rotations using rotate_right. // rotations_right("abcd") == ["abcd", "dabc", "cdab", "bcda"] template > ContainerOut rotations_right(const ContainerIn& xs_in) { return iterate(rotate_right, size_of_cont(xs_in), xs_in); } // API search type: fill_left : (a, Int, [a]) -> [a] // fwd bind count: 2 // Right-align a sequence. // fill_left(0, 6, [1,2,3,4]) == [0,0,1,2,3,4] // Also known as pad_left. template Container fill_left(const T& x, std::size_t min_size, const Container& xs) { if (min_size <= size_of_cont(xs)) return xs; return append(replicate(min_size - size_of_cont(xs), x), xs); } // API search type: fill_right : (a, Int, [a]) -> [a] // fwd bind count: 2 // Left-align a sequence. // fill_right(0, 6, [1,2,3,4]) == [1,2,3,4,0,0] template Container fill_right(const T& x, std::size_t min_size, const Container& xs) { if (min_size <= size_of_cont(xs)) return xs; return append(xs, replicate(min_size - size_of_cont(xs), x)); } // API search type: inits : [a] -> [[a]] // fwd bind count: 0 // Generate all possible segments of xs that include the first element. // inits([0,1,2,3]) == [[],[0],[0,1],[0,1,2],[0,1,2,3]] template > ContainerOut inits(const ContainerIn& xs) { ContainerOut result; std::size_t xs_size = size_of_cont(xs); internal::prepare_container(result, xs_size + 1); auto it_out = internal::get_back_inserter(result); for (std::size_t i = 0; i <= xs_size; ++i) *it_out = get_segment(0, i, xs); return result; } // API search type: tails : [a] -> [[a]] // fwd bind count: 0 // Generate all possible segments of xs that include the last element. // tails([0,1,2,3]) == [[0,1,2,3],[1,2,3],[2,3],[3],[]] template > ContainerOut tails(const ContainerIn& xs) { ContainerOut result; std::size_t xs_size = size_of_cont(xs); internal::prepare_container(result, xs_size + 1); auto it_out = internal::get_back_inserter(result); for (std::size_t i = 0; i <= xs_size; ++i) *it_out = get_segment(i, xs_size, xs); return result; } } // namespace fplus // // search.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include namespace fplus { // API search type: find_first_by : ((a -> Bool), [a]) -> Maybe a // fwd bind count: 1 // Returns the first element fulfilling the predicate. // find_first_by(is_even, [1, 3, 4, 6, 9]) == Just(4) // find_first_by(is_even, [1, 3, 5, 7, 9]) == Nothing template maybe find_first_by(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto it = std::find_if(std::begin(xs), std::end(xs), pred); if (it == std::end(xs)) return nothing(); return just(*it); } // API search type: find_last_by : ((a -> Bool), [a]) -> Maybe a // fwd bind count: 1 // Returns the last element fulfilling the predicate. // find_last_by(is_even, [1, 3, 4, 6, 9]) == Just(6) // find_last_by(is_even, [1, 3, 5, 7, 9]) == Nothing template maybe find_last_by(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); return find_first_by(pred, reverse(xs)); } // API search type: find_first_idx_by : ((a -> Bool), [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the first element fulfilling the predicate. // find_first_idx_by(is_even, [1, 3, 4, 6, 9]) == Just(2) // find_first_idx_by(is_even, [1, 3, 5, 7, 9]) == Nothing template maybe find_first_idx_by (UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto it = std::find_if(std::begin(xs), std::end(xs), pred); if (it == std::end(xs)) return nothing(); return static_cast(std::distance(std::begin(xs), it)); } // API search type: find_last_idx_by : ((a -> Bool), [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the last element fulfilling the predicate. // find_last_idx_by(is_even, [1, 3, 4, 6, 9]) == Just(3) // find_last_idx_by(is_even, [1, 3, 5, 7, 9]) == Nothing template maybe find_last_idx_by (UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); auto calcRevIdx = [&](std::size_t idx) { return size_of_cont(xs) - (idx + 1); }; return lift_maybe(calcRevIdx, find_first_idx_by(pred, reverse(xs))); } // API search type: find_first_idx : (a, [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the first element equal to x. // find_first_idx(4, [1, 3, 4, 4, 9]) == Just(2) // find_first_idx(4, [1, 3, 5, 7, 9]) == Nothing template maybe find_first_idx (const typename Container::value_type& x, const Container& xs) { return find_first_idx_by(is_equal_to(x), xs); } // API search type: find_last_idx : (a, [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the last element equal to x. // find_last_idx(4, [1, 3, 4, 4, 9]) == Just(3) // find_last_idx(4, [1, 3, 5, 7, 9]) == Nothing template maybe find_last_idx (const typename Container::value_type& x, const Container& xs) { return find_last_idx_by(is_equal_to(x), xs); } // API search type: find_all_idxs_by : ((a -> Bool), [a]) -> [Int] // fwd bind count: 1 // Returns the indices off all elements fulfilling the predicate. // find_all_idxs_by(is_even, [1, 3, 4, 6, 9]) == [2, 3] template , typename UnaryPredicate, typename Container> ContainerOut find_all_idxs_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); std::size_t idx = 0; ContainerOut result; auto itOut = internal::get_back_inserter(result); for (const auto& x : xs) { if (internal::invoke(p, x)) *itOut = idx; ++idx; } return result; } // API search type: find_all_idxs_of : (a, [a]) -> [Int] // fwd bind count: 1 // Returns the indices off all elements equal to x. // find_all_idxs_of(4, [1, 3, 4, 4, 9]) == [2, 3] template , typename Container, typename T = typename Container::value_type> ContainerOut find_all_idxs_of (const T& x, const Container& xs) { return find_all_idxs_by(is_equal_to(x), xs); } // API search type: find_all_instances_of_token : ([a], [a]) -> [Int] // fwd bind count: 1 // Returns the starting indices of all segments matching token. // find_all_instances_of_token("haha", "oh, hahaha!") == [4, 6] template , typename Container> ContainerOut find_all_instances_of_token(const Container& token, const Container& xs) { if (size_of_cont(token) > size_of_cont(xs)) return ContainerOut(); auto itInBegin = std::begin(xs); auto itInEnd = itInBegin; internal::advance_iterator(itInEnd, size_of_cont(token)); std::size_t idx = 0; ContainerOut result; auto outIt = internal::get_back_inserter(result); std::size_t last_possible_idx = size_of_cont(xs) - size_of_cont(token); auto check_and_push = [&]() { if (std::equal(itInBegin, itInEnd, std::begin(token))) { *outIt = idx; } }; while (idx != last_possible_idx) { check_and_push(); ++itInBegin; ++itInEnd; ++idx; } check_and_push(); return result; } // API search type: find_all_instances_of_token_non_overlapping : ([a], [a]) -> [Int] // fwd bind count: 1 // Returns the starting indices // of all non-overlapping segments matching token. // find_all_instances_of_token_non_overlapping("haha", "oh, hahaha!") == [4] template , typename Container> ContainerOut find_all_instances_of_token_non_overlapping (const Container& token, const Container& xs) { auto overlapping_instances = find_all_instances_of_token( token, xs); ContainerOut result; auto outIt = internal::get_back_inserter(result); std::size_t token_size = size_of_cont(token); for (const auto idx : overlapping_instances) { if (result.empty() || result.back() + token_size <= idx) { *outIt = idx; } } return result; } // API search type: find_first_instance_of_token : ([a], [a]) -> Maybe Int // fwd bind count: 1 // Returns the index of the first segment matching token. // find_first_instance_of_token("haha", "oh, hahaha!") == just 4 template maybe find_first_instance_of_token (const Container& token, const Container& xs) { if (size_of_cont(token) > size_of_cont(xs)) return nothing(); auto itInBegin = std::begin(xs); auto itInEnd = itInBegin; internal::advance_iterator(itInEnd, size_of_cont(token)); std::size_t idx = 0; std::size_t last_possible_idx = size_of_cont(xs) - size_of_cont(token); while (idx != last_possible_idx) { if (std::equal(itInBegin, itInEnd, std::begin(token))) { return just(idx); } ++itInBegin; ++itInEnd; ++idx; } if (std::equal(itInBegin, itInEnd, std::begin(token))) { return just(idx); } return nothing(); } } // namespace fplus // // sets.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include namespace fplus { // API search type: set_includes : (Set a, Set a) -> Bool // fwd bind count: 1 // Checks if every element of the second set is also present in the first set. // Also known as is_subset_of. template bool set_includes(const SetType& set1, const SetType& set2) { return std::includes(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2)); } // API search type: unordered_set_includes : (Unordered_Set a, Unordered_Set a) -> Bool // fwd bind count: 1 // Checks if every element of the second unordered_set // is also present in the first unordered_set. // Also known as is_subset_of. template bool unordered_set_includes(const UnorderSetType& set1, const UnorderSetType& set2) { auto first_not_included = std::find_if(set2.begin(), set2.end(), [&](const typename UnorderSetType::value_type& x) -> bool { return set1.find(x) == set1.end();}); return first_not_included == set2.end(); } // API search type: set_merge : (Set a, Set a) -> Set a // fwd bind count: 1 // Returns the union of two given sets. template SetType set_merge(const SetType& set1, const SetType& set2) { SetType result; auto itOut = internal::get_back_inserter(result); std::merge(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2), itOut); return result; } // API search type: unordered_set_merge : (Unordered_Set a, Unordered_Set a) -> Unordered_Set a // fwd bind count: 1 // Returns the union of two given sets. template UnorderSetType unordered_set_merge(const UnorderSetType& set1, const UnorderSetType& set2) { UnorderSetType result; auto itOut = internal::get_back_inserter(result); std::copy(std::begin(set1), std::end(set1), itOut); std::copy(std::begin(set2), std::end(set2), itOut); return result; } // API search type: set_intersection : (Set a, Set a) -> Set a // fwd bind count: 1 // Returns the intersection of both sets. template SetType set_intersection(const SetType& set1, const SetType& set2) { SetType result; auto itOut = internal::get_back_inserter(result); std::set_intersection(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2), itOut); return result; } // API search type: unordered_set_intersection : (Unordered_Set a, Unordered_Set a) -> Unordered_Set a // fwd bind count: 1 // Returns the intersection of both unordered_sets. template UnorderSetType unordered_set_intersection( const UnorderSetType& set1, const UnorderSetType& set2) { UnorderSetType result; auto itOut = internal::get_back_inserter(result); std::copy_if(std::begin(set2), std::end(set2), itOut, [&](const typename UnorderSetType::value_type &x) -> bool {return set1.find(x) != set1.end();}); return result; } // API search type: set_is_disjoint : (Set a, Set a) -> Bool // fwd bind count: 1 // Checks if two sets are disjoint. template bool set_is_disjoint(const SetType& set1, const SetType& set2) { return set_intersection(set1, set2).empty(); } // API search type: unordered_set_is_disjoint : (Unordered_Set a, Unordered_Set a) -> Bool // fwd bind count: 1 // Checks if two unordered_sets are disjoint. template bool unordered_set_is_disjoint( const UnorderSetType& set1, const UnorderSetType& set2) { return unordered_set_intersection(set1, set2).empty(); } // API search type: set_difference : (Set a, Set a) -> Set a // fwd bind count: 1 // Returns the elements in set1 that are not present in set2. template SetType set_difference(const SetType& set1, const SetType& set2) { SetType result; auto itOut = internal::get_back_inserter(result); std::set_difference(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2), itOut); return result; } // API search type: unordered_set_difference : (Unordered_Set a, Unordered_Set a) -> Unordered_Set a // fwd bind count: 1 // Returns the elements in unordered_set1 // that are not present in unordered_set2. template UnorderSetType unordered_set_difference(const UnorderSetType& set1, const UnorderSetType& set2) { UnorderSetType result; auto itOut = internal::get_back_inserter(result); std::copy_if(std::begin(set1), std::end(set1), itOut, [&](const typename UnorderSetType::value_type &x) -> bool {return set2.find(x) == set2.end();}); return result; } // API search type: set_symmetric_difference : (Set a, Set a) -> Set a // fwd bind count: 1 // Returns the symmetric difference of both sets. template SetType set_symmetric_difference(const SetType& set1, const SetType& set2) { SetType result; auto itOut = internal::get_back_inserter(result); std::set_symmetric_difference(std::begin(set1), std::end(set1), std::begin(set2), std::end(set2), itOut); return result; } // API search type: unordered_set_symmetric_difference : (Unordered_Set a, Unordered_Set a) -> Unordered_Set a // fwd bind count: 1 // Returns the symmetric difference of both unordered_sets. template UnorderSetType unordered_set_symmetric_difference( const UnorderSetType& set1, const UnorderSetType& set2) { UnorderSetType result; auto itOut = internal::get_back_inserter(result); std::copy_if(std::begin(set1), std::end(set1), itOut, [&](const typename UnorderSetType::value_type &x) -> bool {return set2.find(x) == set2.end();}); std::copy_if(std::begin(set2), std::end(set2), itOut, [&](const typename UnorderSetType::value_type &x) -> bool {return set1.find(x) == set1.end();}); return result; } // API search type: sets_intersection : [Set a] -> Set a // fwd bind count: 0 // Returns the intersection of the given sets. // Also known as intersect_many. // For performance try to sort the inputs sets prior, ascendending by size. template SetType sets_intersection(const ContainerIn& sets) { return fold_left_1(set_intersection, sets); } // API search type: unordered_sets_intersection : [Unordered_Set a] -> Unordered_Set a // fwd bind count: 0 // Returns the intersection of the given unordered_sets. // Also known as intersect_many. template UnordSetType unordered_sets_intersection(const ContainerIn& sets) { return fold_left_1(unordered_set_intersection, sets); } } // namespace fplus #include #include #include namespace fplus { // API search type: any_by : ((a -> Bool), [a]) -> Bool // fwd bind count: 1 // Check if all elements in a container fulfill a predicate. // any_by(is_odd, [2, 4, 6]) == false template bool any_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return std::any_of(std::begin(xs), std::end(xs), p); } // API search type: any : [Bool] -> Bool // fwd bind count: 0 // Checks if all elements in a container are true. // any([false, true, false]) == true template bool any(const Container& xs) { typedef typename Container::value_type T; return any_by(identity, xs); } // API search type: none_by : ((a -> Bool), [a]) -> Bool // fwd bind count: 1 // Check no element in a container fulfills a predicate. // none_by(is_even, [3, 4, 5]) == false template bool none_by(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return std::none_of(std::begin(xs), std::end(xs), p); } // API search type: none : [Bool] -> Bool // fwd bind count: 0 // Checks if all elements in a container are false. // none([false, true, false]) == false template bool none(const Container& xs) { typedef typename Container::value_type T; return none_by(identity, xs); } // API search type: minimum_idx_by : (((a, a) -> Bool), [a]) -> Int // fwd bind count: 1 // Return the index of the first minimum element using a less comparator. // minimum_idx_by(lessLength, ["123", "12", "1234", "123"]) -> "1" // Unsafe! Crashes on an empty sequence. template typename std::size_t minimum_idx_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); assert(is_not_empty(xs)); return static_cast(std::distance(std::begin(xs), std::min_element(std::begin(xs), std::end(xs), comp))); } // API search type: minimum_idx_by_maybe : (((a, a) -> Bool), [a]) -> Int // fwd bind count: 1 // Return the index of the first minimum element using a less comparator // if sequence is not empty. // minimum_idx_by_maybe(lessLength, ["123", "12", "1234", "123"]) -> Just "1" // minimum_idx_by_maybe(lessLength, []) -> Nothing template maybe minimum_idx_by_maybe(Compare comp, const Container& xs) { if (is_empty(xs)) return {}; else return minimum_idx_by(comp, xs); } // API search type: maximum_idx_by : (((a, a) -> Bool), [a]) -> Int // fwd bind count: 1 // Return the index of the first maximum element using a less comparator. // maximum_idx_by(lessLength, ["123", "12", "1234", "123"]) == "2" // Unsafe! Crashes on an empty sequence. template typename std::size_t maximum_idx_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); assert(is_not_empty(xs)); return static_cast(std::distance(std::begin(xs), std::max_element(std::begin(xs), std::end(xs), comp))); } // API search type: maximum_idx_by_maybe : (((a, a) -> Bool), [a]) -> Maybe Int // fwd bind count: 1 // Return the index of the first maximum element using a less comparator // if sequence is not empty. // maximum_idx_by_maybe(lessLength, ["123", "12", "1234", "123"]) == Just "2" // maximum_idx_by_maybe(lessLength, []) == Nothing template maybe maximum_idx_by_maybe(Compare comp, const Container& xs) { if (is_empty(xs)) return {}; else return maximum_idx_by(comp, xs); } // API search type: minimum_idx : [a] -> Int // fwd bind count: 0 // Return the index of the first minimum element. // minimum_idx([3, 1, 4, 2]) == 1 // Unsafe! Crashes on an empty sequence. template typename std::size_t minimum_idx(const Container& xs) { return minimum_idx_by(is_less, xs); } // API search type: minimum_idx_maybe : [a] -> Maybe Int // fwd bind count: 0 // Return the index of the first minimum element if sequence is not empty. // minimum_idx_maybe([3, 1, 4, 2]) == Just 1 // minimum_idx_maybe([]) == Nothing template maybe minimum_idx_maybe(const Container& xs) { if (is_empty(xs)) return {}; else return minimum_idx(xs); } // API search type: maximum_idx : [a] -> Int // fwd bind count: 0 // Return the index of the first maximum element. // maximum_idx([3, 1, 4, 2]) == 2 // Unsafe! Crashes on an empty sequence. template typename std::size_t maximum_idx(const Container& xs) { return maximum_idx_by(is_less, xs); } // API search type: maximum_idx_maybe : [a] -> Maybe Int // fwd bind count: 0 // Return the index of the first maximum element if sequence is not empty. // maximum_idx_maybe([3, 1, 4, 2]) == Just 2 // maximum_imaximum_idx_maybedx([]) == Nothing template maybe maximum_idx_maybe(const Container& xs) { if (is_empty(xs)) return {}; else return maximum_idx(xs); } // API search type: minimum_idx_on : ((a -> b), [a]) -> Int // fwd bind count: 1 // Return the index of the first minimum element using a transformer. // minimum_idx_on(length, ["123", "12", "1234", "123"]) -> "1" // Unsafe! Crashes on an empty sequence. template std::size_t minimum_idx_on(F f, const Container& xs) { using Result = internal::invoke_result_t; auto transformed = transform_convert>>(f, xs); return minimum_idx(transformed); } // API search type: minimum_idx_on_maybe : ((a -> b), [a]) -> Just Int // fwd bind count: 1 // Return the index of the first minimum element using a transformer // if sequence is not empty. // minimum_idx_on_maybe(length, ["123", "12", "1234", "123"]) -> Just "1" // minimum_idx_on_maybe(length, []) -> Nothing" template maybe minimum_idx_on_maybe(F f, const Container& xs) { if (is_empty(xs)) return {}; else return minimum_idx_on(f, xs); } // API search type: maximum_idx_on : ((a -> b), [a]) -> Int // fwd bind count: 1 // Return the index of the first maximum element using a transformer. // maximum_idx_on(length, ["123", "12", "1234", "123"]) == "2" // Unsafe! Crashes on an empty sequence. template std::size_t maximum_idx_on(F f, const Container& xs) { using Result = internal::invoke_result_t; auto transformed = transform_convert>>(f, xs); return maximum_idx(transformed); } // API search type: maximum_idx_on_maybe : ((a -> b), [a]) -> Maybe Int // fwd bind count: 1 // Return the index of the first maximum element using a transformer // if sequence is not empty. // maximum_idx_on_maybe(length, ["123", "12", "1234", "123"]) == Just "2" // maximum_idx_on_maybe(length, []) == Nothing template maybe maximum_idx_on_maybe(F f, const Container& xs) { if (is_empty(xs)) return {}; else return maximum_idx_on(f, xs); } // API search type: minimum_by : (((a, a) -> Bool), [a]) -> a // fwd bind count: 1 // Return the first minimum element using a less comparator. // minimum_by(lessLength, ["123", "12", "1234", "123"]) -> "12" // Unsafe! Crashes on an empty sequence. template typename Container::value_type minimum_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); assert(is_not_empty(xs)); return *std::min_element(std::begin(xs), std::end(xs), comp); } // API search type: minimum_by_maybe : (((a, a) -> Bool), [a]) -> a // fwd bind count: 1 // Return the first minimum element using a less comparator // if sequence is not empty. // minimum_by_maybe(lessLength, ["123", "12", "1234", "123"]) -> Just "12" // minimum_by_maybe(lessLength, []) -> Nothing template maybe minimum_by_maybe(Compare comp, const Container& xs) { if (is_empty(xs)) return {}; else return minimum_by(comp, xs); } // API search type: maximum_by : (((a, a) -> Bool), [a]) -> a // fwd bind count: 1 // Return the first maximum element using a less comparator. // maximum_by(lessLength, ["123", "12", "1234", "123"]) == "1234" // Unsafe! Crashes on an empty sequence. template typename Container::value_type maximum_by(Compare comp, const Container& xs) { internal::check_compare_for_container(); assert(is_not_empty(xs)); return *std::max_element(std::begin(xs), std::end(xs), comp); } // API search type: maximum_by_maybe : (((a, a) -> Bool), [a]) -> Maybe a // fwd bind count: 1 // Return the first maximum element using a less comparator // if sequence is not empty. // maximum_by_maybe(lessLength, ["123", "12", "1234", "123"]) == Just "1234" // maximum_by_maybe(lessLength, []) == Nothing template maybe maximum_by_maybe(Compare comp, const Container& xs) { if (is_empty(xs)) return {}; else return maximum_by(comp, xs); } // API search type: minimum : [a] -> a // fwd bind count: 0 // Return the first minimum element. // minimum([3, 1, 4, 2]) == 1 // Unsafe! Crashes on an empty sequence. template typename Container::value_type minimum(const Container& xs) { return minimum_by(is_less, xs); } // API search type: minimum_maybe : [a] -> Maybe a // fwd bind count: 0 // Return the first minimum element if sequence is not empty // if sequence is not empty. // minimum_maybe([3, 1, 4, 2]) == Just 1 // minimum_maybe([]) == Nothing template maybe minimum_maybe(const Container& xs) { if (is_empty(xs)) return {}; else return minimum(xs); } // API search type: maximum : [a] -> a // fwd bind count: 0 // Return the first maximum element. // maximum([3, 1, 4, 2]) == 4 // Unsafe! Crashes on an empty sequence. template typename Container::value_type maximum(const Container& xs) { return maximum_by(is_less, xs); } // API search type: maximum_maybe : [a] -> Maybe a // fwd bind count: 0 // Return the first maximum element if sequence is not empty // if sequence is not empty. // maximum_maybe([3, 1, 4, 2]) == Just 4 // maximum_maybe([]) == Nothing template maybe maximum_maybe(const Container& xs) { if (is_empty(xs)) return {}; else return maximum(xs); } // API search type: minimum_on : ((a -> b), [a]) -> a // fwd bind count: 1 // Return the first minimum element using a transformer. // minimum_on(length, ["123", "12", "1234", "123"]) -> "12" // Unsafe! Crashes on an empty sequence. template typename Container::value_type minimum_on(F f, const Container& xs) { internal::trigger_static_asserts(); return elem_at_idx(minimum_idx_on(f, xs), xs); } // API search type: minimum_on_maybe : ((a -> b), [a]) -> Maybe a // fwd bind count: 1 // Return the first minimum element using a transformer // if sequence is not empty. // minimum_on_maybe(length, ["123", "12", "1234", "123"]) -> Just "12" // minimum_on_maybe(length, []) -> Nothing template maybe minimum_on_maybe( F f, const Container& xs) { if (is_empty(xs)) return {}; else return minimum_on(f, xs); } // API search type: maximum_on : ((a -> b), [a]) -> a // fwd bind count: 1 // Return the first maximum element using a transformer. // maximum_on(length, ["123", "12", "1234", "123"]) == "1234" // Unsafe! Crashes on an empty sequence. template typename Container::value_type maximum_on(F f, const Container& xs) { internal::trigger_static_asserts(); return elem_at_idx(maximum_idx_on(f, xs), xs); } // API search type: maximum_on_maybe : ((a -> b), [a]) -> Maybe a // fwd bind count: 1 // Return the first maximum element using a transformer // if sequence is not empty. // maximum_on_maybe(length, ["123", "12", "1234", "123"]) == Just "1234" // maximum_on_maybe(length, ["123", "12", "1234", "123"]) == Nothing template maybe maximum_on_maybe( F f, const Container& xs) { if (is_empty(xs)) return {}; else return maximum_on(f, xs); } // API search type: mean : [a] -> a // fwd bind count: 0 // mean([1, 4, 4]) == 3 // Also known as average. // xs must have at least one element. // Use mean_using_doubles if overflow errors for sum(xs) can occur. // Unsafe! Crashes on an empty sequence. template Result mean(const Container& xs) { assert(size_of_cont(xs) != 0); typedef typename Container::value_type T; return static_cast(sum(xs) / static_cast(size_of_cont(xs))); } // API search type: mean_obj_div_size_t : [a] -> a // fwd bind count: 0 // mean_obj_div_size_t([B 1, B 4, B 4]) == B 3 // The provided objects must support division by a std::size_t. // Unsafe! Crashes on an empty sequence. // Also Make sure sum(xs) does not overflow. template T mean_obj_div_size_t(const Container& xs) { assert(size_of_cont(xs) != 0); return sum(xs) / size_of_cont(xs); } // API search type: mean_obj_div_double : [a] -> a // fwd bind count: 0 // mean_obj_div_double([B 1, B 4, B 4]) == B 3 // The provided objects must support division by a double. // Unsafe! Crashes on an empty sequence. // Also Make sure sum(xs) does not overflow. template T mean_obj_div_double(const Container& xs) { assert(size_of_cont(xs) != 0); return sum(xs) / static_cast(size_of_cont(xs)); } // API search type: mean_using_doubles : [a] -> a // fwd bind count: 0 // mean_using_doubles([1, 4, 4]) == 3 // Converts elements to double before calculating the sum // to prevent overflows. // Unsafe! Crashes on an empty sequence. template Result mean_using_doubles(const Container& xs) { assert(size_of_cont(xs) != 0); auto xs_as_doubles = convert_elems(xs); auto result_as_double = mean(xs_as_doubles); if (!std::is_integral::value) return static_cast(result_as_double); else return round(result_as_double); } // API search type: median : [a] -> a // fwd bind count: 0 // median([5, 6, 4, 3, 2, 6, 7, 9, 3]) == 5 // Unsafe! Crashes on an empty sequence. template Result median(const Container& xs) { assert(is_not_empty(xs)); if (size_of_cont(xs) == 1) return static_cast(xs.front()); // std::nth_element (instead of sorting) // would be faster for random-access containers // but not work at all on other containers like std::list. auto xsSorted = sort(xs); if (size_of_cont(xsSorted) % 2 == 1) { auto it = std::begin(xsSorted); internal::advance_iterator(it, size_of_cont(xsSorted) / 2); return static_cast(*it); } else { auto it1 = std::begin(xsSorted); internal::advance_iterator(it1, size_of_cont(xsSorted) / 2 - 1); auto it2 = it1; ++it2; return static_cast(*it1 + *it2) / static_cast(2); } } // API search type: all_unique_by_less : (((a, a) -> Bool), [a]) -> Bool // fwd bind count: 1 // Returns true for empty containers. // O(n*log(n)) template bool all_unique_by_less(Compare comp, const Container& xs) { internal::check_compare_for_container(); if (size_of_cont(xs) < 2) return true; return size_of_cont(unique(sort_by(comp, xs))) == size_of_cont(xs); } // API search type: all_unique_less : [a] -> Bool // fwd bind count: 0 // Returns true for empty containers. // O(n*log(n)) template bool all_unique_less(const Container& xs) { typedef typename Container::value_type T; auto comp = std::less(); return all_unique_by_less(comp, xs); } // API search type: is_infix_of : ([a], [a]) -> Bool // fwd bind count: 1 // is_infix_of("ion", "FunctionalPlus") == true template bool is_infix_of(const Container& token, const Container& xs) { return is_just(find_first_instance_of_token(token, xs)); } // API search type: is_subsequence_of : ([a], [a]) -> Bool // fwd bind count: 1 // is_subsequence_of("Final", "FunctionalPlus") == true template bool is_subsequence_of(const Container& seq, const Container& xs) { if (is_empty(seq)) return true; if (size_of_cont(seq) > size_of_cont(xs)) return false; typedef typename Container::value_type T; auto remaining = convert_container_and_elems>(seq); for (const auto& x : xs) { if (x == remaining.front()) { remaining.pop_front(); if (is_empty(remaining)) return true; } } return false; } // API search type: count_if : ((a -> Bool), [a]) -> Int // fwd bind count: 1 // count_if(is_even, [1, 2, 3, 5, 7, 8]) == 2 template std::size_t count_if(UnaryPredicate p, const Container& xs) { internal::check_unary_predicate_for_container(); return size_of_cont(find_all_idxs_by(p, xs)); } // API search type: count : (a, [a]) -> Int // fwd bind count: 1 // count(2, [1, 2, 3, 5, 7, 2, 2]) == 3 template std::size_t count (const typename Container::value_type& x, const Container& xs) { return size_of_cont(find_all_idxs_of(x, xs)); } // API search type: is_unique_in_by : ((a -> bool), [a]) -> Bool // fwd bind count: 1 // is_unique_in_by((==2), [1, 2, 3, 5, 7, 2, 2]) == false // is_unique_in_by((==5), [1, 2, 3, 5, 7, 2, 2]) == true template bool is_unique_in_by (UnaryPredicate pred, const Container& xs) { std::size_t count = 0; for (const auto& x : xs) { if (internal::invoke(pred, x)) { ++count; if (count > 1) { return false; } } } return true; } // API search type: is_unique_in : (a, [a]) -> Bool // fwd bind count: 1 // is_unique_in(2, [1, 2, 3, 5, 7, 2, 2]) == false // is_unique_in(5, [1, 2, 3, 5, 7, 2, 2]) == true template bool is_unique_in (const typename Container::value_type& x, const Container& xs) { return is_unique_in_by(is_equal_to(x), xs); } // API search type: is_permutation_of : ([a], [a]) -> Bool // fwd bind count: 1 // Checks if one container is a permuation of the other one. // is_permutation_of([2,3,1], [1,2,3]) == true // O(log(n)) template bool is_permutation_of(const Container& xs, const Container& ys) { return size_of_cont(xs) == size_of_cont(ys) && sort(xs) == sort(ys); } // API search type: fill_pigeonholes_to : (Int, [Int]) -> [Int] // fwd bind count: 1 // Returns a list containing the count for every element in xs // with the value corresponding to the index in the result list. // fill_pigeonholes_to(5, [0,1,3,1]) == [1,2,0,1,0] // fill_pigeonholes_to(3, [0,1,3,1]) == [1,2,0] template , typename T = typename ContainerIn::value_type> ContainerOut fill_pigeonholes_to(std::size_t idx_end, const ContainerIn& xs) { static_assert(std::is_integral::value, "Type must be integral."); if (is_empty(xs)) return {}; ContainerOut result(idx_end, 0); for (const auto& x : xs) { if (x >= 0) { const auto idx = static_cast(x); if (idx < result.size()) { ++result[idx]; } } } return result; } // API search type: fill_pigeonholes : [Int] -> [Int] // fwd bind count: 0 // Returns a list containing the count for every element in xs // with the value corresponding to the index in the result list. // fill_pigeonholes([0,1,3,1]) == [1,2,0,1] template , typename T = typename ContainerIn::value_type> ContainerOut fill_pigeonholes(const ContainerIn& xs) { static_assert(std::is_integral::value, "Type must be integral."); if (is_empty(xs)) return {}; return(fill_pigeonholes_to( maximum(xs) + 1, xs)); } // API search type: fill_pigeonholes_bool_to : (Int, [Int]) -> [Int] // fwd bind count: 1 // Returns a list telling if the element corresponding to the index // is present in xs. // fill_pigeonholes_bool_to(5, [0,1,3,1]) == [1,1,0,1,0] // fill_pigeonholes_bool_to(3, [0,1,3,1]) == [1,1,0] template , typename T = typename ContainerIn::value_type> ContainerOut fill_pigeonholes_bool_to(std::size_t idx_end, const ContainerIn& xs) { static_assert(std::is_integral::value, "Type must be integral."); if (is_empty(xs)) return {}; ContainerOut result(idx_end, 0); for (const auto& x : xs) { if (x >= 0) { const auto idx = static_cast(x); if (idx < result.size()) { result[idx] = 1; } } } return result; } // API search type: fill_pigeonholes_bool : [Int] -> [Int] // fwd bind count: 0 // Returns a list telling if the element corresponding to the index // is present in xs. // fill_pigeonholes_bool([0,1,3,1]) == [1,2,0,1] template , typename T = typename ContainerIn::value_type> ContainerOut fill_pigeonholes_bool(const ContainerIn& xs) { static_assert(std::is_integral::value, "Type must be integral."); if (is_empty(xs)) return {}; return(fill_pigeonholes_bool_to( maximum(xs) + 1, xs)); } // API search type: present_in_all : [[a]] -> [a] // fwd bind count: 0 // Returns a list containing only the elements present in all sublists of xs. // Also known as gemstones. // present_in_all([[4,1,2], [5,2,1], [2,4,1]]) == [1,2] template > ContainerOut present_in_all(const ContainerIn& xs) { return convert_container( fplus::sets_intersection( transform( convert_container, SubContainerIn>, xs))); } } // namespace fplus // // extrapolate.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) namespace fplus { // API search type: elem_at_idx_or_nothing : (Int, [a]) -> Maybe a // fwd bind count: 1 // Return nth element of a sequence. // Returns nothing if index is outside of xs. template maybe elem_at_idx_or_nothing(signed int idx, const Container& xs) { if (idx < 0 || idx >= static_cast(size_of_cont(xs))) { return {}; } auto it = std::begin(xs); internal::advance_iterator(it, static_cast(idx)); return *it; } // API search type: elem_at_idx_or_constant : (a, Int, [a]) -> a // fwd bind count: 2 // Return nth element of a sequence. // Interpolate outside of sequence with a constant value. // iiiiii|abcdefgh|iiiiiii template T elem_at_idx_or_constant(const T& c, signed int idx, const Container& xs) { if (idx < 0 || idx >= static_cast(size_of_cont(xs))) { return c; } auto it = std::begin(xs); internal::advance_iterator(it, static_cast(idx)); return *it; } // API search type: elem_at_idx_or_replicate : (Int, [a]) -> a // fwd bind count: 1 // Return nth element of a sequence. // Interpolate outside of sequence by replicating the nearest inside value. // aaaaaa|abcdefgh|hhhhhhh // xs must be non-empty. template T elem_at_idx_or_replicate(signed int idx, const Container& xs) { assert(is_not_empty(xs)); if (idx < 0) { return xs.front(); } if (idx >= static_cast(size_of_cont(xs))) { return xs.back(); } auto it = std::begin(xs); internal::advance_iterator(it, static_cast(idx)); return *it; } // API search type: elem_at_idx_or_wrap : (Int, [a]) -> a // fwd bind count: 1 // Return nth element of a sequence. // Interpolate outside of sequence by replicating the sequence. // For cyclic element access. // cdefgh|abcdefgh|abcdefg // xs must be non-empty. template T elem_at_idx_or_wrap(signed int idx, const Container& xs) { assert(is_not_empty(xs)); const signed int cont_size = static_cast(size_of_cont(xs)); if (idx < 0) idx = cont_size - (std::abs(idx) % cont_size); else idx = idx % cont_size; auto it = std::begin(xs); internal::advance_iterator(it, static_cast(idx)); return *it; } // API search type: extrapolate_replicate : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Extrapolate a sequence by replicating the border values. // count_begin determines the number of elements to be prepended. // count_end determines the number of elements to be appended. // aaaaaa|abcdefgh|hhhhhhh // xs must be non-empty. template Container extrapolate_replicate(std::size_t count_begin, std::size_t count_end, const Container& xs) { assert(is_not_empty(xs)); Container ys; const auto xs_size = size_of_cont(xs); internal::prepare_container(ys, xs_size + count_begin + count_end); auto it = internal::get_back_inserter(ys); const signed int idx_end = static_cast(xs_size + count_end); const signed int idx_start = -static_cast(count_begin); for (signed int idx = idx_start; idx < idx_end; ++idx) { *it = elem_at_idx_or_replicate(idx, xs); } return ys; } // API search type: extrapolate_wrap : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Extrapolate a sequence by accessing the elements in cyclic fashion. // count_begin determines the number of elements to be prepended. // count_end determines the number of elements to be appended. // cdefgh|abcdefgh|abcdefg // xs must be non-empty. template Container extrapolate_wrap(std::size_t count_begin, std::size_t count_end, const Container& xs) { assert(is_not_empty(xs)); Container ys; const auto xs_size = size_of_cont(xs); internal::prepare_container(ys, xs_size + count_begin + count_end); auto it = internal::get_back_inserter(ys); const signed int idx_end = static_cast(xs_size + count_end); const signed int idx_start = -static_cast(count_begin); for (signed int idx = idx_start; idx < idx_end; ++idx) { *it = elem_at_idx_or_wrap(idx, xs); } return ys; } } // namespace fplus // // interpolate.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include namespace fplus { // API search type: elem_at_float_idx : (Float, [a]) -> a // fwd bind count: 1 // Interpolates linearly between elements. // xs must be non-empty. template T elem_at_float_idx(double idx, const Container& xs) { assert(is_not_empty(xs)); if (idx <= 0.0) { return xs.front(); } std::size_t idx_floor = static_cast(floor(idx)); std::size_t idx_ceil = static_cast(ceil(idx)); if (idx_ceil >= size_of_cont(xs)) { return xs.back(); } double idx_floor_float = static_cast(idx_floor); double idx_ceil_float = static_cast(idx_ceil); double weight_floor = idx_ceil_float - idx; double weight_ceil = idx - idx_floor_float; return (weight_floor * elem_at_idx(idx_floor, xs) + weight_ceil * elem_at_idx(idx_ceil, xs)); } } // namespace fplus // // maps.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include namespace fplus { // API search type: pairs_to_map : [(key, val)] -> Map key val // fwd bind count: 0 // Converts a Container of pairs (key, value) into a dictionary. template MapOut pairs_to_map(const ContainerIn& pairs) { return convert_container_and_elems(pairs); } // API search type: pairs_to_map_grouped : [(key, val)] -> Map key [val] // fwd bind count: 0 // Convert a list of key-value pairs to a dictionary // while pushing values having the same key into a vector. // pairs_to_map_grouped([("a", 1), ("a", 2), ("b", 6), ("a", 4)]) // -> {"a": [1, 2, 4], "b": [6]} template >> MapOut pairs_to_map_grouped(const ContainerIn& pairs) { MapOut result; for (const auto& p : pairs) { result[p.first].push_back(p.second); } return result; } // API search type: map_to_pairs : Map key val -> [(key, val)] // fwd bind count: 0 // Converts a dictionary into a Container of pairs (key, value). template ::type, typename Val = typename std::remove_const::type, typename OutPair = std::pair, typename ContainerOut = std::vector> ContainerOut map_to_pairs(const MapType& dict) { return convert_container_and_elems(dict); } // API search type: transform_map_values : ((old_val -> new_val), Map key old_val) -> Map key new_val // fwd bind count: 1 // Manipulate the values in a dictionary, keeping the key-value relationship. // transform_map_values((*2), {0: 2, 1: 3}) == {0: 4, 1: 6} template auto transform_map_values(F f, const MapIn& map) { using MapInPair = typename MapIn::value_type; using Key = std::remove_const_t; using InVal = std::remove_const_t; using OutVal = std::decay_t>; using MapOut = typename internal::SameMapTypeNewTypes::type; return pairs_to_map( transform( bind_1st_of_2(transform_snd, f), map_to_pairs(map))); } // API search type: map_union_with : (((val, val) -> val), Map key val, Map key val) -> Map key val // fwd bind count: 2 // Combine two dictionaries using a binary function for the values. // map_union_with((++), {0: a, 1: b}, {0: c, 2: d}) == {0: ac, 1: b, 2: d} template auto map_union_with(F f, const MapIn& dict1, const MapIn& dict2) { const auto both = append(map_to_pairs(dict1), map_to_pairs(dict2)); using Key = typename decltype(both)::value_type::first_type; using SingleValue = typename decltype(both)::value_type::second_type; auto full_map = pairs_to_map_grouped>::type>(both); const auto group_f = [f](const auto& vals) { return fold_left_1(f, vals); }; return transform_map_values(group_f, full_map); } // API search type: map_union : (Map key val, Map key val) -> Map key val // fwd bind count: 1 // Combine two dictionaries keeping the value of the first map // in case of key collissions. // map_union({0: a, 1: b}, {0: c, 2: d}) == {0: a, 1: b, 2: d} template MapType map_union(const MapType& dict1, const MapType& dict2) { using Value = typename MapType::value_type::second_type; const auto get_first = [](const Value& a, const Value&) -> Value { return a; }; return map_union_with(get_first, dict1, dict2); } // API search type: get_map_keys : Map key val -> [key] // fwd bind count: 0 // Returns all keys used in a map. template ::type>> ContainerOut get_map_keys(const MapType& dict) { auto pairs = map_to_pairs(dict); typedef typename decltype(pairs)::value_type::first_type FirstType; typedef typename decltype(pairs)::value_type::second_type SecondType; return transform_convert( fst, map_to_pairs(dict)); } // API search type: get_map_values : Map key val -> [val] // fwd bind count: 0 // Returns all values present in a map. template ::type>> ContainerOut get_map_values(const MapType& dict) { auto pairs = map_to_pairs(dict); typedef typename decltype(pairs)::value_type::first_type FirstType; typedef typename decltype(pairs)::value_type::second_type SecondType; return transform_convert( snd, map_to_pairs(dict)); } // API search type: swap_keys_and_values : Map a b -> Map b a // fwd bind count: 0 // Swaps keys and Values of a dict: // swap_keys_and_values({1: "a", 2: "b"}) == {"a": 1, "b": 2} template ::type, typename MapOut = typename internal::SameMapTypeNewTypes::type> MapOut swap_keys_and_values(const MapIn& dict) { auto inAsPairs = map_to_pairs(dict); auto outAsPairs = transform(swap_pair_elems, inAsPairs); return pairs_to_map(outAsPairs); } // API search type: create_map : ([key], [val]) -> Map key val // fwd bind count: 1 // Zip a sequence of keys with a sequence of values to obtain a dictionary. // create_map([1,2,3], ["one", "two"]) == {1: "one", 2: "two"} template ::type, typename Val = typename std::remove_const::type, typename MapOut = std::map> MapOut create_map(const ContainerIn1& keys, const ContainerIn2& values) { auto pairs = zip(keys, values); return pairs_to_map(pairs); } // API search type: create_map_with : ((key -> val), [key]) -> Map key val // fwd bind count: 1 // Take a list of keys and create a dictionary // generating the values by applying f to each key. // create_map_with(show, [1,2]) == {1: "1", 2: "2"} template auto create_map_with(F f, const ContainerIn& keys) { return create_map(keys, transform(f, keys)); } // API search type: create_unordered_map : ([key], [val]) -> Map key val // fwd bind count: 1 // Zip a sequence of keys with a sequence of values to obtain a dictionary. // create_unordered_map([1,2,3], ["one", "two"]) == {1: "one", 2: "two"} template ::type, typename Val = typename std::remove_const::type, typename MapOut = std::unordered_map> MapOut create_unordered_map( const ContainerIn1& keys, const ContainerIn2& values) { auto pairs = zip(keys, values); return pairs_to_map(pairs); } // API search type: create_unordered_map_with : ((key -> val), [key]) -> Map key val // fwd bind count: 1 // Take a list of keys and create a dictionary // generating the values by applying f to each key. // create_unordered_map_with(show, [1,2]) == {1: "1", 2: "2"} template auto create_unordered_map_with(F f, const ContainerIn& keys) { return create_unordered_map(keys, transform(f, keys)); } // API search type: get_from_map : (Map key val, key) -> Maybe val // fwd bind count: 1 // Returns just the value of a key if key is present. // Otherwise returns nothing. template maybe get_from_map(const MapType& map, const Key& key) { auto it = map.find(key); if (it == std::end(map)) return nothing(); return just(it->second); } // API search type: get_from_map_unsafe : (Map key val, key) -> val // fwd bind count: 1 // Returns the value of a key if key is present. // Crashes otherwise. template Val get_from_map_unsafe(const MapType& map, const Key& key) { return unsafe_get_just(get_from_map(map, key)); } // API search type: get_from_map_with_def : (Map key val, val, key) -> val // fwd bind count: 2 // Returns the value of a key if key is present. // Otherwise returns the provided default. // Also known as prop_or. template Val get_from_map_with_def(const MapType& map, const Val& defVal, const Key& key) { return just_with_default(defVal, get_from_map(map, key)); } // API search type: get_first_from_map : (Map key val, [key]) -> Maybe val // fwd bind count: 1 // Returns just the value of the first key present. // Otherwise returns nothing. template maybe get_first_from_map(const MapType& map, const KeysContainer& keys) { static_assert(std::is_same::value, "Key type does not match."); for (const auto& key: keys) { auto it = map.find(key); if (it != std::end(map)) return just(it->second); } return nothing(); } // API search type: get_first_from_map_unsafe : (Map key val, [key]) -> val // fwd bind count: 1 // Returns the value of the first key present. // Crashes otherwise. template Val get_first_from_map_unsafe(const MapType& map, const KeysContainer& keys) { return unsafe_get_just(get_first_from_map(map, keys)); } // API search type: get_first_from_map_with_def : (Map key val, val, [key]) -> val // fwd bind count: 2 // Returns the value of the first key present. // Otherwise returns the provided default. template Val get_first_from_map_with_def(const MapType& map, const Val& defVal, const KeysContainer& keys) { return just_with_default(defVal, get_first_from_map(map, keys)); } // API search type: map_contains : (Map key val, key) -> Bool // fwd bind count: 1 // Checks if a map contains a key. template bool map_contains(const MapType& map, const Key& key) { auto it = map.find(key); return it != std::end(map); } // API search type: map_keep_if : ((key -> Bool), Map key val) -> Map key val // fwd bind count: 1 // Filters the map by keys. // map_keep_if(is_upper_case, {a: 1, b: 2, A: 3, C: 4}) == {A: 3, C: 4} // Also known as pick_by. template MapType map_keep_if(Pred pred, const MapType& map) { MapType result; for (const auto& key_and_value : map) { if (internal::invoke(pred, key_and_value.first)) { result.insert(key_and_value); } } return result; } // API search type: map_drop_if : ((key -> Bool), Map key val) -> Map key val // fwd bind count: 1 // Filters the map by keys. // map_drop_if(is_lower_case, {a: 1, b: 2, A: 3, C: 4}) == {A: 3, C: 4} // Inverse of map_keep_if. template MapType map_drop_if(Pred pred, const MapType& map) { return map_keep_if(logical_not(pred), map); } // API search type: map_keep : ([key], Map key val) -> Map key val // fwd bind count: 1 // Keeps only the pairs of the map found in the key list. // map_keep([a, d], {a: 1, b: 2, c: 3, d: 4}) == {a: 1, d: 4} // map_keep([a, e, f], {a: 1, b: 2, c: 3, d: 4}) == {a: 1} // Also known as pick. template MapType map_keep(const KeyContainer& keys, const MapType& map) { static_assert(std::is_same< typename KeyContainer::value_type, typename MapType::key_type>::value, "Key types do not match."); return map_keep_if(bind_2nd_of_2(is_elem_of, keys), map); } // API search type: map_drop : ([key], Map key val) -> Map key val // fwd bind count: 1 // Keeps only the pairs of the map not found in the key list. // Inverse of map_keep. // map_drop([b, c], {a: 1, b: 2, c: 3, d: 4}); == {a: 1, d: 4} template MapType map_drop(const KeyContainer& keys, const MapType& map) { static_assert(std::is_same< typename KeyContainer::value_type, typename MapType::key_type>::value, "Key types do not match."); return map_drop_if(bind_2nd_of_2(is_elem_of, keys), map); } // API search type: map_keep_if_value : ((val -> Bool), Map key val) -> Map key val // fwd bind count: 1 // Filters the map by values. // map_keep_if_value(is_upper_case, {1: a, 2: b, 3: A, 4: C}) == {3: A, 4: C} // Also known as filter_values. template MapType map_keep_if_value(Pred pred, const MapType& map) { MapType result; for (const auto& key_and_value : map) { if (internal::invoke(pred, key_and_value.second)) { result.insert(key_and_value); } } return result; } // API search type: map_drop_if_value : ((value -> Bool), Map key val) -> Map key val // fwd bind count: 1 // Filters the map by values. // map_drop_if_value(is_lower_case, {1: a, 2: b, 3: A, 4: C}) == {3: A, 4: C} // Inverse of map_keep_if_value. template MapType map_drop_if_value(Pred pred, const MapType& map) { return map_keep_if_value(logical_not(pred), map); } // API search type: map_keep_values : ([value], Map key val) -> Map key val // fwd bind count: 1 // Keeps only the pairs of the map found in the value list. // map_keep_values([1, 4], {a: 1, b: 2, c: 3, d: 4}) == {a: 1, d: 4} // map_keep_values([1, 5, 6], {a: 1, b: 2, c: 3, d: 4}) == {a: 1} template MapType map_keep_values(const ValueContainer& values, const MapType& map) { static_assert(std::is_same< typename ValueContainer::value_type, typename MapType::mapped_type>::value, "Value types do not match."); return map_keep_if_value( bind_2nd_of_2(is_elem_of, values), map); } // API search type: map_drop_values : ([value], Map key val) -> Map key val // fwd bind count: 1 // Keeps only the pairs of the map not found in the value list. // Inverse of map_keep_values. // map_drop_values([2, 3], {a: 1, b: 2, c: 3, d: 4}) == {a: 1, d: 4} template MapType map_drop_values(const ValueContainer& values, const MapType& map) { static_assert(std::is_same< typename ValueContainer::value_type, typename MapType::mapped_type>::value, "Value types do not match."); return map_drop_if_value( bind_2nd_of_2(is_elem_of, values), map); } // API search type: map_pluck : (key, [Map key val]) -> [val] // fwd bind count: 1 // Extracts values to a specific key from a list of maps. // map_pluck('a', [{a: 1, b: 2}, {a: 3}, {c: 4}]) == [Just 1, Just 3, Nothing] template >, typename MapType = typename MapContainer::value_type, typename Key = typename MapType::key_type, typename Val = typename MapType::mapped_type> ContainerOut map_pluck(const Key& key, const MapContainer& maps) { return transform_convert( bind_2nd_of_2(get_from_map, key), maps); } // API search type: choose : ([(a, b)], a) -> Maybe b // fwd bind count: 1 // Selects a value assigned to a key iff the key exists exactly once. // choose([(1,a), (2,b)], 2) == Just b; // choose([(1,a), (1,b)], 2) == Nothing; // choose([(1,a), (2,b)], 3) == Nothing; template maybe choose(const std::vector>& pairs, const Key& x) { if (count(x, transform(fst, pairs)) != 1) return {}; return get_from_map(pairs_to_map>(pairs), x); } // API search type: choose_by : ([((a -> Bool), b)], a) -> Maybe b // fwd bind count: 2 // Iff exactly one predicate is fulfilled // the value assigned to this predicate is selected. // choose_by([(is_even,a), (is_bigger_than_3,b)], 2) == Just a; // choose_by([(is_even,a), (is_bigger_than_3,b)], 5) == Just b; // choose_by([(is_even,a), (is_bigger_than_3,b)], 1) == Nothing; // choose_by([(is_even,a), (is_bigger_than_3,b)], 4) == Nothing; template maybe choose_by( const std::vector, Val>>& pairs, const Key& x) { maybe result; for (const auto& p : pairs) { if (internal::invoke(p.first, x)) { if (is_just(result)) { return nothing(); } result = p.second; } } return result; } // API search type: choose_lazy : ([(a, (() -> b))], a) -> Maybe b // fwd bind count: 1 // Evaluates a lazy value assigned to a key iff the key exists exactly once. // choose_lazy([(1,a), (2,b)], 2) == Just b(); // choose_lazy([(1,a), (1,b)], 2) == Nothing; // choose_lazy([(1,a), (2,b)], 3) == Nothing; template auto choose_lazy(const std::vector>& pairs, const Key& x) { using Ret = maybe>>; const auto res = choose(pairs, x); if (res.is_nothing()) return Ret{}; else return Ret{res.unsafe_get_just()()}; } // API search type: choose_by_lazy : ([((a -> Bool), (() -> b))], a) -> Maybe b // fwd bind count: 2 // Iff exactly one predicate is fulfilled // the lazy value assigned to this predicate is evaluated. // choose_by_lazy([(is_even,a), (is_bigger_than_3,b)], 2) == Just a(); // choose_by_lazy([(is_even,a), (is_bigger_than_3,b)], 5) == Just b(); // choose_by_lazy([(is_even,a), (is_bigger_than_3,b)], 1) == Nothing; // choose_by_lazy([(is_even,a), (is_bigger_than_3,b)], 4) == Nothing; template auto choose_by_lazy( const std::vector, ValStub>>& pairs, const Key& x) { using Ret = maybe>>; const auto res = choose_by(pairs, x); if (res.is_nothing()) return Ret{}; else return Ret{res.unsafe_get_just()()}; } // API search type: choose_def : (b, [(a, b)], a) -> b // fwd bind count: 1 // Selects a value assigned to a key iff the key exists exactly once, // otherwise returns the given default value. // choose_def(c, [(1,a), (2,b)], 2) == b; // choose_def(c, [(1,a), (1,b)], 2) == c; // choose_def(c, [(1,a), (2,b)], 3) == c; template Val choose_def(const Val& def, const std::vector>& pairs, const Key& x) { if (count(x, transform(fst, pairs)) != 1) return def; return get_from_map_with_def( pairs_to_map>(pairs), def, x); } // API search type: choose_by_def : (b, [((a -> Bool), b)], a) -> b // fwd bind count: 2 // Iff exactly one predicate is fulfilled // the value assigned to this predicate is selected. // Otherwise the given default value is returned. // choose_by_def(c, [(is_even,a), (is_bigger_than_3,b)], 2) == Just a; // choose_by_def(c, [(is_even,a), (is_bigger_than_3,b)], 5) == Just b; // choose_by_def(c, [(is_even,a), (is_bigger_than_3,b)], 1) == c; // choose_by_def(c, [(is_even,a), (is_bigger_than_3,b)], 4) == c; template Val choose_by_def(const Val& def, const std::vector, Val>>& pairs, const Key& x) { return just_with_default(def, choose_by(pairs, x)); } // API search type: choose_def_lazy : ((() -> b), [(a, (() -> b))], a) -> b // fwd bind count: 1 // Evaluates a lazy value assigned to a key iff the key exists exactly once, // otherwise evaluates the given default lazy value. // choose_def_lazy(c, [(1,a), (2,b)], 2) == b(); // choose_def_lazy(c, [(1,a), (1,b)], 2) == c(); // choose_def_lazy(c, [(1,a), (2,b)], 3) == c(); template auto choose_def_lazy(const ValStub& def, const std::vector>& pairs, const Key& x) { return choose_def(def, pairs, x)(); } // API search type: choose_by_def_lazy : ((() -> b), [((a -> Bool), (() -> b))], a) -> b // fwd bind count: 2 // Iff exactly one predicate is fulfilled // the value assigned to this predicate is evaluated. // Otherwise the given default value is evaluated and returned. // choose_by_def_lazy(c, [(is_even,a), (is_bigger_than_3,b)], 2) == Just a(); // choose_by_def_lazy(c, [(is_even,a), (is_bigger_than_3,b)], 5) == Just b(); // choose_by_def_lazy(c, [(is_even,a), (is_bigger_than_3,b)], 1) == c(); // choose_by_def_lazy(c, [(is_even,a), (is_bigger_than_3,b)], 4) == c(); template auto choose_by_def_lazy( const ValStub& def, const std::vector, ValStub>>& pairs, const Key& x) { return choose_by_def(def, pairs, x)(); } } // namespace fplus // // optimize.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // transform.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // split.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // internal/split.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include namespace fplus { namespace internal { template auto group_on_labeled_impl(GroupByCallable group, F f, const ContainerIn& xs) { const auto grouped = group(is_equal_by(f), xs); const auto attach_label = [f](const auto& g) { using std::begin; return std::make_pair(internal::invoke(f, *begin(g)), g); }; return fplus::transform(attach_label, grouped); } } } namespace fplus { // API search type: group_by : (((a, a) -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Arrange the elements into groups using a given predicate. // Only groups of consecutive elements are formed. // For a version scanning the whole container see group_globally_by. // group_by((==), [1,2,2,2,3,2,2,4,5,5]) == [[1],[2,2,2],[3],[2,2],[4],[5,5]] // BinaryPredicate p is a (not neccessarily transitive) connectivity check. // O(n) template > ContainerOut group_by(BinaryPredicate p, const ContainerIn& xs) { // ContainerOut is not deduced to // SameContNewType(ContainerIn, ContainerIn) // here, since ContainerIn could be a std::string. internal::check_binary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); ContainerOut result; if (is_empty(xs)) return result; typedef typename ContainerOut::value_type InnerContainerOut; *internal::get_back_inserter(result) = InnerContainerOut(1, xs.front()); for (auto it = ++std::begin(xs); it != std::end(xs); ++it) { if (internal::invoke(p, result.back().back(), *it)) *internal::get_back_inserter(result.back()) = *it; else *internal::get_back_inserter(result) = InnerContainerOut(1, *it); } return result; } // API search type: group_on : ((a -> b), [a]) -> [[a]] // fwd bind count: 1 // Arrange elements equal after applying a transformer into groups. // Only groups of consecutive elements are formed. // For a version scanning the whole container see group_globally_on. // group_on((mod 10), [12,22,34]) == [[12,22],[34]] // O(n) template auto group_on(F f, const ContainerIn& xs) { return group_by(is_equal_by(f), xs); } // API search type: group_on_labeled : ((a -> b), [a]) -> [(b, [a])] // fwd bind count: 1 // Arrange elements equal after applying a transformer into groups, // adding the transformation result as a label to the group. // Only groups of consecutive elements are formed. // For a version scanning the whole container see group_globally_on_labeled. // group_on_labeled((mod 10), [12,22,34]) == [(2,[12,22]), (4,[34])] // O(n) template auto group_on_labeled(F f, const ContainerIn& xs) { const auto group = [](auto f1, const auto& xs1) { return group_by(f1, xs1); }; return internal::group_on_labeled_impl(group, f, xs); } // API search type: group : [a] -> [[a]] // fwd bind count: 0 // Arrange equal elements into groups. // Only groups of consecutive elements are formed. // For a version scanning the whole container see group_globally. // group([1,2,2,2,3,2,2,4,5,5]) == [[1],[2,2,2],[3],[2,2],[4],[5,5]] // O(n) template > ContainerOut group(const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); typedef typename ContainerIn::value_type T; auto pred = [](const T& x, const T& y) { return x == y; }; return group_by(pred, xs); } // API search type: group_globally_by : (((a, a) -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Arrange equal elements into groups. // group_globally_by((==), [1,2,2,2,3,2,2,4,5,5]) // == [[1],[2,2,2,2,2],[3],[4],[5,5]] // BinaryPredicate p is a // transitive (whenever p(x,y) and p(y,z), then also p(x,z)) equality check. // O(n^2) // If you need O(n*log(n)), sort and then use group_by template > ContainerOut group_globally_by(BinaryPredicate p, const ContainerIn& xs) { internal::check_binary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); typedef typename ContainerOut::value_type InnerContainerOut; ContainerOut result; for (const auto& x : xs) { bool found = false; for (auto& ys : result) { if (internal::invoke(p, x, ys.back())) { *internal::get_back_inserter(ys) = x; found = true; break; } } if (!found) { *internal::get_back_inserter(result) = InnerContainerOut(1, x); } } return result; } // API search type: group_globally_on : ((a -> b), [a]) -> [[a]] // fwd bind count: 1 // Arrange elements equal after applying a transformer into groups. // group_globally_on((mod 10), [12,34,22]) == [[12,22],[34]] // O(n^2) // If you need O(n*log(n)), sort and then use group_on template auto group_globally_on(F f, const ContainerIn& xs) { return group_globally_by(is_equal_by(f), xs); } // API search type: group_globally_on_labeled : ((a -> b), [a]) -> [(b, [a])] // fwd bind count: 1 // Arrange elements equal after applying a transformer into groups, // adding the transformation result as a label to the group. // group_globally_on_labeled((mod 10), [12,34,22]) == [(2,[12,22]),(4, [34])] // O(n^2) // If you need O(n*log(n)), sort and then use group_on_labeled template auto group_globally_on_labeled(F f, const ContainerIn& xs) { const auto group = [](auto f1, const auto& xs1) { return group_globally_by(f1, xs1); }; return internal::group_on_labeled_impl(group, f, xs); } // API search type: group_globally : [a] -> [[a]] // fwd bind count: 0 // Arrange equal elements into groups. // group_globally([1,2,2,2,3,2,2,4,5,5]) == [[1],[2,2,2,2,2],[3],[4],[5,5]] // O(n^2) // If you need O(n*log(n)), sort and then use group template > ContainerOut group_globally(const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); typedef typename ContainerIn::value_type T; auto pred = [](const T& x, const T& y) { return x == y; }; return group_globally_by(pred, xs); } // API search type: cluster_by : (((a, a) -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Groups connected components, stable regarding initial order. // cluster_by(\x y -> abs (y - x) <= 3), [2,3,6,4,12,11,20,23,8,4]) // == [[2,3,6,4,12,11,8,4],[20,23]] // BinaryPredicate p is a connectivity check, being // a) commutative (p(x,y) = p(y,x)) // b) reflexive (p(x,x) = true) // c) not neccessarily transitive, but can be // O(n^2), memory complexity also O(n^2) template > ContainerOut cluster_by(BinaryPredicate p, const ContainerIn& xs) { internal::check_binary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); typedef std::vector bools; bools zero_filled_row(size_of_cont(xs), 0); // adjecency matrix typedef std::vector boolss; boolss adj_mat(size_of_cont(xs), zero_filled_row); for (const auto& idx_and_val_y : enumerate(xs)) { auto idx_y = idx_and_val_y.first; auto val_y = idx_and_val_y.second; for (const auto& idx_and_val_x : enumerate(xs)) { auto idx_x = idx_and_val_x.first; auto val_x = idx_and_val_x.second; if (internal::invoke(p, val_y, val_x)) { adj_mat[idx_y][idx_x] = 1; } } } bools already_used = zero_filled_row; auto is_already_used = [&](std::size_t i) -> bool { return already_used[i] != 0; }; typedef std::vector idxs; typedef std::vector idxss; auto bools_to_idxs = [](const bools& activations) -> idxs { auto unsigned_char_to_bool = [](unsigned char x) { return x != 0; }; return find_all_idxs_by(unsigned_char_to_bool, activations); }; idxss idx_clusters; std::function process_idx = [&](std::size_t idx) -> void { auto connected_idxs = bools_to_idxs(adj_mat[idx]); auto new_connected_idxs = drop_if(is_already_used, connected_idxs); if (is_empty(new_connected_idxs)) { return; } idx_clusters.back() = append(idx_clusters.back(), new_connected_idxs); for (const auto& new_idx : new_connected_idxs) { already_used[new_idx] = 1; } for (const auto& new_idx : new_connected_idxs) { process_idx(new_idx); } }; typedef typename ContainerOut::value_type InnerContainerOut; for (const auto& idx : all_idxs(xs)) { if (is_already_used(idx)) { continue; } *internal::get_back_inserter(idx_clusters) = idxs(); *internal::get_back_inserter(idx_clusters.back()) = idx; already_used[idx] = 1; process_idx(idx); } typedef typename ContainerIn::value_type T; auto idx_to_val = [&](std::size_t idx) -> T { return elem_at_idx(idx, xs); }; auto idxs_to_vals = [&](const idxs& val_idxs) -> InnerContainerOut { return transform_convert(idx_to_val, sort(val_idxs)); }; return transform_convert(idxs_to_vals, idx_clusters); } // API search type: split_by : ((a -> Bool), Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every element fulfilling a predicate. // The splitting elements are discarded. // split_by(is_even, true, [1,3,2,2,5,5,3,6,7,9]) == [[1,3],[],[5,5,3],[7,9]] // also known as split_when // O(n) template > ContainerOut split_by (UnaryPredicate pred, bool allow_empty, const ContainerIn& xs) { internal::check_unary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); if (allow_empty && is_empty(xs)) { return {{}}; } ContainerOut result; auto itOut = internal::get_back_inserter(result); auto start = std::begin(xs); while (start != std::end(xs)) { const auto stop = std::find_if(start, std::end(xs), pred); if (start != stop || allow_empty) { *itOut = { start, stop }; } if (stop == std::end(xs)) { break; } start = internal::add_to_iterator(stop); if (allow_empty && start == std::end(xs)) { *itOut = typename ContainerOut::value_type(); } } return result; } // API search type: split_by_keep_separators : ((a -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Split a sequence at every element fulfilling a predicate. // The splitting elements are kept. // split_by_keep_separators(is_even, true, [1,3,2,2,5,5,3,6,7,9]) // == [[1,3],[2],[2,5,5,3],[6,7,9]] // O(n) template > ContainerOut split_by_keep_separators (UnaryPredicate pred, const ContainerIn& xs) { internal::check_unary_predicate_for_container(); static_assert(std::is_same::value, "Containers do not match."); ContainerOut result; if (is_empty(xs)) return result; auto itOut = internal::get_back_inserter(result); auto start = std::begin(xs); while (start != std::end(xs)) { const auto stop = std::find_if( internal::add_to_iterator(start), std::end(xs), pred); *itOut = { start, stop }; if (stop == std::end(xs)) { break; } start = stop; } return result; } // API search type: split : (a, Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every element equal to x. // The splitting elements are discarded. // split(0, true, [1,3,2,0,0,6,0,7,5]) == [[1,3,2],[],[6],[7,5]] // O(n) template auto split(const T& x, bool allow_empty, const ContainerIn& xs) { return split_by(is_equal_to(x), allow_empty, xs); } // API search type: split_one_of : ([a], Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every element present in delimiters. // The splitting elements are discarded. // Also known as split_words_by_many. // split_one_of([0,3], true [1,3,2,0,0,6,0,7,5]) == [[1],[2],[],[6],[7,5]] // split_one_of(" o", false, "How are u?") == ["H","w","are","u?"] // O(n) template auto split_one_of( const ContainerDelims delimiters, bool allow_empty, const ContainerIn& xs) { const auto pred = [&](const typename ContainerIn::value_type& x) -> bool { return is_elem_of(x, delimiters); }; return split_by(pred, allow_empty, xs); } // API search type: split_keep_separators : ((a -> Bool), [a]) -> [[a]] // fwd bind count: 1 // Split a sequence at every element equal to x. // The splitting elements are kept. // split_keep_separators(2, true, [1,3,2,2,5,5,3,2,7,9]) // == [[1,3],[2],[2,5,5,3],[6,7,9]] // O(n) template auto split_keep_separators(const T& x, const ContainerIn& xs) { return split_by_keep_separators(is_equal_to(x), xs); } // API search type: split_at_idx : (Int, [a]) -> ([a], [a]) // fwd bind count: 1 // Split a sequence at a specific position. // split_at_idx(2, [0,1,2,3,4]) == ([0,1],[2,3,4]) template std::pair split_at_idx (std::size_t idx, const Container& xs) { assert(idx <= size_of_cont(xs)); return make_pair(get_segment(0, idx, xs), get_segment(idx, size_of_cont(xs), xs)); } // API search type: insert_at_idx : (Int, a, [a]) -> [a] // fwd bind count: 2 // Insert an element into a sequence at a specific position. // insert_at_idx(2, 0, [1,2,3,4]) == [1,2,0,3,4]. template Container insert_at_idx(std::size_t idx, const T& x, const Container& xs) { const auto splitted = split_at_idx(idx, xs); return concat(std::vector( { splitted.first, singleton_seq(x), splitted.second })); } // API search type: partition : ((a -> Bool), [a]) -> ([a], [a]) // fwd bind count: 1 // Split a sequence into two groups. // The first group contains all elements fulfilling the predicate. // The second group contains the remaining elements. // partition(is_even, [0,1,1,3,7,2,3,4]) == ([0,2,4],[1,1,3,7,3]) template std::pair partition (UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); Container matching; Container notMatching; auto itOutMatching = internal::get_back_inserter(matching); auto itOutNotMatching = internal::get_back_inserter(notMatching); for (const auto& x : xs) { if (internal::invoke(pred, x)) *itOutMatching = x; else *itOutNotMatching = x; } return make_pair(matching, notMatching); } // API search type: split_at_idxs : ([Int], [a]) -> [[a]] // fwd bind count: 1 // Split a sequence at specific indices. // split_at_idxs([2,5], [0,1,2,3,4,5,6,7]) == [[0,1],[2,3,4],[5,6,7]] // split_at_idxs([2,5,5], [0,1,2,3,4,5,6,7]) == [[0,1],[2,3,4],[],[5,6,7]] template > ContainerOut split_at_idxs(const ContainerIdxs& idxsIn, const ContainerIn& xs) { static_assert(std::is_same::value, "Indices must be std::size_t"); static_assert(std::is_same::value, "Containers do not match."); ContainerIdxs idxStartC = {0}; ContainerIdxs idxEndC = {size_of_cont(xs)}; std::vector containerIdxss = {idxStartC, idxsIn, idxEndC}; auto idxs = concat(containerIdxss); auto idxsClean = sort(idxs); ContainerOut result; internal::prepare_container(result, size_of_cont(idxsClean) - 1); auto itOut = internal::get_back_inserter(result); auto idxPairs = overlapping_pairs(idxsClean); for (const auto& idxPair : idxPairs) { *itOut = get_segment(idxPair.first, idxPair.second, xs); } return result; } // API search type: split_every : (Int, [a]) -> [[a]] // fwd bind count: 1 // Split a sequence every n elements. // split_every(3, [0,1,2,3,4,5,6,7]) == [[0,1,2],[3,4,5],[6,7]] // Also known as chunk or chunks. template > ContainerOut split_every(std::size_t n, const ContainerIn& xs) { return split_at_idxs< std::vector, ContainerIn, ContainerOut>( numbers_step( n, size_of_cont(xs), n), xs); } // API search type: split_by_token : ([a], Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every segment matching a token. // split_by_token(", ", true, "foo, bar, baz") == ["foo", "bar", "baz"] template > ContainerOut split_by_token(const ContainerIn& token, bool allow_empty, const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); const auto token_begins = find_all_instances_of_token_non_overlapping(token, xs); const auto token_ends = transform(add_to(size_of_cont(token)), token_begins); assert(is_sorted(interweave(token_begins, token_ends))); typedef std::vector idx_vec; const auto segments = zip( fplus::append(idx_vec(1, 0), token_ends), fplus::append(token_begins, idx_vec(1, size_of_cont(xs)))); ContainerOut result; auto itOut = internal::get_back_inserter(result); for (const auto& segment : segments) { if (segment.first != segment.second || allow_empty) *itOut = get_segment(segment.first, segment.second, xs); } return result; } // API search type: run_length_encode_by : (((a, a) -> Bool), [a]) -> [(Int, a)] // fwd bind count: 1 // RLE using a specific binary predicate as equality check. // run_length_encode_by((==),[1,2,2,2,2,3,3,2)) == [(1,1),(4,2),(2,3),(1,2)] template >> ContainerOut run_length_encode_by(BinaryPredicate pred, const ContainerIn& xs) { internal::check_binary_predicate_for_container(); ContainerOut result; auto groups = group_by(pred, xs); auto group_to_pair = [](const ContainerIn& group) -> std::pair { return std::make_pair(size_of_cont(group), group.front()); }; return transform(group_to_pair, groups); } // API search type: run_length_encode : [a] -> [(Int, a)] // fwd bind count: 0 // RLE. // run_length_encode([1,2,2,2,2,3,3,2)) == [(1,1),(4,2),(2,3),(1,2)] template auto run_length_encode(const ContainerIn& xs) { return run_length_encode_by(is_equal, xs); } // API search type: run_length_decode : [(Int, a)] -> [a] // fwd bind count: 0 // Inverse operation to run_length_encode. // run_length_decode([(1,1),(4,2),(2,3),(1,2)]) == [1,2,2,2,2,3,3,2) template auto run_length_decode(const ContainerIn& pairs) { static_assert(std::is_convertible::value, "Count type must be convertible to std::size_t."); const auto pair_to_vec = [](const Pair& p) { return replicate(p.first, p.second); }; return concat(transform(pair_to_vec, pairs)); } // API search type: span : ((a -> Bool), [a]) -> ([a], [a]) // fwd bind count: 1 // span, applied to a predicate p and a list xs, // returns a tuple where first element is longest prefix (possibly empty) // of xs of elements that satisfy p // and second element is the remainder of the list. // span(is_even, [0,2,4,5,6,7,8]) == ([0,2,4], [5,6,7,8]) template std::pair span(UnaryPredicate pred, const Container& xs) { auto maybeIdx = find_first_idx_by(logical_not(pred), xs); return { take(just_with_default(size_of_cont(xs), maybeIdx), xs), drop(just_with_default(size_of_cont(xs), maybeIdx), xs) }; } // API search type: divvy : (Int, Int, [a]) -> [[a]] // fwd bind count: 2 // Generates subsequences overlapping with a specific step. // divvy(5, 2, [0,1,2,3,4,5,6,7,8,9]) == [[0,1,2,3,4],[2,3,4,5,6],[4,5,6,7,8]] // divvy(length, 1, xs) is also known as aperture // divvy(1, step, xs) is also known as stride // (but withouts the nested lists in the result) template > ContainerOut divvy(std::size_t length, std::size_t step, const ContainerIn& xs) { assert(length > 0); assert(step > 0); const auto start_idxs = numbers_step( 0, size_of_cont(xs) - (length - 1), step); ContainerOut result; internal::prepare_container(result, size_of_cont(start_idxs)); auto itOut = internal::get_back_inserter(result); for (const auto start_idx : start_idxs) { *itOut = get_segment(start_idx, start_idx + length, xs); } return result; } // API search type: aperture : (Int, [a]) -> [[a]] // fwd bind count: 1 // Generates overlapping subsequences. // aperture(5, [0,1,2,3,4,5,6]) == [[0,1,2,3,4],[1,2,3,4,5],[2,3,4,5,6]] template > ContainerOut aperture(std::size_t length, const ContainerIn& xs) { assert(length > 0); const auto start_idxs = numbers( 0, size_of_cont(xs) - (length - 1)); ContainerOut result; internal::prepare_container(result, size_of_cont(start_idxs)); auto itOut = internal::get_back_inserter(result); for (const auto start_idx : start_idxs) { *itOut = get_segment(start_idx, start_idx + length, xs); } return result; } // API search type: stride : (Int, [a]) -> [a] // fwd bind count: 1 // Keeps every nth element. // stride(3, [0,1,2,3,4,5,6,7]) == [0,3,6] template Container stride(std::size_t step, const Container& xs) { assert(step > 0); Container ys; auto it = internal::get_back_inserter(ys); auto it_in = std::begin(xs); std::size_t i = 0; const auto xs_size = size_of_cont(xs); while(it_in != std::end(xs)) { *it = *it_in; std::size_t increment = std::min(step, xs_size - i); internal::advance_iterator(it_in, increment); i += increment; } return ys; } // API search type: winsorize : (Float, [Float]) -> [Float] // fwd bind count: 1 // Winsorizing // winsorize(0.1, [1,3,4,4,4,4,4,4,6,8]) == [3,3,4,4,4,4,4,4,6,6] template Container winsorize(double trim_ratio, const Container& xs) { if (size_of_cont(xs) < 2) { return xs; } trim_ratio = std::max(trim_ratio, 0.0); const auto xs_sorted = sort(xs); std::size_t amount = floor( trim_ratio * static_cast(size_of_cont(xs_sorted))); amount = std::min(size_of_cont(xs_sorted) / 2, amount); const auto parts = split_at_idxs( std::vector({amount, size_of_cont(xs_sorted) - amount}), xs_sorted); assert(size_of_cont(parts) == 3); typedef typename Container::value_type T; if (is_empty(parts[1])) { return Container(size_of_cont(xs_sorted), median(xs_sorted)); } else { const T lower = parts[1].front(); const T upper = parts[1].back(); const auto result = concat(std::vector({ Container(amount, lower), parts[1], Container(amount, upper)})); assert(size_of_cont(result) == size_of_cont(xs_sorted)); return result; } } // API search type: separate_on : ((a -> b), [a]) -> [[a]] // fwd bind count: 1 // Separate elements equal after applying a transformer into groups. // separate_on((mod 10), [12,22,34]) == [[12,34],[22]] template > ContainerOut separate_on(F f, const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); ContainerOut result; if (is_empty(xs)) { return result; } const auto groups = group_globally_on(f, xs); bool found = true; auto itOut = internal::get_back_inserter(result); std::size_t index = 0; while (found) { typename ContainerOut::value_type sub_result; found = false; auto itOutInner = internal::get_back_inserter(sub_result); for (auto& group: groups) { if (size_of_cont(group) > index) { *itOutInner = group[index]; found = true; } } if (found) { *itOut = sub_result; ++index; } } return result; } // API search type: separate : [a] -> [[a]] // fwd bind count: 0 // Separate equal elements into groups. // separate([1, 2, 2, 3, 3, 4, 4, 4]) == [[1, 2, 3, 4], [2, 3, 4], [4]] template > ContainerOut separate(const ContainerIn& xs) { static_assert(std::is_same::value, "Containers do not match."); typedef typename ContainerIn::value_type T; return separate_on(identity, xs); } } // namespace fplus #include #include #include #include #include namespace fplus { // API search type: transform_with_idx : (((Int, a) -> b), [a]) -> [b] // fwd bind count: 1 // Apply a function to every index and corresponding element of a sequence. // transform_with_idx(f, [6, 4, 7]) == [f(0, 6), f(1, 4), f(2, 7)] template ::type> ContainerOut transform_with_idx(F f, const ContainerIn& xs) { internal::trigger_static_asserts(); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); std::size_t idx = 0; for (const auto& x : xs) { *it = internal::invoke(f, idx++, x); } return ys; } // API search type: transform_and_keep_justs : ((a -> Maybe b), [a]) -> [b] // fwd bind count: 1 // Map function over values and drop resulting nothings. // Also known as filter_map. template auto transform_and_keep_justs(F f, const ContainerIn& xs) { using X = typename ContainerIn::value_type; internal:: trigger_static_asserts(); using ContainerOut = typename internal::same_cont_new_t< ContainerIn, typename std::decay_t>::type>::type; auto transformed = transform(f, xs); return justs(transformed); } // API search type: transform_and_keep_oks : ((a -> Result b), [a]) -> [b] // fwd bind count: 1 // Map function over values and drop resulting errors. template auto transform_and_keep_oks(F f, const ContainerIn& xs) { using X = typename ContainerIn::value_type; internal:: trigger_static_asserts(); using ContainerOut = typename internal::same_cont_new_t< ContainerIn, typename std::decay_t>::ok_t>::type; auto transformed = transform(f, xs); return oks(transformed); } // API search type: transform_and_concat : ((a -> [b]), [a]) -> [b] // fwd bind count: 1 // Map function over values and concat results. // Also known as flat_map or concat_map. template auto transform_and_concat(F f, const ContainerIn& xs) { internal::trigger_static_asserts(); return concat(transform(f, xs)); } // API search type: replicate_elems : (Int, [a]) -> [a] // fwd bind count: 1 // Replicate every element n times, concatenate the result. // replicate_elems(3, [1,2]) == [1, 1, 1, 2, 2, 2] template Container replicate_elems(std::size_t n, const Container& xs) { typedef typename Container::value_type T; return transform_and_concat(bind_1st_of_2(replicate, n), xs); } // API search type: interleave : [[a]] -> [a] // fwd bind count: 0 // Return a sequence that contains elements from the provided sequences // in alternating order. If one list runs out of items, // appends the items from the remaining list. // interleave([[1,2,3],[4,5],[6,7,8]]) == [1,4,6,2,5,7,3,8] template ContainerOut interleave(const ContainerIn& xss) { typedef typename ContainerIn::value_type inner_t; typedef std::vector its_t; const auto inner_cbegin = [](const inner_t& xs) { return xs.cbegin(); }; const auto inner_cend = [](const inner_t& xs) { return xs.cend(); }; auto it_pairs = zip( transform_convert(inner_cbegin, xss), transform_convert(inner_cend, xss)); ContainerOut result; const std::size_t length = sum(transform(size_of_cont, xss)); internal::prepare_container(result, length); auto it_out = internal::get_back_inserter(result); bool still_appending = true; while (still_appending) { still_appending = false; for (auto& it_pair : it_pairs) { if (it_pair.first != it_pair.second) { *it_out = *it_pair.first; still_appending = true; ++it_pair.first; } } } return result; } // API search type: transpose : [[a]] -> [[a]] // fwd bind count: 0 // Transpose a nested sequence aka. table aka. two-dimensional matrix. // transpose([[1,2,3],[4,5,6],[7,8,9]]) == [[1,4,7],[2,5,8],[3,6,9]] // transpose([[1,2,3],[4,5],[7,8,9]]) == [[1,4,7],[2,5,8],[3,9]] template Container transpose(const Container& rows) { if (is_empty(rows)) { return {}; } return split_every( size_of_cont(rows), interleave(rows)); } namespace internal { template Container shuffle(internal::reuse_container_t, std::uint_fast32_t seed, Container&& xs) { std::mt19937 g(seed); std::shuffle(std::begin(xs), std::end(xs), g); return std::forward(xs); } template Container shuffle(internal::create_new_container_t, std::uint_fast32_t seed, const Container& xs) { Container ys = xs; return internal::shuffle(internal::reuse_container_t(), seed, std::move(ys)); } } // namespace internal // API search type: shuffle : (Int, [a]) -> [a] // fwd bind count: 1 // Returns a randomly shuffled version of xs. // Example call: shuffle(std::mt19937::default_seed, xs); // Example call: shuffle(std::random_device()(), xs); template auto shuffle(std::uint_fast32_t seed, Container&& xs) { return(internal::shuffle(internal::can_reuse_v{}, seed, std::forward(xs))); } // API search type: sample : (Int, Int, [a]) -> [a] // fwd bind count: 2 // Returns n random elements from xs without replacement. // n has to be smaller than or equal to the number of elements in xs. // Also known as rnd_select. // Example call: sample(std::mt19937::default_seed, 3, xs); // Example call: sample(std::random_device()(), 3, xs); template Container sample(std::uint_fast32_t seed, std::size_t n, const Container& xs) { assert(n <= size_of_cont(xs)); return get_segment(0, n, shuffle(seed, xs)); } // API search type: random_element : (Int, [a]) -> a // fwd bind count: 1 // Returns one random element from xs. // xs must be non-empty. // Example call: random_element(std::mt19937::default_seed, xs) // Example call: random_element(std::random_device()(), xs) // Also known as choice. template typename Container::value_type random_element( std::uint_fast32_t seed, const Container& xs) { assert(is_not_empty(xs)); std::mt19937 gen(seed); std::uniform_int_distribution dis(0, size_of_cont(xs) - 1); return elem_at_idx(dis(gen), xs); } // API search type: random_elements : (Int, Int, [a]) -> a // fwd bind count: 2 // Returns random elements from xs with replacement. // xs must be non-empty. // Example call: random_elements(std::mt19937::default_seed, 10, xs) // Example call: random_elements(std::random_device()(), 10, xs) template Container random_elements( std::uint_fast32_t seed, std::size_t n, const Container& xs) { assert(is_not_empty(xs)); std::mt19937 gen(seed); std::uniform_int_distribution dis(0, size_of_cont(xs) - 1); const auto draw = [&]() -> typename Container::value_type { return elem_at_idx(dis(gen), xs); }; return generate(draw, n); } // API search type: apply_functions : ([(a -> b)], a) -> [b] // fwd bind count: 1 // Applies a list of functions to a value. template auto apply_functions(const FunctionContainer& functions, const FIn& x) { internal::trigger_static_asserts(); using FOut = std::decay_t>; using ContainerOut = typename internal::same_cont_new_t::type; ContainerOut ys; internal::prepare_container(ys, size_of_cont(functions)); auto it = internal::get_back_inserter(ys); for (const auto& f : functions) { *it = internal::invoke(f, x); } return ys; } // API search type: apply_function_n_times : ((a -> a), Int, a) -> a // fwd bind count: 2 // Applies a functional n times in a row. template auto apply_function_n_times(F f, std::size_t n, const FIn& x) { internal::trigger_static_asserts(); using FOut = std::decay_t>; static_assert(std::is_same::value, "Input and output of F must be the same type."); if (n == 0) { return x; } FOut y = internal::invoke(f, x); for (std::size_t i = 1; i < n; ++i) { y = internal::invoke(f, y); } return y; } // API search type: transform_parallelly : ((a -> b), [a]) -> [b] // fwd bind count: 1 // transform_parallelly((*2), [1, 3, 4]) == [2, 6, 8] // Same as transform, but can utilize multiple CPUs by using std::launch::async. // Only makes sense if one run of the provided function // takes enough time to justify the synchronization overhead. // One thread per container element is spawned. // Check out transform_parallelly_n_threads to limit the number of threads. template auto transform_parallelly(F f, const ContainerIn& xs) { using ContainerOut = typename internal:: same_cont_new_t_from_unary_f::type; using X = typename ContainerIn::value_type; internal::trigger_static_asserts(); auto handles = transform([&f](const X& x) { return std::async(std::launch::async, [&x, &f]() { return internal::invoke(f, x); }); }, xs); ContainerOut ys; internal::prepare_container(ys, size_of_cont(xs)); auto it = internal::get_back_inserter(ys); for (auto& handle : handles) { *it = handle.get(); } return ys; } // API search type: transform_parallelly_n_threads : (Int, (a -> b), [a]) -> [b] // fwd bind count: 2 // transform_parallelly_n_threads(4, (*2), [1, 3, 4]) == [2, 6, 8] // Same as transform, but uses n threads in parallel. // Only makes sense if one run of the provided function // takes enough time to justify the synchronization overhead. // Can be used for applying the MapReduce pattern. template auto transform_parallelly_n_threads(std::size_t n, F f, const ContainerIn& xs) { using ContainerOut = typename internal:: same_cont_new_t_from_unary_f::type; using X = typename ContainerIn::value_type; using Y = internal::invoke_result_t; using x_ptr_t = const X*; auto queue = transform_convert>( [](const X& x) -> x_ptr_t { return &x; }, xs); std::mutex queue_mutex; std::mutex thread_results_mutex; std::map> thread_results; std::size_t queue_idx = 0; const auto worker_func = [&]() { for (;;) { std::size_t idx = std::numeric_limits::max(); x_ptr_t x_ptr = nullptr; { std::lock_guard queue_lock(queue_mutex); if (queue_idx == queue.size()) { return; } idx = queue_idx; x_ptr = queue[idx]; ++queue_idx; } const auto y = internal::invoke(f, *x_ptr); { std::lock_guard thread_results_lock( thread_results_mutex); thread_results.insert(std::make_pair(idx, y)); } } }; const auto create_thread = [&]() -> std::thread { return std::thread(worker_func); }; auto threads = generate>(create_thread, n); for (auto& thread : threads) { thread.join(); } return get_map_values( thread_results); } // API search type: reduce_parallelly : (((a, a) -> a), a, [a]) -> a // fwd bind count: 2 // reduce_parallelly((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 // Same as reduce, but can utilize multiple CPUs by using std::launch::async. // Combines the initial value and all elements of the sequence // using the given function in unspecified order. // The set of f, init and value_type should form a commutative monoid. // One thread per container element is spawned. // Check out reduce_parallelly_n_threads to limit the number of threads. template typename Container::value_type reduce_parallelly( F f, const typename Container::value_type& init, const Container& xs) { if (is_empty(xs)) { return init; } else if (size_of_cont(xs) == 1) { return internal::invoke(f, init, xs.front()); } else { typedef typename Container::value_type T; const auto f_on_pair = [f](const std::pair& p) -> T { return internal::invoke(f, p.first, p.second); }; auto transform_result = transform_parallelly(f_on_pair, adjacent_pairs(xs)); if (size_of_cont(xs) % 2 == 1) { transform_result.push_back(last(xs)); } return reduce_parallelly(f, init, transform_result); } } // API search type: reduce_parallelly_n_threads : (Int, ((a, a) -> a), a, [a]) -> a // fwd bind count: 3 // reduce_parallelly_n_threads(2, (+), 0, [1, 2, 3]) == (0+1+2+3) == 6 // Same as reduce, but can utilize multiple CPUs by using std::launch::async. // Combines the initial value and all elements of the sequence // using the given function in unspecified order. // The set of f, init and value_type should form a commutative monoid. template typename Container::value_type reduce_parallelly_n_threads( std::size_t n, F f, const typename Container::value_type& init, const Container& xs) { if (is_empty(xs)) { return init; } else if (size_of_cont(xs) == 1) { return internal::invoke(f, init, xs.front()); } else { typedef typename Container::value_type T; const auto f_on_pair = [f](const std::pair& p) -> T { return internal::invoke(f, p.first, p.second); }; auto transform_result = transform_parallelly_n_threads(n, f_on_pair, adjacent_pairs(xs)); if (size_of_cont(xs) % 2 == 1) { transform_result.push_back(last(xs)); } return reduce_parallelly_n_threads(n, f, init, transform_result); } } // API search type: reduce_1_parallelly : (((a, a) -> a), [a]) -> a // fwd bind count: 1 // reduce_1_parallelly((+), [1, 2, 3]) == (1+2+3) == 6 // Same as reduce_1, but can utilize multiple CPUs by using std::launch::async. // Joins all elements of the sequence using the given function // in unspecified order. // The set of f and value_type should form a commutative semigroup. // One thread per container element is spawned. // Check out reduce_1_parallelly_n_threads to limit the number of threads. template typename Container::value_type reduce_1_parallelly(F f, const Container& xs) { assert(is_not_empty(xs)); if (size_of_cont(xs) == 1) { return xs.front(); } else { typedef typename Container::value_type T; const auto f_on_pair = [f](const std::pair& p) -> T { return internal::invoke(f, p.first, p.second); }; auto transform_result = transform_parallelly(f_on_pair, adjacent_pairs(xs)); if (size_of_cont(xs) % 2 == 1) { transform_result.push_back(last(xs)); } return reduce_1_parallelly(f, transform_result); } } // API search type: reduce_1_parallelly_n_threads : (Int, ((a, a) -> a), [a]) -> a // fwd bind count: 2 // reduce_1_parallelly_n_threads(2, (+), [1, 2, 3]) == (1+2+3) == 6 // Same as reduce_1, but can utilize multiple CPUs by using std::launch::async. // Joins all elements of the sequence using the given function // in unspecified order. // The set of f and value_type should form a commutative semigroup. template typename Container::value_type reduce_1_parallelly_n_threads( std::size_t n, F f, const Container& xs) { assert(is_not_empty(xs)); if (size_of_cont(xs) == 1) { return xs.front(); } else { typedef typename Container::value_type T; const auto f_on_pair = [f](const std::pair& p) -> T { return internal::invoke(f, p.first, p.second); }; auto transform_result = transform_parallelly_n_threads(n, f_on_pair, adjacent_pairs(xs)); if (size_of_cont(xs) % 2 == 1) { transform_result.push_back(last(xs)); } return reduce_1_parallelly_n_threads(n, f, transform_result); } } // API search type: keep_if_parallelly : ((a -> Bool), [a]) -> [a] // fwd bind count: 1 // Same as keep_if but using multiple threads. // Can be useful if calling the predicate takes some time. // keep_if_parallelly(is_even, [1, 2, 3, 2, 4, 5]) == [2, 2, 4] // One thread per container element is spawned. // Check out keep_if_parallelly_n_threads to limit the number of threads. template Container keep_if_parallelly(Pred pred, const Container& xs) { // Avoid a temporary std::vector. const auto idxs = find_all_idxs_by( is_equal_to(1), transform_parallelly([pred](const auto & x) -> std::uint8_t { return pred(x) ? 1 : 0; }, xs)); return elems_at_idxs(idxs, xs); } // API search type: keep_if_parallelly_n_threads : (Int, (a -> Bool), [a]) -> [a] // fwd bind count: 2 // Same as keep_if but using multiple threads. // Can be useful if calling the predicate takes some time. // keep_if_parallelly_n_threads(3, is_even, [1, 2, 3, 2, 4, 5]) == [2, 2, 4] template Container keep_if_parallelly_n_threads( std::size_t n, Pred pred, const Container& xs) { // Avoid a temporary std::vector. const auto idxs = find_all_idxs_by( is_equal_to(1), transform_parallelly_n_threads(n, [pred](const auto & x) -> std::uint8_t { return pred(x) ? 1 : 0; }, xs)); return elems_at_idxs(idxs, xs); } // API search type: transform_reduce : ((a -> b), ((b, b) -> b), b, [a]) -> b // fwd bind count: 3 // transform_reduce(square, add, 0, [1,2,3]) == 0+1+4+9 = 14 // The set of binary_f, init and unary_f::output should form a // commutative monoid. template auto transform_reduce(UnaryF unary_f, BinaryF binary_f, const Acc& init, const Container& xs) { return reduce(binary_f, init, transform(unary_f, xs)); } // API search type: transform_reduce_1 : ((a -> b), ((b, b) -> b), [a]) -> b // fwd bind count: 2 // transform_reduce_1(square, add, [1,2,3]) == 0+1+4+9 = 14 // The set of binary_f, and unary_f::output should form // a commutative semigroup. template auto transform_reduce_1(UnaryF unary_f, BinaryF binary_f, const Container& xs) { return reduce_1(binary_f, transform(unary_f, xs)); } // API search type: transform_reduce_parallelly : ((a -> b), ((b, b) -> b), b, [a]) -> b // fwd bind count: 3 // transform_reduce_parallelly(square, add, 0, [1,2,3]) == 0+1+4+9 = 14 // Also known as map_reduce. // The set of binary_f, init and unary_f::output // should form a commutative monoid. // One thread per container element is spawned. // Check out transform_reduce_parallelly_n_threads to limit the number of threads. template auto transform_reduce_parallelly(UnaryF unary_f, BinaryF binary_f, const Acc& init, const Container& xs) { return reduce_parallelly(binary_f, init, transform_parallelly(unary_f, xs)); } // API search type: transform_reduce_parallelly_n_threads : (Int, (a -> b), ((b, b) -> b), b, [a]) -> b // fwd bind count: 4 // transform_reduce_parallelly_n_threads(2, square, add, 0, [1,2,3]) == 0+1+4+9 = 14 // Also known as map_reduce. // The set of binary_f, init and unary_f::output // should form a commutative monoid. template auto transform_reduce_parallelly_n_threads(std::size_t n, UnaryF unary_f, BinaryF binary_f, const Acc& init, const Container& xs) { return reduce_parallelly_n_threads( n, binary_f, init, transform_parallelly_n_threads(n, unary_f, xs)); } // API search type: transform_reduce_1_parallelly : ((a -> b), ((b, b) -> b), [a]) -> b // fwd bind count: 2 // transform_reduce_1_parallelly(square, add, [1,2,3]) == 0+1+4+9 = 14 // Also Known as map_reduce. // The set of binary_f, and unary_f::output // should form a commutative semigroup. // One thread per container element is spawned. // Check out transform_reduce_1_parallelly_n_threads to limit the number of threads. template auto transform_reduce_1_parallelly(UnaryF unary_f, BinaryF binary_f, const Container& xs) { return reduce_1_parallelly(binary_f, transform_parallelly(unary_f, xs)); } // API search type: transform_reduce_1_parallelly_n_threads : (Int, (a -> b), ((b, b) -> b), [a]) -> b // fwd bind count: 3 // transform_reduce_1_parallelly_n_threads(2, square, add, [1,2,3]) == 0+1+4+9 = 14 // Also Known as map_reduce. // The set of binary_f, and unary_f::output // should form a commutative semigroup. template auto transform_reduce_1_parallelly_n_threads(std::size_t n, UnaryF unary_f, BinaryF binary_f, const Container& xs) { return reduce_1_parallelly_n_threads( n, binary_f, transform_parallelly_n_threads(n, unary_f, xs)); } } // namespace fplus #include #include #include namespace fplus { // Optimizes the initial position to the nearest local minimum // in regards to the objective_function // using numerical gradient descent based on the epsilon neighborhood. // momentum_conservation should be in [0, 1). A low value means much decay. // If no fixed step size is provided, each step advances by the length // of the gradient. // In both cases the step is scaled with a step factor, starting at 1.0. // If one iteration results in no further improvement, // the step factor is reduced by a factor of 0.5. // The callback is executed with // iteration, step factor, momentum and current position // after every iteration. // A initial step factor other than 1.0 in all dimensions // can be emulated by scaling ones objective function accordingly. // Optimization stops if one of the provided criteria is met. // minimize_downhill<1>(\x -> square(x[0] + 2), 0.0001, 0.01, {123})[0] == -2; template > pos_t minimize_downhill( F objective_function, double epsilon, const pos_t& init_pos, maybe fixed_step_size = nothing(), double momentum_conservation = 0.5, double sufficing_value = std::numeric_limits::lowest(), double min_step_factor = std::numeric_limits::min(), std::size_t max_iterations = std::numeric_limits::max(), long int max_milliseconds = std::numeric_limits::max(), const std::function< void (std::size_t, double, const pos_t&, const pos_t&)>& callback = std::function< void (std::size_t, double, const pos_t&, const pos_t&)>()) { std::size_t iteration = 0; double step_factor = 1.0; pos_t position = init_pos; double value = internal::invoke(objective_function, position); const auto start_time = std::chrono::steady_clock::now(); const auto is_done = [&]() -> bool { if (max_milliseconds != std::numeric_limits::max()) { const auto current_time = std::chrono::steady_clock::now(); const auto elapsed = current_time - start_time; const auto elapsed_ms = std::chrono::duration_cast(elapsed).count(); if (elapsed_ms >= max_milliseconds) { return true; } } return iteration >= max_iterations || step_factor <= min_step_factor || value <= sufficing_value; }; const auto calc_gradient = [&](const pos_t& pos) -> pos_t { pos_t result; for (std::size_t dim = 0; dim < N; ++dim) { auto test_pos_1 = pos; auto test_pos_2 = pos; test_pos_1[dim] -= epsilon / 2.0; test_pos_2[dim] += epsilon / 2.0; const auto val_1 = internal::invoke(objective_function, test_pos_1); const auto val_2 = internal::invoke(objective_function, test_pos_2); result[dim] = (val_2 - val_1) / epsilon; } return result; }; const auto add = [](const pos_t& p1, const pos_t& p2) -> pos_t { pos_t result; for (std::size_t dim = 0; dim < N; ++dim) { result[dim] = p1[dim] + p2[dim]; } return result; }; const auto multiply = [](const pos_t& p, double f) -> pos_t { pos_t result; for (std::size_t dim = 0; dim < N; ++dim) { result[dim] = p[dim] * f; } return result; }; const auto dist_to_origin = [](const pos_t& p) -> double { double acc = 0; for (std::size_t dim = 0; dim < N; ++dim) { acc += square(p[dim]); } return sqrt(acc); }; const auto normalize = [&](const pos_t& p) -> pos_t { return multiply(p, 1.0 / dist_to_origin(p)); }; const auto null_vector = []() -> pos_t { pos_t result; for (std::size_t dim = 0; dim < N; ++dim) { result[dim] = 0; } return result; }; pos_t momentum = null_vector(); while (!is_done()) { auto new_momentum = multiply(momentum, momentum_conservation); pos_t gradient = calc_gradient(add(position, new_momentum)); const auto inverse_gradient = multiply(gradient, -1.0); auto new_momentum_add = is_nothing(fixed_step_size) ? inverse_gradient : multiply( normalize(inverse_gradient), fixed_step_size.unsafe_get_just()); new_momentum = multiply( add(new_momentum, new_momentum_add), step_factor); if (dist_to_origin(momentum) <= std::numeric_limits::min() && dist_to_origin(new_momentum) <= std::numeric_limits::min()) { break; } const auto new_position = add(position, new_momentum); const auto new_value = internal::invoke(objective_function, new_position); if (new_value >= value) { step_factor /= 2.0; } else { value = new_value; position = new_position; momentum = new_momentum; } ++iteration; if (callback) { callback(iteration, step_factor, momentum, position); } } return position; } } // namespace fplus // // queue.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include namespace fplus { // A thread-safe queue. template class queue { public: queue() : queue_(), mutex_(), cond_() {} fplus::maybe pop() { std::unique_lock lock(mutex_); if (queue_.empty()) { return {}; } auto item = queue_.front(); queue_.pop_front(); return item; } void push(const T& item) { { std::unique_lock lock(mutex_); queue_.push_back(item); } cond_.notify_one(); } std::vector pop_all() { std::unique_lock mlock(mutex_); const auto result = fplus::convert_container>(queue_); queue_.clear(); return result; } std::vector wait_and_pop_all() { std::unique_lock mlock(mutex_); cond_.wait(mlock, [&]() -> bool { return !queue_.empty(); }); const auto result = fplus::convert_container>(queue_); queue_.clear(); return result; } std::vector wait_for_and_pop_all(std::int64_t max_wait_time_us) { std::unique_lock mlock(mutex_); const auto t = std::chrono::microseconds{ max_wait_time_us }; cond_.wait_for(mlock, t, [&]() -> bool { return !queue_.empty(); }); const auto result = fplus::convert_container>(queue_); queue_.clear(); return result; } private: std::deque queue_; std::mutex mutex_; std::condition_variable cond_; }; } // namespace fplus // // raii.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // shared_ref.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include namespace fplus { // A std::shared_ptr expresses // optionality of the contained value (can be nullptr) // and shared ownership that can be transferred. // A std::optional expresses optionality only. // The standard does not provide a class to // express only shared ownership without optionality. // shared_ref fills this gap. // It is recommended to use make_shared_ref for constructing an instance. template class shared_ref { public: shared_ref(const shared_ref&) = default; shared_ref(shared_ref&&) = default; shared_ref& operator=(const shared_ref&) = default; shared_ref& operator=(shared_ref&&) = default; ~shared_ref() = default; T* operator->() { return m_ptr.get(); } const T* operator->() const { return m_ptr.get(); } T& operator*() { return *m_ptr.get(); } const T& operator*() const { return *m_ptr.get(); } template friend shared_ref make_shared_ref(XTypes&&...args); private: std::shared_ptr m_ptr; shared_ref(T* value) :m_ptr(value) { assert(value != nullptr); } }; // http://stackoverflow.com/a/41976419/1866775 template shared_ref make_shared_ref(Types&&...args) { return shared_ref(new T(std::forward(args)...)); } } // namespace fplus namespace fplus { // A generic RAII class. // It is recommended to use make_raii for constructing an instance. template class raii { public: raii(INIT init, QUIT quit) : quit_(quit) { init(); } ~raii() { quit_(); } raii(const raii&) = delete; raii(raii&&) = default; raii& operator=(const raii&) = delete; raii& operator=(raii&&) = default; private: QUIT quit_; }; template shared_ref> make_raii(INIT init, QUIT quit) { return make_shared_ref>(init, quit); } } // namespace fplus // // read.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include namespace fplus { namespace internal { template struct helper_read_value_struct {}; template <> struct helper_read_value_struct { static void read(const std::string& str, int& result, std::size_t& num_chars_used) { result = std::stoi(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, long& result, std::size_t& num_chars_used) { result = std::stol(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, long long& result, std::size_t& num_chars_used) { result = std::stoll(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, unsigned int& result, std::size_t& num_chars_used) { unsigned long result_u_l = std::stoul(str, &num_chars_used); result = static_cast(result_u_l); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, unsigned long& result, std::size_t& num_chars_used) { result = std::stoul(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, unsigned long long& result, std::size_t& num_chars_used) { result = std::stoull(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, float& result, std::size_t& num_chars_used) { result = std::stof(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, double& result, std::size_t& num_chars_used) { result = std::stod(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, long double& result, std::size_t& num_chars_used) { result = std::stold(str, &num_chars_used); } }; template <> struct helper_read_value_struct { static void read(const std::string& str, std::string& result, std::size_t& num_chars_used) { num_chars_used = str.size(); result = str; } }; } // API search type: read_value_result : String -> Result a // Try to deserialize a value. template result read_value_result(const std::string& str) { try { T result; std::size_t num_chars_used = 0; internal::helper_read_value_struct::read(str, result, num_chars_used); if (num_chars_used != str.size()) { return error(std::string("String not fully parsable.")); } return ok(result); } catch(const std::invalid_argument& e) { return error(e.what()); } catch(const std::out_of_range& e) { return error(e.what()); } } // API search type: read_value : String -> Maybe a // Try to deserialize/parse a value, e.g.: // String to Int // String to Float // String to Double // read_value("42") == 42 // etc. template maybe read_value(const std::string& str) { return to_maybe(read_value_result(str)); } // API search type: read_value_with_default : (a, String) -> a // fwd bind count: 1 // Try to deserialize a value, return given default on failure, e.g.: // String to Int // String to Float // String to Double // read_value_with_default(3, "42") == 42 // read_value_with_default(3, "") == 3 // read_value_with_default(3, "foo") == 3 // etc. template T read_value_with_default(const T& def, const std::string& str) { return just_with_default(def, to_maybe(read_value_result(str))); } // API search type: read_value_unsafe : String -> a // Try to deserialize a value, crash on failure, e.g.: // String to Int // String to Float // String to Double // read_value_unsafe("42") == 42 // read_value_unsafe("") == crash // read_value_unsafe("foo") == crash // See read_value and read_value_with_default for safe versions. // etc. template T read_value_unsafe(const std::string& str) { return unsafe_get_just(to_maybe(read_value_result(str))); } } // namespace fplus // // replace.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) namespace fplus { namespace internal { template Container replace_if(internal::reuse_container_t, UnaryPredicate p, const T& dest, Container&& xs) { std::replace_if(std::begin(xs), std::end(xs), p, dest); return std::forward(xs); } template Container replace_if(internal::create_new_container_t, UnaryPredicate p, const T& dest, const Container& xs) { Container ys = xs; return replace_if(internal::reuse_container_t(), p, dest, std::move(ys)); } } // namespace internal // API search type: replace_if : ((a -> Bool), a, [a]) -> [a] // fwd bind count: 2 // Replace every element fulfilling a predicate with a specific value. // replace_if(is_even, 0, [1, 3, 4, 6, 7]) == [1, 3, 0, 0, 7] template > ContainerOut replace_if(UnaryPredicate p, const typename ContainerOut::value_type& dest, Container&& xs) { return internal::replace_if(internal::can_reuse_v{}, p, dest, std::forward(xs)); } namespace internal { template Container replace_elem_at_idx(internal::reuse_container_t, std::size_t idx, const T& dest, Container&& xs) { assert(idx < xs.size()); auto it = std::begin(xs); advance_iterator(it, idx); *it = dest; return std::forward(xs); } template Container replace_elem_at_idx(internal::create_new_container_t, std::size_t idx, const T& dest, const Container& xs) { Container ys = xs; return replace_elem_at_idx(internal::reuse_container_t(), idx, dest, std::move(ys)); } } // namespace internal // API search type: replace_elem_at_idx : (Int, a, [a]) -> [a] // fwd bind count: 2 // Replace the element at a specific index. // replace_elem_at_idx(2, 0, [1, 3, 4, 4, 7]) == [1, 3, 0, 4, 7] template , typename T = typename ContainerOut::value_type> ContainerOut replace_elem_at_idx(std::size_t idx, const T& dest, Container&& xs) { return internal::replace_elem_at_idx(internal::can_reuse_v{}, idx, dest, std::forward(xs)); } // API search type: replace_elems : (a, a, [a]) -> [a] // fwd bind count: 2 // Replace all elements matching source with dest. // replace_elems(4, 0, [1, 3, 4, 4, 7]) == [1, 3, 0, 0, 7] template , typename T = typename ContainerOut::value_type> ContainerOut replace_elems(const T& source, const T& dest, Container&& xs) { return replace_if(bind_1st_of_2(is_equal, source), dest, xs); } // API search type: replace_tokens : ([a], [a], [a]) -> [a] // fwd bind count: 2 // Replace all segments matching source with dest. // replace_tokens("haha", "hihi", "oh, hahaha!") == "oh, hihiha!" // replace_tokens("haha", "o", "oh, hahaha!") == "oh, oha!" template Container replace_tokens (const Container& source, const Container& dest, const Container& xs) { auto splitted = split_by_token(source, true, xs); return join(dest, splitted); } } // namespace fplus // // show.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include namespace fplus { // API search type: show : a -> String // fwd bind count: 0 // 42 -> "42" // Useful to simply show values, e.g.: // Int to String // Float to String // Double to String // Pair to String // std::vector> to String // std::vector to String template std::string show(const T& x) { std::ostringstream ss; ss << x; return ss.str(); } // string identity // "foo" -> "foo" inline std::string show(const std::string& str) { return str; } template std::string show(const std::vector& xs); template std::string show(const std::list& xs); // {1, "one"} -> "(1, one)" template std::string show(const std::pair& p) { return std::string("(") + show(p.first) + ", " + show(p.second) + ")"; } template std::string show_cont(const Container& xs); template std::string show(const std::vector& xs) { return show_cont(xs); } template std::string show(const std::list& xs) { return show_cont(xs); } template std::string show(const std::set& xs) { return show_cont(xs); } template std::string show(const std::deque& xs) { return show_cont(xs); } // API search type: show_cont_with_frame_and_newlines : (String, String, String, [a], Int) -> String // fwd bind count: 3 // show_cont_with_frame_and_newlines (",", "(", ")", [1, 2, 3, 4, 5], 2) // == "(1,2) // 3,4) // 5)" template std::string show_cont_with_frame_and_newlines( const std::string& separator, const std::string& prefix, const std::string& suffix, const Container& xs, std::size_t new_line_every_nth_elem ) { std::vector elemStrs; elemStrs.reserve(xs.size()); if (new_line_every_nth_elem == 0) { for (const auto& x : xs) { elemStrs.push_back(show(x)); } } else { std::size_t i = 0; std::string newline = std::string("\n") + std::string(prefix.size(), ' '); for (const auto& x : xs) { if ( i && i % new_line_every_nth_elem == 0) elemStrs.push_back(newline + show(x)); else elemStrs.push_back(show(x)); ++i; } } return prefix + join(separator, elemStrs) + suffix; } // API search type: show_cont_with_frame : (String, String, String, [a]) -> String // fwd bind count: 3 // show_cont_with_frame (" => ", "{", "}", [1, 2, 3]) == "{1 => 2 => 3}" template std::string show_cont_with_frame( const std::string& separator, const std::string& prefix, const std::string& suffix, const Container& xs) { return show_cont_with_frame_and_newlines( separator, prefix, suffix, xs, 0); } // API search type: show_cont_with : (String, [a]) -> String // fwd bind count: 1 // show_cont_with( " - ", [1, 2, 3]) == "[1 - 2 - 3]" template std::string show_cont_with(const std::string& separator, const Container& xs) { return show_cont_with_frame(separator, "[", "]", xs); } // API search type: show_cont : [a] -> String // fwd bind count: 0 // show_cont [1, 2, 3] -> "[1, 2, 3]" // Can i.a show std::vector and std::map. template std::string show_cont(const Container& xs) { return show_cont_with(", ", xs); } // API search type: show_maybe : Maybe a -> String // fwd bind count: 0 // show_maybe(Just 42) -> "Just 42" template std::string show_maybe(const maybe& maybe) { if (is_nothing(maybe)) return "Nothing"; else return std::string("Just " + show(unsafe_get_just(maybe))); } // API search type: show_result : Result a b -> String // fwd bind count: 0 // show_result(Ok 42) -> "Ok 42" // show_result(Error "fail") -> "Error fail" template std::string show_result(const result& result) { if (is_error(result)) return std::string("Error " + show(unsafe_get_error(result))); else return std::string("Ok " + show(unsafe_get_ok(result))); } // API search type: show_float : (Int, Int, Float) -> String // fwd bind count: 2 // Can be used to show floating point values in a specific format // (Float to String, Double to String etc.) // Examples: // const double pi = 3.14159 // show_float(0, 3, pi) == "3.142" // show_float(1, 3, pi) == "3.142" // show_float(2, 3, pi) == "03.142" // show_float(3, 3, pi) == "003.142" // show_float(1, 2, pi) == "3.14" // show_float(1, 4, pi) == "3.1416" // show_float(1, 7, pi) == "3.1415900" // show_float(0, 3, -pi) == "-3.142" // show_float(1, 3, -pi) == "-3.142" // show_float(2, 3, -pi) == "-3.142" // show_float(3, 3, -pi) == "-03.142" // show_float(4, 3, -pi) == "-003.142" // show_float(0, 3, 0.142) == "0.142"; // show_float(1, 3, 0.142) == "0.142"; // show_float(2, 3, 0.142) == "00.142"; // fill_left(8, ' ', show_float(0, 3, -pi)) == " -3.142" template std::string show_float( std::size_t min_left_chars, std::size_t right_char_count, const T& x) { bool is_negative = x < 0; std::size_t min_left_chars_final = is_negative && min_left_chars > 0 ? min_left_chars - 1 : min_left_chars; std::stringstream stream; stream << std::fixed << std::setprecision(static_cast(right_char_count)) << std::abs(x); std::string s = stream.str(); std::size_t min_dest_length = min_left_chars_final + 1 + right_char_count; std::string result = fill_left('0', min_dest_length, s); if (is_negative) { result = std::string("-") + result; } return result; } // API search type: show_float_fill_left : (Char, Int, Int, Float) -> String // fwd bind count: 3 // Can be used to show floating point values in a specific precision // left-padded with some character. // (Float to String, Double to String etc.) // Examples: // const double pi = 3.14159 // show_float_fill_left(' ', 8, 3, pi) == " 3.142" // show_float_fill_left(' ', 8, 6, pi) == "3.141590" // show_float_fill_left(' ', 8, 3, -pi) == " -3.142" // show_float_fill_left(' ', 2, 3, -pi) == "-3.142" template std::string show_float_fill_left(const std::string::value_type& filler, std::size_t min_size, std::size_t right_char_count, const T& x) { return fill_left(filler, min_size, show_float(0, right_char_count, x)); } // API search type: show_fill_left : (Char, Int, a) -> String // fwd bind count: 2 // Convert some value to a string with left-padded with some character. // (Int to String etc.) // Examples: // show_fill_left(' ', 4, 3) == " 3" // show_fill_left('0', 4, 3) == "0003" // show_fill_left(' ', 4, 12345) == "12345" template std::string show_fill_left( const std::string::value_type& filler, std::size_t min_size, const T& x) { return fill_left(filler, min_size, show(x)); } // API search type: show_fill_right : (Char, Int, a) -> String // fwd bind count: 2 // Convert some value to a string with left-padded with some character. // (Int to String etc.) // Examples: // show_fill_right(' ', 4, 3) == "3 " // show_fill_right(' ', 4, 12345) == "12345" template std::string show_fill_right(const std::string::value_type& filler, std::size_t min_size, const T& x) { return fill_right(filler, min_size, show(x)); } } // namespace fplus // // string_tools.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include namespace fplus { // API search type: is_letter_or_digit : Char -> Bool // fwd bind count: 0 // Is character alphanumerical? template bool is_letter_or_digit(const typename String::value_type& c) { return std::isdigit(static_cast(c)) || std::isalpha(static_cast(c)); } // API search type: is_whitespace : Char -> Bool // fwd bind count: 0 // Is character a whitespace. template bool is_whitespace(const typename String::value_type& c) { return (c == 32 || is_in_interval(9, 14, static_cast(c))); } // API search type: is_line_break : Char -> Bool // fwd bind count: 0 // Newline character ('\n')? template bool is_line_break(const typename String::value_type& c) { return c == '\n'; } // API search type: clean_newlines : String -> String // fwd bind count: 0 // Replaces windows and mac newlines with linux newlines. template String clean_newlines(const String& str) { return replace_elems('\r', '\n', replace_tokens(String("\r\n"), String("\n"), str)); } // API search type: split_words : (Bool, String) -> [String] // fwd bind count: 1 // Splits a string by non-letter and non-digit characters. // split_words(false, "How are you?") == ["How", "are", "you"] template > ContainerOut split_words(const bool allowEmpty, const String& str) { return split_by(logical_not(is_letter_or_digit), allowEmpty, str); } // API search type: split_lines : (Bool, String) -> [String] // fwd bind count: 1 // Splits a string by the found newlines. // split_lines(false, "Hi,\nhow are you?") == ["Hi,", "How are you"] template > ContainerOut split_lines(bool allowEmpty, const String& str) { return split_by(is_line_break, allowEmpty, clean_newlines(str)); } // API search type: trim_whitespace_left : String -> String // fwd bind count: 0 // trim_whitespace_left(" text ") == "text " template String trim_whitespace_left(const String& str) { return drop_while(is_whitespace, str); } // API search type: trim_whitespace_right : String -> String // fwd bind count: 0 // Remove whitespace characters from the end of a string. // trim_whitespace_right(" text ") == " text" template String trim_whitespace_right(const String& str) { return trim_right_by(is_whitespace, str); } // API search type: trim_whitespace : String -> String // fwd bind count: 0 // Remove whitespace characters from the beginning and the end of a string. // trim_whitespace(" text ") == "text" template String trim_whitespace(const String& str) { return trim_by(is_whitespace, str); } // API search type: to_lower_case : String -> String // fwd bind count: 0 // Convert a string to lowercase characters. // to_lower_case("ChaRacTer&WorDs23") == "character&words23" template String to_lower_case(const String& str) { typedef typename String::value_type Char; return transform([](Char c) -> Char { return static_cast( std::tolower(static_cast(c))); }, str); } // API search type: to_lower_case_loc : (Locale, String) -> String // fwd bind count: 1 // Convert a string to lowercase characters using specified locale. // to_upper_case_loc(locale("ru_RU.utf8"), "cYrIlLiC КиРиЛлИцА") == "cyrillic кириллица" template String to_lower_case_loc(const std::locale &lcl, const String &str) { typedef typename String::value_type Char; return transform([&lcl](Char c) -> Char { return static_cast( std::tolower(c, lcl)); }, str); } // API search type: to_upper_case : String -> String // fwd bind count: 0 // Convert a string to uppercase characters. // to_upper_case("ChaRacTer&WorDs34") == "CHARACTER&WORDS34" template String to_upper_case(const String& str) { typedef typename String::value_type Char; return transform([](Char c) -> Char { return static_cast( std::toupper(static_cast(c))); }, str); } // API search type: to_upper_case_loc : (Locale, String) -> String // fwd bind count: 1 // Convert a string to uppercase characters using specified locale. // to_upper_case_loc(locale("ru_RU.utf8"), "cYrIlLiC КиРиЛлИцА") == "CYRILLIC КИРИЛЛИЦА" template String to_upper_case_loc(const std::locale &lcl, const String &str) { typedef typename String::value_type Char; return transform([&lcl](Char c) -> Char { return static_cast( std::toupper(c, lcl)); }, str); } // API search type: to_string_fill_left : (Char, Int, a) -> String // fwd bind count: 2 // Convert a type right-aligned string using a fill character. // to_string_fill_left('0', 5, 42) == "00042" // to_string_fill_left(' ', 5, 42) == " 42" template std::string to_string_fill_left(const std::string::value_type& filler, std::size_t min_size, const T& x) { return fill_left(filler, min_size, std::to_string(x)); } // API search type: to_string_fill_right : (Char, Int, a) -> String // fwd bind count: 2 // Convert a type left-aligned string using a fill character. // to_string_fill_right(' ', 5, 42) == "42 " template std::string to_string_fill_right(const std::string::value_type& filler, std::size_t min_size, const T& x) { return fill_right(filler, min_size, std::to_string(x)); } } // namespace fplus // // tree.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include namespace fplus { template struct tree { tree (const T& value, const std::vector>& children) : value_(value), children_(children) {} T value_; std::vector> children_; }; namespace internal { template tree make_singleton_tree(const T& x) { return {x, {}}; } } // namespace internal namespace internal { template std::vector> presort_trees(BinaryPredicate tree_is_child_of, std::vector> xs_orig) { auto xs = fplus::convert_container>>(xs_orig); std::vector> result; while (!xs.empty()) { for (auto it = std::begin(xs); it != std::end(xs);) { bool has_children = false; for (auto it_rest = std::begin(xs); it_rest != std::end(xs); ++it_rest) { if (it_rest != it && tree_is_child_of(*it_rest, *it)) { has_children = true; } } if (!has_children) { result.push_back(*it); it = xs.erase(it); } else { ++it; } } } return result; } template // todo: name? TreeCont trees_from_sequence_helper( BinaryPredicate tree_is_child_of, TreeCont xs_unsorted) { TreeCont result; auto xs = presort_trees(tree_is_child_of, xs_unsorted); for (auto it = std::begin(xs); it != std::end(xs); ++it) { const auto find_pred = bind_1st_of_2(tree_is_child_of, *it); auto it_find_begin = it; internal::advance_iterator(it_find_begin, 1); auto parent_it = std::find_if(it_find_begin, std::end(xs), find_pred); if (parent_it != std::end(xs)) { parent_it->children_.push_back(*it); } else { result.push_back(*it); } } return result; } } // namespace internal // API search type: trees_from_sequence : (((a, a) -> Bool), [a]) -> [Tree a] // fwd bind count: 1 // Converts the sequence into a tree considering the given binary predicate. template // todo: name? std::vector> trees_from_sequence( BinaryPredicate is_child_of, const Container& xs) { internal::check_binary_predicate_for_container(); typedef typename Container::value_type T; typedef tree Tree; const auto singletons = transform_convert>( internal::make_singleton_tree, xs); const auto tree_is_child_of = [is_child_of](const tree& a, const tree& b) -> bool { return is_child_of(a.value_, b.value_); }; return internal::trees_from_sequence_helper( tree_is_child_of, std::move(singletons)); } namespace internal { // -1 = a < b // 0 = a == b // 1 = b < a template int tree_cmp(const tree& a, const tree& b) { if(a.value_ < b.value_) { return -1; } else if(b.value_ < a.value_) { return 1; } else { const auto results = zip_with(tree_cmp, sort_by(tree_cmp, a.children_), sort_by(tree_cmp, b.children_)); return just_with_default(0, find_first_by( bind_1st_of_2(is_not_equal, 0), results)); } } template bool tree_less(const tree& a, const tree& b) { return tree_cmp(a, b) < 0; } } // namespace internal namespace internal { template bool are_normalized_trees_equal(const tree& a, const tree& b) { if (a.value_ != b.value_ || a.children_.size() != b.children_.size()) { return false; } else { return all(zip_with(are_normalized_trees_equal, a.children_, b.children_)); } } template tree normalize_tree(tree x) { x.children_ = sort_by( internal::tree_less, transform(normalize_tree, x.children_)); return x; } } // namespace internal // API search type: are_trees_equal : (Tree a, Tree a) -> Bool // fwd bind count: 1 template bool are_trees_equal(const tree& a, const tree& b) { return internal::are_normalized_trees_equal( internal::normalize_tree(a), internal::normalize_tree(b)); } // API search type: tree_size : Tree a -> Int // fwd bind count: 0 // A tree with only one element (root) has size 1. template std::size_t tree_size(const tree& x) { return 1 + sum(transform(tree_size, x.children_)); } // API search type: tree_depth : Tree a -> Int // fwd bind count: 0 // A tree with only one element (root) has depth 1. template std::size_t tree_depth(const tree& x) { return 1 + just_with_default(0, maximum_maybe(transform(tree_depth, x.children_))); } // API search type: flatten_tree_depth_first : Tree a -> [a] // fwd bind count: 0 template std::vector flatten_tree_depth_first(const tree& x) { return prepend_elem(x.value_, transform_and_concat(flatten_tree_depth_first, x.children_)); } // API search type: flatten_tree_breadth_first : Tree a -> [a] // fwd bind count: 0 template std::vector flatten_tree_breadth_first(const tree& x) { std::vector result; result.reserve(tree_size(x)); std::queue*> q; q.push(&x); while (!q.empty()) { const auto current = q.front(); q.pop(); result.push_back(current->value_); for (const auto& c : current->children_) { q.push(&c); } } return result; } } // namespace fplus // // side_effects.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fplus { // Executes a function f in a fixed interval, // i.e. an average timespan between two consecutive calls of f, // given in microseconds. // f is a unary function, taking the time delta (in microseconds) // between the last and the current call as its argument. // In case of a delay outdated calls are be executed immediately. // So the average executation time of f should be way shorter // than the requested interval. // Call ticker::start() to run. // The ticker stops when ticker::stop() is called // or the instance runs out of scope. // // Example usage: // // void say_hi(std::int64_t) // { // std::cout << "hi " << std::endl; // } // int main() // { // ticker hi_ticker(say_hi, 2 * 1000 * 1000); // hi_ticker.start(); // std::this_thread::sleep_for(std::chrono::milliseconds(4500)); // } class ticker { public: typedef std::function function; ticker(const function& f, std::int64_t interval_us) : f_(f), interval_us_(interval_us), control_mutex_(), is_running_(false), thread_(), stop_mutex_() { } bool is_running() { std::lock_guard lock(control_mutex_); return is_running_; } bool start() { std::lock_guard lock(control_mutex_); if (is_running_) return false; stop_mutex_.lock(); thread_ = std::thread([this]() { thread_function(); }); is_running_ = true; return true; } bool stop() { std::lock_guard lock(control_mutex_); if (!is_running_) return false; stop_mutex_.unlock(); if (thread_.joinable()) { thread_.join(); thread_ = std::thread(); } is_running_ = false; return true; } ~ticker() { stop(); } private: void thread_function() { auto last_wake_up_time = std::chrono::steady_clock::now(); auto last_time = last_wake_up_time; bool quit = false; while (!quit) { const auto wake_up_time = last_wake_up_time + std::chrono::microseconds{ interval_us_ }; const auto sleep_time = wake_up_time - std::chrono::steady_clock::now(); if (stop_mutex_.try_lock_for(sleep_time)) { stop_mutex_.unlock(); quit = true; } const auto current_time = std::chrono::steady_clock::now(); const auto elapsed = current_time - last_time; last_wake_up_time = wake_up_time; last_time = current_time; const auto elapsed_us = std::chrono::duration_cast( elapsed).count(); try { f_(elapsed_us); } catch (...) { } } } const function f_; const std::int64_t interval_us_; std::mutex control_mutex_; bool is_running_; std::thread thread_; std::timed_mutex stop_mutex_; }; // API search type: sleep_for_n_seconds : Int -> Io () // Returns a function that suspends // the calling thread for n seconds when executed. inline std::function sleep_for_n_seconds(std::size_t seconds) { return [seconds]() { std::this_thread::sleep_for(std::chrono::seconds(seconds)); }; } // API search type: sleep_for_n_milliseconds : Int -> Io () // Returns a function that suspends // the calling thread for n milliseconds when executed. inline std::function sleep_for_n_milliseconds(std::size_t milliseconds) { return [milliseconds]() { std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); }; } // API search type: sleep_for_n_microseconds : Int -> Io () // Returns a function that suspends // the calling thread for n microseconds when executed. inline std::function sleep_for_n_microseconds(std::size_t microseconds) { return [microseconds]() { std::this_thread::sleep_for(std::chrono::microseconds(microseconds)); }; } // API search type: execute_serially : [Io ()] -> Io () // Returns a function that executes // the given side effects one after another when called. template auto execute_serially(const Container& effs) { using Effect = typename Container::value_type; using Result = internal::invoke_result_t; return [effs] { std::vector> results; for (const Effect& e : effs) { results.push_back(internal::invoke(e)); } return results; }; } // API search type: execute_serially_until_success : [Io Bool] -> Io Bool // Returns a function that (when called) executes // the given side effects one after another until one of it returns true. template auto execute_serially_until_success(const Container& effs) { using Effect = typename Container::value_type; using Result = internal::invoke_result_t; static_assert(std::is_convertible::value, "Effects must return a boolish type."); return [effs]() -> bool { for (const Effect& e : effs) { if (internal::invoke(e)) { return true; } } return false; }; } // API search type: execute_and_return_fixed_value : (a, [Io b]) -> Io a // Returns a function that executes the given side effect // and returns a fixed value when called. template std::function execute_and_return_fixed_value( Result result, Effect eff) { return [eff, result]() -> Result { eff(); return result; }; } // Converts an arbitrary callable effect to an std::function. template std::function ()> effect_to_std_function(Effect eff) { return [eff] { return internal::invoke(eff); }; } // API search type: execute_max_n_times_until_success : (Int, Io (), Int) -> Io Bool // Returns a function that (when called) executes a side effect // until it succeds once or the maximum number // of attempts with an optional pause in between. template auto execute_max_n_times_until_success(std::size_t n, const Effect& eff, std::size_t pause_in_milliseconds = 0) { if (pause_in_milliseconds > 0) { auto sleep_and_return_false = execute_and_return_fixed_value( false, sleep_for_n_milliseconds(pause_in_milliseconds)); return execute_serially_until_success( intersperse( sleep_and_return_false, replicate(n, effect_to_std_function(eff)))); } return execute_serially_until_success( replicate(n, effect_to_std_function(eff))); } // API search type: execute_n_times : (Int, Io a) -> Io () // Returns a function that (when called) executes n times // the provided side effect function. // The return values (if present) are dropped. template auto execute_n_times(std::size_t n, const Effect& eff) { for (auto _ : fplus::numbers(static_cast(0), n)) { (void) _; // suppress warning / unused variable eff(); } } // API search type: execute_serially_until_failure : [Io Bool] -> Io Bool // Returns a function that (when called) executes the given side effects // one after another until one of them returns false. template std::function execute_serially_until_failure(const Container& effs) { using Effect = typename Container::value_type; using Result = internal::invoke_result_t; static_assert(std::is_convertible::value, "Effects must return a boolish type."); return [effs]() -> bool { for (const Effect& e : effs) { if (!internal::invoke(e)) { return false; } } return true; }; } // API search type: execute_parallelly : [Io a] -> Io [a] // Returns a function that (when called) executes the given side effects // in parallel (one thread each) and returns the collected results. template auto execute_parallelly(const Container& effs) { return [effs] { // Bluntly re-using the transform implementation to execute side effects. return transform_parallelly([](const auto& eff) { return internal::invoke(eff); }, effs); }; } // API search type: execute_parallelly_n_threads : (Int, [Io a]) -> Io [a] // Returns a function that (when called) executes the given side effects // in parallel (one thread each) and returns the collected results. template auto execute_parallelly_n_threads(std::size_t n, const Container& effs) { return [n, effs] { // Bluntly re-using the transform implementation to execute side effects. return transform_parallelly_n_threads(n, [](const auto& eff) { return internal::invoke(eff); }, effs); }; } // API search type: execute_fire_and_forget : Io a -> Io a // Returns a function that (when called) executes the given side effect // in a new thread and returns immediately. template std::function execute_fire_and_forget(Effect eff) { return [eff]() { std::thread t(eff); t.detach(); }; } // API search type: read_text_file_maybe : String -> Io (Maybe String) // Returns a function that reads the content of a text file when called. inline std::function()> read_text_file_maybe( const std::string& filename) { return [filename]() -> maybe { std::ifstream input(filename); if (!input.good()) return {}; return just(std::string( std::istreambuf_iterator(input), std::istreambuf_iterator())); }; } // API search type: read_text_file : String -> Io String // Returns a function that reads the content of a text file when called. // This function then returns an empty string if the file could not be read. inline std::function read_text_file(const std::string& filename) { return [filename]() -> std::string { return just_with_default( std::string(), read_text_file_maybe(filename)()); }; } // API search type: read_binary_file_maybe : String -> Io (Maybe [Int]) // Returns a function that reads the content of a binary file when executed. inline std::function>()> read_binary_file_maybe( const std::string& filename) { return [filename]() -> maybe> { std::ifstream file(filename, std::ios::binary); if (!file.good()) return {}; file.unsetf(std::ios::skipws); std::streampos fileSize; file.seekg(0, std::ios::end); fileSize = file.tellg(); if (fileSize == static_cast(0)) return {}; file.seekg(0, std::ios::beg); std::vector vec(static_cast(fileSize), 0); file.read(reinterpret_cast(&vec[0]), fileSize); return vec; }; } // API search type: read_binary_file : String -> Io [Int] // Returns a function that reads the content of a binary file when executed. // This function then returns an empty vector if the file could not be read. inline std::function()> read_binary_file( const std::string& filename) { return [filename]() -> std::vector { return just_with_default( std::vector(), read_binary_file_maybe(filename)()); }; } // API search type: read_text_file_lines_maybe : (String, Bool) -> Io (Maybe [String]) // Returns a function that (when called) reads the content of a text file // and returns it line by line. inline std::function>()> read_text_file_lines_maybe( bool allow_empty, const std::string& filename) { return [filename, allow_empty]() -> maybe> { const auto maybe_content = read_text_file_maybe(filename)(); if (maybe_content.is_nothing()) return {}; else return split_lines(allow_empty, maybe_content.unsafe_get_just()); }; } // API search type: read_text_file_lines : (String, Bool) -> Io [String] // Returns a function that (when called) reads the content of a text file // and returns it line by line. // This function then returns an empty vector if the file could not be read. inline std::function()> read_text_file_lines( bool allow_empty, const std::string& filename) { return [filename, allow_empty]() -> std::vector { return just_with_default( std::vector(), read_text_file_lines_maybe(allow_empty, filename)()); }; } // API search type: write_text_file : (String, String) -> Io Bool // Returns a function that (when called) writes content into a text file, // replacing it if it already exists. inline std::function write_text_file(const std::string& filename, const std::string& content) { return [filename, content]() -> bool { std::ofstream output(filename); output << content; return output.good(); }; } // API search type: write_binary_file : (String, [Int]) -> Io Bool // Returns a function that (when called) writes content into a binary file, // replacing it if it already exists. inline std::function write_binary_file(const std::string& filename, const std::vector& content) { return [filename, content]() -> bool { std::ofstream file(filename, std::ios::binary); file.write(reinterpret_cast(&content[0]), static_cast(content.size())); return file.good(); }; } // API search type: write_text_file_lines : (String, [String], Bool) -> Io Bool // Returns a function that (when called) writes lines into a text file, // replacing it if it already exists. inline std::function write_text_file_lines(bool trailing_newline, const std::string& filename, const std::vector& lines) { std::string content = join(std::string("\n"), lines); if (trailing_newline) { content += "\n"; } return write_text_file(filename, content); } // API search type: execute_effect : Io a -> a // Simply run a side effect (call a function without parameters) // and returns the result. // Can be useful for chaining. template auto execute_effect(const F f) { return internal::invoke(f); } // API search type: interact : (String -> String) -> Io () // Takes a function F of type (String -> String) // and returns a function that // reads the entire input from standard input, // passes it through the given function, // and writes the result to standard output. template std::function interact(F f) { return [f]() -> void { std::cout << f(std::string( std::istreambuf_iterator(std::cin.rdbuf()), std::istreambuf_iterator())); }; } // API search type: execute_with_maybe : ((a -> void), Maybe a) -> Io Bool // Returns a function that // akes a unary side-effect function with // a maybe holding a matching type // and runs the sideeffect if the Maybe holds a just. // The returned function returns false if the maybe was a nothing. template std::function execute_with_maybe(Effect eff, const maybe& m) { return [eff, m]() -> bool { if (m.is_nothing()) { return false; } eff(m.unsafe_get_just()); return true; }; } } // namespace fplus // // stopwatch.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include namespace fplus { class stopwatch { public: stopwatch() : beg_(clock::now()) {} void reset() { beg_ = clock::now(); } // time since creation or last reset in seconds double elapsed() const { return std::chrono::duration_cast (clock::now() - beg_).count(); } private: typedef std::chrono::high_resolution_clock clock; typedef std::chrono::duration> second; std::chrono::time_point beg_; }; } // namespace fplus // // variant.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include namespace fplus { namespace internal { // http://stackoverflow.com/a/18987405/1866775 template struct is_one_of; template struct is_one_of { static constexpr bool value = false; }; template struct is_one_of { static constexpr bool value = std::is_same::value || is_one_of::value; }; template struct is_one_of> { static constexpr bool value = is_one_of::value; }; template struct is_unique; template <> struct is_unique<> { static constexpr bool value = true; }; template struct is_unique { static constexpr bool value = is_unique::value && !is_one_of::value; }; template struct is_unique> { static constexpr bool value = is_unique::value; }; template struct are_same; template <> struct are_same<> { static constexpr bool value = true; }; template struct are_same { static constexpr bool value = std::is_same::value; }; template struct are_same { static constexpr bool value = are_same::value && std::is_same::value; }; template struct are_same> { static constexpr bool value = are_same::value; }; // http://stackoverflow.com/a/3273571/1866775 template class List, template class Mod, typename ...Args> struct transform_parameter_pack { typedef List::type...> type; }; template struct as_shared_pointer { typedef std::shared_ptr type; }; // http://stackoverflow.com/a/27588263/1866775 template struct get_index; template struct get_index : std::integral_constant {}; template struct get_index : std::integral_constant::value> {}; template struct get_index { // condition is always false, but should be dependant of T static_assert(sizeof(T) == 0, "element not found"); }; template struct parameter_pack_head { typedef T type; }; template struct function_first_input_type { typedef typename std::remove_const< typename std::remove_reference< typename utils::function_traits< F>::template arg<0>::type>::type>::type type; }; template struct unary_function_result_type { static_assert(utils::function_traits::arity == 1, "Wrong arity."); typedef typename function_first_input_type::type T; typedef std::decay_t> type; }; // http://stackoverflow.com/a/42493805/1866775 template struct tag { }; template struct type_set_eq_helper: tag... { }; template struct type_set_eq: std::false_type { }; template struct bool_pack { }; template using my_and = std::is_same, bool_pack>; template struct type_set_eq, std::tuple, typename std::enable_if< (sizeof...(Ts1) == sizeof...(Ts2)) && my_and< std::is_base_of, type_set_eq_helper>::value... >::value >::type >: std::true_type { }; // http://stackoverflow.com/a/42581257/1866775 template struct is_superset_of; template struct is_superset_of> { static const bool value = is_one_of::value && is_superset_of>::value; }; template struct is_superset_of> { static const bool value = true; }; // http://stackoverflow.com/a/36934374/1866775 template using all_true = std::is_same, bool_pack>; } // namespace internal template struct variant { static_assert(internal::is_unique::value, "Types must be unique."); static_assert(internal::all_true<(!std::is_reference::value)...>::value, "No reference types allowed."); static_assert(internal::all_true<(!std::is_const::value)...>::value, "No const types allowed."); static_assert(sizeof...(Types) >= 1, "Please provide at least one type."); template variant(const T& val) : shared_ptrs_({}) { std::get::value>(shared_ptrs_) = std::make_shared(val); } template bool is() const { static_assert( internal::is_one_of::value , "Type must match one possible variant type."); const auto ptr = std::get::value>(shared_ptrs_); return static_cast(ptr); } friend bool operator== ( const variant& a, const variant& b) { return a.shared_ptrs_ == b.shared_ptrs_; } friend bool operator!= ( const variant& a, const variant& b) { return a.shared_ptrs_ != b.shared_ptrs_; } template auto visit_one(F f) const { using T = typename internal::function_first_input_type::type; using Ret = internal::invoke_result_t; internal::trigger_static_asserts(); static_assert( internal::is_one_of< typename internal::function_first_input_type::type, Types...>::value , "Function input must match one variant type."); static_assert(!std::is_same, void>::value, "Function must return non-void type."); const auto ptr = std::get::value>(shared_ptrs_); if (ptr) { return just(internal::invoke(f, *ptr)); } return nothing>(); } template auto visit(Fs ... fs) const -> typename internal::unary_function_result_type< typename internal::parameter_pack_head::type>::type { typedef typename internal::unary_function_result_type< typename internal::parameter_pack_head::type>::type Res; static_assert( sizeof...(Fs) >= std::tuple_size::value, "Too few functions provided."); static_assert( sizeof...(Fs) <= std::tuple_size::value, "Too many functions provided."); typedef typename internal::transform_parameter_pack< std::tuple, internal::unary_function_result_type, Fs... >::type return_types_tuple; typedef typename internal::transform_parameter_pack< std::tuple, internal::function_first_input_type, Fs... >::type function_first_input_types_tuple; static_assert( internal::is_unique::value, "Only one function per input type allowed."); static_assert( internal::are_same::value, "All Functions must return the same type."); static_assert( internal::type_set_eq>::value, "Functions do not cover all possible types."); const auto results = justs(visit_helper(fs...)); assert(size_of_cont(results) == 1); return head(results); } template variant transform(Fs ... fs) const { static_assert( sizeof...(Fs) >= std::tuple_size::value, "Too few functions provided."); static_assert( sizeof...(Fs) <= std::tuple_size::value, "Too many functions provided."); typedef typename internal::transform_parameter_pack< std::tuple, internal::unary_function_result_type, Fs... >::type return_types_tuple; typedef typename internal::transform_parameter_pack< std::tuple, internal::function_first_input_type, Fs... >::type function_first_input_types_tuple; static_assert( internal::type_set_eq>::value, "Functions do not cover all possible types."); static_assert( internal::is_superset_of, return_types_tuple>::value, "All Functions must return a possible variant type."); return visit(fs...); } private: template std::vector> visit_helper(F f) const { return {visit_one(f)}; } template std::vector> visit_helper(F f, Fs ... fs) const { return fplus::append(visit_helper(f), visit_helper(fs...)); } typedef typename internal::transform_parameter_pack< std::tuple, internal::as_shared_pointer, Types... >::type shared_ptr_pack; shared_ptr_pack shared_ptrs_; }; } // namespace fplus // // timed.hpp // #include #include #include #include #include #include namespace fplus { using ExecutionTime = double; // in seconds // Holds a value of type T plus an execution time template class timed : public std::pair { using base_pair = std::pair; public: timed() : base_pair() {} timed(const T& val, ExecutionTime t = 0.) : base_pair(val, t) {} // Execution time in seconds (returns a double) ExecutionTime time_in_s() const { return base_pair::second; } // Execution time as a std::chrono::duration std::chrono::duration> duration_in_s() const { return std::chrono::duration>(time_in_s()); } // Inner value const T& get() const { return base_pair::first; } T& get() { return base_pair::first; } }; // API search type: show_timed : Timed a -> String // fwd bind count: 0 // show_timed((42,1)) -> "42 (1000ms)" template std::string show_timed(const fplus::timed& v) { std::string result = fplus::show(v.get()) + " (" + fplus::show(v.time_in_s() * 1000.) + "ms)"; return result; } namespace internal { template class timed_function_impl { public: explicit timed_function_impl(Fn fn) : _fn(fn) {}; template auto operator()(Args... args) { return _timed_result(args...); } private: template auto _timed_result(Args... args) { fplus::stopwatch timer; auto r = _fn(args...); auto r_t = fplus::timed(r, timer.elapsed()); return r_t; } Fn _fn; }; } // API search type: make_timed_function : ((a -> b)) -> (a -> Timed b) // fwd bind count: 0 // Transforms a function into a timed / benchmarked version of the same function. // - // Example: // - // using Ints = std::vector; // Ints ascending_numbers = fplus::numbers(0, 1000); // Ints shuffled_numbers = fplus::shuffle(std::mt19937::default_seed, ascending_numbers); // auto sort_func = [](const Ints& values) { return fplus::sort(values); }; // auto sort_bench = fplus::make_timed_function(sort_func); // auto sorted_numbers = sort_bench(shuffled_numbers); // assert(sorted_numbers.get() == ascending_numbers); // sorted_numbers.get() <=> actual output // assert(sorted_numbers.time_in_s() < 0.1); // // sorted_numbers.time_in_s() <=> execution time template auto make_timed_function(Fn f) { return internal::timed_function_impl(f); } namespace internal { template class timed_void_function_impl { public: explicit timed_void_function_impl(Fn fn) : _fn(fn) {}; template auto operator()(Args... args) { return _timed_result(args...); } private: template auto _timed_result(Args... args) { fplus::stopwatch timer; _fn(args...); return timer.elapsed(); } Fn _fn; }; } // API search type: make_timed_void_function : ((a -> Void)) -> (a -> Double) // fwd bind count: 0 // Transforms a void function into a timed / benchmarked version of the same function. // - // Example: // - // void foo() { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } // ... // auto foo_bench = make_timed_void_function(foo); // auto r = foo_bench(); // double run_time = foo_bench(); // in seconds template auto make_timed_void_function(Fn f) { return internal::timed_void_function_impl(f); } } // // benchmark_session.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include namespace fplus { using FunctionName = std::string; struct benchmark_function_report { std::size_t nb_calls; ExecutionTime total_time; ExecutionTime average_time; ExecutionTime deviation; }; namespace internal { std::string show_benchmark_function_report( const std::map & reports); } // benchmark_session stores timings during a benchmark session // and is able to emit a report at the end class benchmark_session { public: benchmark_session() : functions_times_mutex_(), functions_times_() {}; // report() shall return a string with a summary of the session // Example below: // Function |Nb calls|Total time|Av. time|Deviation| // ----------------------+--------+----------+--------+---------+ // convert_charset_string| 4000| 4.942ms| 1.236us| 1.390us| // split_lines | 1000| 4.528ms| 4.528us| 1.896us| inline std::string report() const { const auto reports = report_list(); return fplus::internal::show_benchmark_function_report(reports); } std::map report_list() const { std::lock_guard lock(functions_times_mutex_); std::map report; for (const auto & one_function_time : functions_times_) { report[one_function_time.first] = make_bench_report(one_function_time.second); } return report; } inline void store_one_time(const FunctionName & function_name, ExecutionTime time) { std::lock_guard lock(functions_times_mutex_); functions_times_[function_name].push_back(time); } private: benchmark_function_report make_bench_report( const std::vector & times) const { benchmark_function_report result; result.nb_calls = times.size(); auto mean_and_dev = fplus::mean_stddev(times); result.average_time = mean_and_dev.first; result.deviation = mean_and_dev.second; result.total_time = fplus::sum(times); return result; } mutable std::mutex functions_times_mutex_; std::map> functions_times_; }; namespace internal { template class bench_function_impl { public: explicit bench_function_impl( benchmark_session & benchmark_sess, FunctionName function_name, Fn fn) : benchmark_session_(benchmark_sess) , function_name_(function_name) , fn_(fn) {}; template auto operator()(Args... args) { return _bench_result(args...); } private: template auto _bench_result(Args... args) { fplus::stopwatch timer; auto r = fn_(args...); benchmark_session_.store_one_time(function_name_, timer.elapsed()); return r; } benchmark_session & benchmark_session_; FunctionName function_name_; Fn fn_; }; template class bench_void_function_impl { public: explicit bench_void_function_impl( benchmark_session & benchmark_sess, FunctionName function_name, Fn fn) : benchmark_session_(benchmark_sess) , function_name_(function_name) , fn_(fn) {}; template auto operator()(Args... args) { _bench_result(args...); } private: template auto _bench_result(Args... args) { fplus::stopwatch timer; fn_(args...); benchmark_session_.store_one_time(function_name_, timer.elapsed()); } benchmark_session & benchmark_session_; FunctionName function_name_; Fn fn_; }; } // namespace internal // API search type: make_benchmark_function : (benchmark_session, string, (a... -> b)) -> (a... -> b) // Transforms a function into a function with the *same* signature // and behavior, except that it also stores stats into the benchmark session (first parameter), // under the name given by the second parameter. // - // Notes: // Side effects: make_benchmark_function *will add side effects* to the function, since it stores data // into the benchmark session at each call. // If you intend to benchmark only one function, prefer to use the simpler "make_timed_function" // Use "make_benchmark_void_function" if your function returns void // - // Example of a minimal benchmark session (read benchmark_session_test.cpp for a full example) // fplus::benchmark_session benchmark_sess; // void foo() { // auto add_bench = fplus::make_benchmark_function(benchmark_sess, "add", add); // auto printf_bench = fplus::make_benchmark_void_function(benchmark_sess, "printf", printf); // int forty_five = add_bench(20, add_bench(19, 6)); // int forty_two = benchmark_expression(benchmark_sess, "sub", forty_five - 3); // printf_bench("forty_two is %i\n", forty_two); // } // int main() { // foo(); // std::cout << benchmark_sess.report(); // } // This will output a report like this // Function|Nb calls|Total time|Av. time|Deviation| // --------+--------+----------+--------+---------+ // printf | 1| 0.010ms| 9.952us| 0.000us| // add | 2| 0.000ms| 0.050us| 0.009us| // sub | 1| 0.000ms| 0.039us| 0.000us| // - // As an alternative to make_benchmark_function, you can also benchmark an expression. // For example, in order to benchmark the following line: // auto sorted = fplus::sort(my_vector); // Just copy/paste this expression into "bench_expression" like shown below: this expression // will then be benchmarked with the name "sort_my_vector" // auto sorted = benchmark_expression( // my_benchmark_session, // "sort_my_vector", // fplus::sort(my_vector); // ); // Notes : // benchmark_expression is a preprocessor macro that uses an immediately invoked lambda (IIL). // The expression can be copy-pasted with no modification, and it is possible to not remove the ";" // (although it also works if it is not present) // You can also benchmark an expression that returns void using benchmark_void_expression template auto make_benchmark_function(benchmark_session & session, const FunctionName & name, Fn f) { // transforms f into a function with the same // signature, that will store timings into the benchmark session return internal::bench_function_impl(session, name, f); } // API search type: make_benchmark_void_function : (benchmark_session, string, (a... -> Void)) -> (a... -> Void) // Transforms a function that returns a void into a function with the *same* signature // and behavior, except that it also stores stats into the benchmark session (first parameter), // under the name given by the second parameter // Note that make_benchmark_void_function *will add side effects* to the function // (since it stores data into the benchmark session at each call) // - // Example: // benchmark_session bench_session; // ... // void foo() { // std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // } // ... // auto foo_bench = make_benchmark_void_function(bench_session, "foo", foo); // foo_bench(); // ... // std::cout << benchmark_session.report(); template auto make_benchmark_void_function(benchmark_session & session, const FunctionName & name, Fn f) { // transforms a void returning function into a function with the same // signature, that will store timings into the benchmark session return internal::bench_void_function_impl(session, name, f); } #define benchmark_expression(bench_session, name, expression) \ make_benchmark_function( \ bench_session, \ name, \ [&]() { return expression; } \ )(); #define benchmark_void_expression(bench_session, name, expression) \ make_benchmark_void_function( \ bench_session, \ name, \ [&]() { expression; } \ )(); namespace internal { inline std::string show_table(const std::vector>& rows) { if (rows.empty() || rows[0].empty()) return ""; const std::vector columns_width = [&]() { auto string_size = [](const std::string & s) -> std::size_t { return s.size(); }; auto largest_string_size = [&](const std::vector & strings) -> std::size_t { return string_size(fplus::maximum_on(string_size, strings)); }; return fplus::transform(largest_string_size, fplus::transpose(rows)); }(); auto show_one_element = [](const std::pair & elem_and_width) { const std::string & element = elem_and_width.first; const auto col_width = elem_and_width.second; bool is_number = element.size() > 0 && isdigit(element[0]); if (is_number) return fplus::show_fill_left(' ', col_width, element) + "|"; else return fplus::show_fill_right(' ', col_width, element) + "|"; }; auto show_one_separator = [](std::size_t col_width) { return fplus::show_fill_left('-', col_width, "") + "+"; }; auto show_one_row = [&](const std::vector & row) { return fplus::sum(fplus::transform( show_one_element, fplus::zip(row, columns_width))); }; auto firstrow_separator = fplus::sum(fplus::transform(show_one_separator, columns_width)); auto rows_formatted = fplus::transform(show_one_row, rows); auto rows_separated = fplus::insert_at_idx(1, firstrow_separator, rows_formatted); return fplus::join( std::string("\n"), rows_separated) + "\n"; } inline std::vector< std::pair > make_ordered_reports( const std::map & report_map) { auto report_pairs = fplus::map_to_pairs(report_map); auto report_pairs_sorted = fplus::sort_by([](const auto &a, const auto &b) { return a.second.total_time > b.second.total_time; }, report_pairs); return report_pairs_sorted; } inline std::string show_benchmark_function_report(const std::map & reports) { auto ordered_reports = make_ordered_reports(reports); auto my_show_time_ms = [](double time) -> std::string { std::stringstream ss; ss << std::fixed << std::setprecision(3); ss << (time * 1000.); return ss.str() + "ms"; }; auto my_show_time_us = [](double time) -> std::string { std::stringstream ss; ss << std::fixed << std::setprecision(3); ss << (time * 1000000.); return ss.str() + "us"; }; std::vector header_row{ { "Function", "Nb calls", "Total time", "Av. time", "Deviation" } }; auto value_rows = fplus::transform([&](const auto & kv) { const auto & report = kv.second; const auto & function_name = kv.first; std::vector row; row.push_back(function_name); row.push_back(fplus::show(report.nb_calls)); row.push_back(my_show_time_ms(report.total_time)); row.push_back(my_show_time_us(report.average_time)); row.push_back(my_show_time_us(report.deviation)); return row; }, ordered_reports); return fplus::internal::show_table(fplus::insert_at_idx(0, header_row, value_rows)); } } // namespace internal } // // curry.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) namespace fplus { namespace curry { // Currying. // Allow to generically bind parameters one by one. #define fplus_curry_define_fn_0(fplus_curry_define_fn_0_name) \ inline auto fplus_curry_define_fn_0_name() \ { \ return [](auto&& fplus_curry_p1) \ { \ return fplus::fplus_curry_define_fn_0_name(std::forward(fplus_curry_p1)); \ }; \ } #define fplus_curry_define_fn_1(fplus_curry_define_fn_1_name) \ template \ auto fplus_curry_define_fn_1_name(P1 p1) \ { \ return [p1](auto&& fplus_curry_p2) \ { \ return fplus::fplus_curry_define_fn_1_name(p1, std::forward(fplus_curry_p2)); \ }; \ } #define fplus_curry_define_fn_2(fplus_curry_define_fn_2_name) \ template \ auto fplus_curry_define_fn_2_name(P1 p1) \ { \ return [p1](const auto& fplus_curry_p2) \ { \ return [p1, fplus_curry_p2](auto&& fplus_curry_p3) \ { \ return fplus::fplus_curry_define_fn_2_name(p1, fplus_curry_p2, std::forward(fplus_curry_p3)); \ }; \ }; \ } #define fplus_curry_define_fn_3(fplus_curry_define_fn_3_name) \ template \ auto fplus_curry_define_fn_3_name(P1 p1) \ { \ return [p1](const auto& fplus_curry_p2) \ { \ return [p1, fplus_curry_p2](const auto& fplus_curry_p3) \ { \ return [p1, fplus_curry_p2, fplus_curry_p3](auto&& fplus_curry_p4) \ { \ return fplus::fplus_curry_define_fn_3_name(p1, fplus_curry_p2, fplus_curry_p3, std::forward(fplus_curry_p4)); \ }; \ }; \ }; \ } #define fplus_curry_define_fn_4(fplus_curry_define_fn_4_name) \ template \ auto fplus_curry_define_fn_4_name(P1 p1) \ { \ return [p1](const auto& fplus_curry_p2) \ { \ return [p1, fplus_curry_p2](const auto& fplus_curry_p3) \ { \ return [p1, fplus_curry_p2, fplus_curry_p3](const auto& fplus_curry_p4) \ { \ return [p1, fplus_curry_p2, fplus_curry_p3, fplus_curry_p4](auto&& fplus_curry_p5) \ { \ return fplus::fplus_curry_define_fn_4_name(p1, fplus_curry_p2, fplus_curry_p3, fplus_curry_p4, std::forward(fplus_curry_p5)); \ }; \ }; \ }; \ }; \ } // // curry_instances.autogenerated_defines // // THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT EDIT. fplus_curry_define_fn_0(identity) fplus_curry_define_fn_1(is_equal) fplus_curry_define_fn_1(is_not_equal) fplus_curry_define_fn_1(is_less) fplus_curry_define_fn_1(is_less_or_equal) fplus_curry_define_fn_1(is_greater) fplus_curry_define_fn_1(is_greater_or_equal) fplus_curry_define_fn_1(xor_bools) fplus_curry_define_fn_0(is_just) fplus_curry_define_fn_0(is_nothing) fplus_curry_define_fn_0(unsafe_get_just) fplus_curry_define_fn_0(just_with_default) fplus_curry_define_fn_1(throw_on_nothing) fplus_curry_define_fn_0(just) fplus_curry_define_fn_1(as_just_if) fplus_curry_define_fn_0(maybe_to_seq) fplus_curry_define_fn_0(singleton_seq_as_maybe) fplus_curry_define_fn_1(lift_maybe) fplus_curry_define_fn_2(lift_maybe_def) fplus_curry_define_fn_2(lift_maybe_2) fplus_curry_define_fn_3(lift_maybe_2_def) fplus_curry_define_fn_1(and_then_maybe) fplus_curry_define_fn_0(flatten_maybe) fplus_curry_define_fn_0(is_empty) fplus_curry_define_fn_0(is_not_empty) fplus_curry_define_fn_0(size_of_cont) fplus_curry_define_fn_0(convert) fplus_curry_define_fn_0(convert_elems) fplus_curry_define_fn_0(convert_container) fplus_curry_define_fn_0(convert_container_and_elems) fplus_curry_define_fn_2(get_segment) fplus_curry_define_fn_2(set_segment) fplus_curry_define_fn_2(remove_segment) fplus_curry_define_fn_2(insert_at) fplus_curry_define_fn_1(elem_at_idx) fplus_curry_define_fn_1(elem_at_idx_maybe) fplus_curry_define_fn_1(elems_at_idxs) fplus_curry_define_fn_1(transform) fplus_curry_define_fn_1(transform_convert) fplus_curry_define_fn_1(transform_inner) fplus_curry_define_fn_0(reverse) fplus_curry_define_fn_1(take) fplus_curry_define_fn_1(take_exact) fplus_curry_define_fn_1(take_cyclic) fplus_curry_define_fn_1(drop) fplus_curry_define_fn_1(take_last) fplus_curry_define_fn_1(drop_last) fplus_curry_define_fn_1(drop_exact) fplus_curry_define_fn_1(take_while) fplus_curry_define_fn_1(drop_while) fplus_curry_define_fn_2(fold_left) fplus_curry_define_fn_2(reduce) fplus_curry_define_fn_1(fold_left_1) fplus_curry_define_fn_1(reduce_1) fplus_curry_define_fn_2(fold_right) fplus_curry_define_fn_1(fold_right_1) fplus_curry_define_fn_2(scan_left) fplus_curry_define_fn_1(scan_left_1) fplus_curry_define_fn_2(scan_right) fplus_curry_define_fn_1(scan_right_1) fplus_curry_define_fn_0(sum) fplus_curry_define_fn_0(product) fplus_curry_define_fn_1(append_elem) fplus_curry_define_fn_1(prepend_elem) fplus_curry_define_fn_1(append) fplus_curry_define_fn_1(append_convert) fplus_curry_define_fn_0(concat) fplus_curry_define_fn_1(interweave) fplus_curry_define_fn_0(unweave) fplus_curry_define_fn_1(sort_by) fplus_curry_define_fn_1(sort_on) fplus_curry_define_fn_0(sort) fplus_curry_define_fn_1(stable_sort_by) fplus_curry_define_fn_1(stable_sort_on) fplus_curry_define_fn_0(stable_sort) fplus_curry_define_fn_2(partial_sort_by) fplus_curry_define_fn_2(partial_sort_on) fplus_curry_define_fn_1(partial_sort) fplus_curry_define_fn_2(nth_element_by) fplus_curry_define_fn_2(nth_element_on) fplus_curry_define_fn_1(nth_element) fplus_curry_define_fn_1(unique_by) fplus_curry_define_fn_1(unique_on) fplus_curry_define_fn_0(unique) fplus_curry_define_fn_1(intersperse) fplus_curry_define_fn_1(join) fplus_curry_define_fn_1(join_elem) fplus_curry_define_fn_1(is_elem_of_by) fplus_curry_define_fn_1(is_elem_of) fplus_curry_define_fn_1(nub_by) fplus_curry_define_fn_1(nub_on) fplus_curry_define_fn_0(nub) fplus_curry_define_fn_1(all_unique_by_eq) fplus_curry_define_fn_1(all_unique_on) fplus_curry_define_fn_0(all_unique) fplus_curry_define_fn_1(is_strictly_sorted_by) fplus_curry_define_fn_1(is_strictly_sorted_on) fplus_curry_define_fn_0(is_strictly_sorted) fplus_curry_define_fn_1(is_sorted_by) fplus_curry_define_fn_1(is_sorted_on) fplus_curry_define_fn_0(is_sorted) fplus_curry_define_fn_1(is_prefix_of) fplus_curry_define_fn_1(is_suffix_of) fplus_curry_define_fn_1(all_by) fplus_curry_define_fn_0(all) fplus_curry_define_fn_1(all_the_same_by) fplus_curry_define_fn_1(all_the_same_on) fplus_curry_define_fn_0(all_the_same) fplus_curry_define_fn_2(numbers_step) fplus_curry_define_fn_1(numbers) fplus_curry_define_fn_0(singleton_seq) fplus_curry_define_fn_0(all_idxs) fplus_curry_define_fn_0(init) fplus_curry_define_fn_0(tail) fplus_curry_define_fn_0(head) fplus_curry_define_fn_0(last) fplus_curry_define_fn_0(mean_stddev) fplus_curry_define_fn_1(count_occurrences_by) fplus_curry_define_fn_0(count_occurrences) fplus_curry_define_fn_2(lexicographical_less_by) fplus_curry_define_fn_1(lexicographical_less) fplus_curry_define_fn_0(lexicographical_sort) fplus_curry_define_fn_1(replicate) fplus_curry_define_fn_2(instead_of_if) fplus_curry_define_fn_2(instead_of_if_empty) fplus_curry_define_fn_0(is_ok) fplus_curry_define_fn_0(is_error) fplus_curry_define_fn_0(unsafe_get_ok) fplus_curry_define_fn_0(unsafe_get_error) fplus_curry_define_fn_1(ok_with_default) fplus_curry_define_fn_0(ok) fplus_curry_define_fn_0(error) fplus_curry_define_fn_0(to_maybe) fplus_curry_define_fn_1(from_maybe) fplus_curry_define_fn_1(throw_on_error) fplus_curry_define_fn_1(lift_result) fplus_curry_define_fn_2(lift_result_both) fplus_curry_define_fn_2(unify_result) fplus_curry_define_fn_1(and_then_result) fplus_curry_define_fn_1(keep_if) fplus_curry_define_fn_1(drop_if) fplus_curry_define_fn_1(without) fplus_curry_define_fn_1(without_any) fplus_curry_define_fn_1(keep_if_with_idx) fplus_curry_define_fn_1(drop_if_with_idx) fplus_curry_define_fn_1(keep_by_idx) fplus_curry_define_fn_1(drop_by_idx) fplus_curry_define_fn_1(keep_idxs) fplus_curry_define_fn_1(drop_idxs) fplus_curry_define_fn_1(drop_idx) fplus_curry_define_fn_0(justs) fplus_curry_define_fn_0(oks) fplus_curry_define_fn_0(errors) fplus_curry_define_fn_1(trim_left) fplus_curry_define_fn_1(trim_token_left) fplus_curry_define_fn_1(trim_right_by) fplus_curry_define_fn_1(trim_right) fplus_curry_define_fn_1(trim_token_right) fplus_curry_define_fn_1(trim_by) fplus_curry_define_fn_1(trim) fplus_curry_define_fn_1(trim_token) fplus_curry_define_fn_1(adjacent_keep_snd_if) fplus_curry_define_fn_1(adjacent_drop_fst_if) fplus_curry_define_fn_1(adjacent_drop_snd_if) fplus_curry_define_fn_1(adjacent_keep_fst_if) fplus_curry_define_fn_1(apply_to_pair) fplus_curry_define_fn_2(zip_with) fplus_curry_define_fn_3(zip_with_3) fplus_curry_define_fn_4(zip_with_defaults) fplus_curry_define_fn_1(zip) fplus_curry_define_fn_0(unzip) fplus_curry_define_fn_0(fst) fplus_curry_define_fn_0(snd) fplus_curry_define_fn_1(transform_fst) fplus_curry_define_fn_1(transform_snd) fplus_curry_define_fn_2(transform_pair) fplus_curry_define_fn_0(swap_pair_elems) fplus_curry_define_fn_0(swap_pairs_elems) fplus_curry_define_fn_0(adjacent_pairs) fplus_curry_define_fn_0(overlapping_pairs) fplus_curry_define_fn_0(overlapping_pairs_cyclic) fplus_curry_define_fn_0(enumerate) fplus_curry_define_fn_4(inner_product_with) fplus_curry_define_fn_2(inner_product) fplus_curry_define_fn_2(first_mismatch_idx_by) fplus_curry_define_fn_2(first_mismatch_by) fplus_curry_define_fn_2(first_mismatch_idx_on) fplus_curry_define_fn_2(first_mismatch_on) fplus_curry_define_fn_2(first_mismatch_idx) fplus_curry_define_fn_2(first_mismatch) fplus_curry_define_fn_2(first_match_idx_by) fplus_curry_define_fn_2(first_match_by) fplus_curry_define_fn_2(first_match_idx_on) fplus_curry_define_fn_2(first_match_on) fplus_curry_define_fn_2(first_match_idx) fplus_curry_define_fn_2(first_match) fplus_curry_define_fn_2(is_in_interval) fplus_curry_define_fn_2(is_in_interval_around) fplus_curry_define_fn_2(is_in_open_interval) fplus_curry_define_fn_2(is_in_open_interval_around) fplus_curry_define_fn_2(is_in_closed_interval) fplus_curry_define_fn_4(reference_interval) fplus_curry_define_fn_2(clamp) fplus_curry_define_fn_0(is_negative) fplus_curry_define_fn_0(is_positive) fplus_curry_define_fn_0(is_even) fplus_curry_define_fn_0(is_odd) fplus_curry_define_fn_0(abs) fplus_curry_define_fn_1(abs_diff) fplus_curry_define_fn_0(square) fplus_curry_define_fn_0(cube) fplus_curry_define_fn_0(sign) fplus_curry_define_fn_0(sign_with_zero) fplus_curry_define_fn_0(integral_cast_throw) fplus_curry_define_fn_0(integral_cast_clamp) fplus_curry_define_fn_0(round) fplus_curry_define_fn_0(floor) fplus_curry_define_fn_1(floor_to_int_mult) fplus_curry_define_fn_1(ceil_to_int_mult) fplus_curry_define_fn_0(ceil) fplus_curry_define_fn_1(int_power) fplus_curry_define_fn_2(min_2_on) fplus_curry_define_fn_2(max_2_on) fplus_curry_define_fn_1(min_2) fplus_curry_define_fn_1(max_2) fplus_curry_define_fn_0(deg_to_rad) fplus_curry_define_fn_0(rad_to_deg) fplus_curry_define_fn_2(normalize_min_max) fplus_curry_define_fn_2(normalize_mean_stddev) fplus_curry_define_fn_0(standardize) fplus_curry_define_fn_1(histogram_using_intervals) fplus_curry_define_fn_2(generate_consecutive_intervals) fplus_curry_define_fn_3(histogram) fplus_curry_define_fn_1(modulo_chain) fplus_curry_define_fn_2(line_equation) fplus_curry_define_fn_1(generate_by_idx) fplus_curry_define_fn_1(repeat) fplus_curry_define_fn_1(infixes) fplus_curry_define_fn_3(carthesian_product_with_where) fplus_curry_define_fn_2(carthesian_product_with) fplus_curry_define_fn_2(carthesian_product_where) fplus_curry_define_fn_1(carthesian_product) fplus_curry_define_fn_1(carthesian_product_n) fplus_curry_define_fn_1(permutations) fplus_curry_define_fn_1(combinations) fplus_curry_define_fn_1(combinations_with_replacement) fplus_curry_define_fn_0(power_set) fplus_curry_define_fn_2(iterate) fplus_curry_define_fn_1(iterate_maybe) fplus_curry_define_fn_1(adjacent_difference_by) fplus_curry_define_fn_0(adjacent_difference) fplus_curry_define_fn_0(rotate_left) fplus_curry_define_fn_0(rotate_right) fplus_curry_define_fn_0(rotations_left) fplus_curry_define_fn_0(rotations_right) fplus_curry_define_fn_2(fill_left) fplus_curry_define_fn_2(fill_right) fplus_curry_define_fn_0(inits) fplus_curry_define_fn_0(tails) fplus_curry_define_fn_1(find_first_by) fplus_curry_define_fn_1(find_last_by) fplus_curry_define_fn_1(find_first_idx_by) fplus_curry_define_fn_1(find_last_idx_by) fplus_curry_define_fn_1(find_first_idx) fplus_curry_define_fn_1(find_last_idx) fplus_curry_define_fn_1(find_all_idxs_by) fplus_curry_define_fn_1(find_all_idxs_of) fplus_curry_define_fn_1(find_all_instances_of_token) fplus_curry_define_fn_1(find_all_instances_of_token_non_overlapping) fplus_curry_define_fn_1(find_first_instance_of_token) fplus_curry_define_fn_1(set_includes) fplus_curry_define_fn_1(unordered_set_includes) fplus_curry_define_fn_1(set_merge) fplus_curry_define_fn_1(unordered_set_merge) fplus_curry_define_fn_1(set_intersection) fplus_curry_define_fn_1(unordered_set_intersection) fplus_curry_define_fn_1(set_is_disjoint) fplus_curry_define_fn_1(unordered_set_is_disjoint) fplus_curry_define_fn_1(set_difference) fplus_curry_define_fn_1(unordered_set_difference) fplus_curry_define_fn_1(set_symmetric_difference) fplus_curry_define_fn_1(unordered_set_symmetric_difference) fplus_curry_define_fn_0(sets_intersection) fplus_curry_define_fn_0(unordered_sets_intersection) fplus_curry_define_fn_1(any_by) fplus_curry_define_fn_0(any) fplus_curry_define_fn_1(none_by) fplus_curry_define_fn_0(none) fplus_curry_define_fn_1(minimum_idx_by) fplus_curry_define_fn_1(minimum_idx_by_maybe) fplus_curry_define_fn_1(maximum_idx_by) fplus_curry_define_fn_1(maximum_idx_by_maybe) fplus_curry_define_fn_0(minimum_idx) fplus_curry_define_fn_0(minimum_idx_maybe) fplus_curry_define_fn_0(maximum_idx) fplus_curry_define_fn_0(maximum_idx_maybe) fplus_curry_define_fn_1(minimum_idx_on) fplus_curry_define_fn_1(minimum_idx_on_maybe) fplus_curry_define_fn_1(maximum_idx_on) fplus_curry_define_fn_1(maximum_idx_on_maybe) fplus_curry_define_fn_1(minimum_by) fplus_curry_define_fn_1(minimum_by_maybe) fplus_curry_define_fn_1(maximum_by) fplus_curry_define_fn_1(maximum_by_maybe) fplus_curry_define_fn_0(minimum) fplus_curry_define_fn_0(minimum_maybe) fplus_curry_define_fn_0(maximum) fplus_curry_define_fn_0(maximum_maybe) fplus_curry_define_fn_1(minimum_on) fplus_curry_define_fn_1(minimum_on_maybe) fplus_curry_define_fn_1(maximum_on) fplus_curry_define_fn_1(maximum_on_maybe) fplus_curry_define_fn_0(mean) fplus_curry_define_fn_0(mean_obj_div_size_t) fplus_curry_define_fn_0(mean_obj_div_double) fplus_curry_define_fn_0(mean_using_doubles) fplus_curry_define_fn_0(median) fplus_curry_define_fn_1(all_unique_by_less) fplus_curry_define_fn_0(all_unique_less) fplus_curry_define_fn_1(is_infix_of) fplus_curry_define_fn_1(is_subsequence_of) fplus_curry_define_fn_1(count_if) fplus_curry_define_fn_1(count) fplus_curry_define_fn_1(is_unique_in_by) fplus_curry_define_fn_1(is_unique_in) fplus_curry_define_fn_1(is_permutation_of) fplus_curry_define_fn_1(fill_pigeonholes_to) fplus_curry_define_fn_0(fill_pigeonholes) fplus_curry_define_fn_1(fill_pigeonholes_bool_to) fplus_curry_define_fn_0(fill_pigeonholes_bool) fplus_curry_define_fn_0(present_in_all) fplus_curry_define_fn_1(elem_at_idx_or_nothing) fplus_curry_define_fn_2(elem_at_idx_or_constant) fplus_curry_define_fn_1(elem_at_idx_or_replicate) fplus_curry_define_fn_1(elem_at_idx_or_wrap) fplus_curry_define_fn_2(extrapolate_replicate) fplus_curry_define_fn_2(extrapolate_wrap) fplus_curry_define_fn_1(elem_at_float_idx) fplus_curry_define_fn_0(pairs_to_map) fplus_curry_define_fn_0(pairs_to_map_grouped) fplus_curry_define_fn_0(map_to_pairs) fplus_curry_define_fn_1(transform_map_values) fplus_curry_define_fn_2(map_union_with) fplus_curry_define_fn_1(map_union) fplus_curry_define_fn_0(get_map_keys) fplus_curry_define_fn_0(get_map_values) fplus_curry_define_fn_0(swap_keys_and_values) fplus_curry_define_fn_1(create_map) fplus_curry_define_fn_1(create_map_with) fplus_curry_define_fn_1(create_unordered_map) fplus_curry_define_fn_1(create_unordered_map_with) fplus_curry_define_fn_1(get_from_map) fplus_curry_define_fn_1(get_from_map_unsafe) fplus_curry_define_fn_2(get_from_map_with_def) fplus_curry_define_fn_1(get_first_from_map) fplus_curry_define_fn_1(get_first_from_map_unsafe) fplus_curry_define_fn_2(get_first_from_map_with_def) fplus_curry_define_fn_1(map_contains) fplus_curry_define_fn_1(map_keep_if) fplus_curry_define_fn_1(map_drop_if) fplus_curry_define_fn_1(map_keep) fplus_curry_define_fn_1(map_drop) fplus_curry_define_fn_1(map_keep_if_value) fplus_curry_define_fn_1(map_drop_if_value) fplus_curry_define_fn_1(map_keep_values) fplus_curry_define_fn_1(map_drop_values) fplus_curry_define_fn_1(map_pluck) fplus_curry_define_fn_1(choose) fplus_curry_define_fn_2(choose_by) fplus_curry_define_fn_1(choose_lazy) fplus_curry_define_fn_2(choose_by_lazy) fplus_curry_define_fn_1(choose_def) fplus_curry_define_fn_2(choose_by_def) fplus_curry_define_fn_1(choose_def_lazy) fplus_curry_define_fn_2(choose_by_def_lazy) fplus_curry_define_fn_1(group_by) fplus_curry_define_fn_1(group_on) fplus_curry_define_fn_1(group_on_labeled) fplus_curry_define_fn_0(group) fplus_curry_define_fn_1(group_globally_by) fplus_curry_define_fn_1(group_globally_on) fplus_curry_define_fn_1(group_globally_on_labeled) fplus_curry_define_fn_0(group_globally) fplus_curry_define_fn_1(cluster_by) fplus_curry_define_fn_2(split_by) fplus_curry_define_fn_1(split_by_keep_separators) fplus_curry_define_fn_2(split) fplus_curry_define_fn_2(split_one_of) fplus_curry_define_fn_1(split_keep_separators) fplus_curry_define_fn_1(split_at_idx) fplus_curry_define_fn_2(insert_at_idx) fplus_curry_define_fn_1(partition) fplus_curry_define_fn_1(split_at_idxs) fplus_curry_define_fn_1(split_every) fplus_curry_define_fn_2(split_by_token) fplus_curry_define_fn_1(run_length_encode_by) fplus_curry_define_fn_0(run_length_encode) fplus_curry_define_fn_0(run_length_decode) fplus_curry_define_fn_1(span) fplus_curry_define_fn_2(divvy) fplus_curry_define_fn_1(aperture) fplus_curry_define_fn_1(stride) fplus_curry_define_fn_1(winsorize) fplus_curry_define_fn_1(separate_on) fplus_curry_define_fn_0(separate) fplus_curry_define_fn_1(transform_with_idx) fplus_curry_define_fn_1(transform_and_keep_justs) fplus_curry_define_fn_1(transform_and_keep_oks) fplus_curry_define_fn_1(transform_and_concat) fplus_curry_define_fn_1(replicate_elems) fplus_curry_define_fn_0(interleave) fplus_curry_define_fn_0(transpose) fplus_curry_define_fn_1(shuffle) fplus_curry_define_fn_2(sample) fplus_curry_define_fn_1(random_element) fplus_curry_define_fn_2(random_elements) fplus_curry_define_fn_1(apply_functions) fplus_curry_define_fn_2(apply_function_n_times) fplus_curry_define_fn_1(transform_parallelly) fplus_curry_define_fn_2(transform_parallelly_n_threads) fplus_curry_define_fn_2(reduce_parallelly) fplus_curry_define_fn_3(reduce_parallelly_n_threads) fplus_curry_define_fn_1(reduce_1_parallelly) fplus_curry_define_fn_2(reduce_1_parallelly_n_threads) fplus_curry_define_fn_1(keep_if_parallelly) fplus_curry_define_fn_2(keep_if_parallelly_n_threads) fplus_curry_define_fn_3(transform_reduce) fplus_curry_define_fn_2(transform_reduce_1) fplus_curry_define_fn_3(transform_reduce_parallelly) fplus_curry_define_fn_4(transform_reduce_parallelly_n_threads) fplus_curry_define_fn_2(transform_reduce_1_parallelly) fplus_curry_define_fn_3(transform_reduce_1_parallelly_n_threads) fplus_curry_define_fn_1(read_value_with_default) fplus_curry_define_fn_2(replace_if) fplus_curry_define_fn_2(replace_elem_at_idx) fplus_curry_define_fn_2(replace_elems) fplus_curry_define_fn_2(replace_tokens) fplus_curry_define_fn_0(show) fplus_curry_define_fn_3(show_cont_with_frame_and_newlines) fplus_curry_define_fn_3(show_cont_with_frame) fplus_curry_define_fn_1(show_cont_with) fplus_curry_define_fn_0(show_cont) fplus_curry_define_fn_0(show_maybe) fplus_curry_define_fn_0(show_result) fplus_curry_define_fn_2(show_float) fplus_curry_define_fn_3(show_float_fill_left) fplus_curry_define_fn_2(show_fill_left) fplus_curry_define_fn_2(show_fill_right) fplus_curry_define_fn_0(is_letter_or_digit) fplus_curry_define_fn_0(is_whitespace) fplus_curry_define_fn_0(is_line_break) fplus_curry_define_fn_0(clean_newlines) fplus_curry_define_fn_1(split_words) fplus_curry_define_fn_1(split_lines) fplus_curry_define_fn_0(trim_whitespace_left) fplus_curry_define_fn_0(trim_whitespace_right) fplus_curry_define_fn_0(trim_whitespace) fplus_curry_define_fn_0(to_lower_case) fplus_curry_define_fn_1(to_lower_case_loc) fplus_curry_define_fn_0(to_upper_case) fplus_curry_define_fn_1(to_upper_case_loc) fplus_curry_define_fn_2(to_string_fill_left) fplus_curry_define_fn_2(to_string_fill_right) fplus_curry_define_fn_1(trees_from_sequence) fplus_curry_define_fn_1(are_trees_equal) fplus_curry_define_fn_0(tree_size) fplus_curry_define_fn_0(tree_depth) fplus_curry_define_fn_0(flatten_tree_depth_first) fplus_curry_define_fn_0(flatten_tree_breadth_first) fplus_curry_define_fn_0(show_timed) fplus_curry_define_fn_0(make_timed_function) fplus_curry_define_fn_0(make_timed_void_function) } // namespace curry } // namespace fplus // // fwd.hpp // // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) namespace fplus { namespace fwd { // Partial currying. // Allow to generically bind all but parameters except the last one. // The lambda paramter ist named fplus_fwd_x instead of x // because gcc can produce unjustified shadow warnings. see: // http://stackoverflow.com/questions/41208811/parameter-of-returned-generic-lambda-allegedly-shadows-parameter-of-free-functio #define fplus_fwd_define_fn_0(fplus_fwd_define_fn_0_name) \ inline auto fplus_fwd_define_fn_0_name() \ { \ return [](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_0_name(std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_define_fn_1(fplus_fwd_define_fn_1_name) \ template \ auto fplus_fwd_define_fn_1_name(P1 p1) \ { \ return [p1](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_1_name(p1, std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_define_fn_2(fplus_fwd_define_fn_2_name) \ template \ auto fplus_fwd_define_fn_2_name(P1 p1, P2 p2) \ { \ return [p1, p2](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_2_name(p1, p2, std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_define_fn_3(fplus_fwd_define_fn_3_name) \ template \ auto fplus_fwd_define_fn_3_name(P1 p1, P2 p2, P3 p3) \ { \ return [p1, p2, p3](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_3_name(p1, p2, p3, std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_define_fn_4(fplus_fwd_define_fn_4_name) \ template \ auto fplus_fwd_define_fn_4_name(P1 p1, P2 p2, P3 p3, P4 p4) \ { \ return [p1, p2, p3, p4](auto&& fplus_fwd_x) \ { \ return fplus::fplus_fwd_define_fn_4_name(p1, p2, p3, p4, std::forward(fplus_fwd_x)); \ }; \ } #define fplus_fwd_flip_define_fn_1(fplus_fwd_flip_define_fn_1_name) \ namespace flip \ { \ template \ auto fplus_fwd_flip_define_fn_1_name(P2 p2) \ { \ return [p2](auto&& fplus_fwd_flip_x) \ { \ return fplus::fplus_fwd_flip_define_fn_1_name(std::forward(fplus_fwd_flip_x), p2); \ }; \ } \ } // namespace flip namespace internal { template struct compose_helper{ compose_helper(F f, G g) : f_(f), g_(g) {} template decltype(auto) operator()(X&& x) const { return g_(f_(std::forward(x))); } private: F f_; G g_; }; } // namespace internal template auto compose(F f, G g) { return internal::compose_helper {f, g}; } template auto compose(F1 f, Fs ... args) { return compose(f, compose(args...)); } template auto apply(X&& x, Fs ... args) { return compose(args...)(std::forward(x)); } template auto apply(X&& x, F f) { return f(std::forward(x)); } // // fwd_instances.autogenerated_defines // // THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT EDIT. fplus_fwd_define_fn_0(identity) fplus_fwd_define_fn_1(is_equal) fplus_fwd_define_fn_1(is_not_equal) fplus_fwd_define_fn_1(is_less) fplus_fwd_define_fn_1(is_less_or_equal) fplus_fwd_define_fn_1(is_greater) fplus_fwd_define_fn_1(is_greater_or_equal) fplus_fwd_define_fn_1(xor_bools) fplus_fwd_define_fn_0(is_just) fplus_fwd_define_fn_0(is_nothing) fplus_fwd_define_fn_0(unsafe_get_just) fplus_fwd_define_fn_0(just_with_default) fplus_fwd_define_fn_1(throw_on_nothing) fplus_fwd_define_fn_0(just) fplus_fwd_define_fn_1(as_just_if) fplus_fwd_define_fn_0(maybe_to_seq) fplus_fwd_define_fn_0(singleton_seq_as_maybe) fplus_fwd_define_fn_1(lift_maybe) fplus_fwd_define_fn_2(lift_maybe_def) fplus_fwd_define_fn_2(lift_maybe_2) fplus_fwd_define_fn_3(lift_maybe_2_def) fplus_fwd_define_fn_1(and_then_maybe) fplus_fwd_define_fn_0(flatten_maybe) fplus_fwd_define_fn_0(is_empty) fplus_fwd_define_fn_0(is_not_empty) fplus_fwd_define_fn_0(size_of_cont) fplus_fwd_define_fn_0(convert) fplus_fwd_define_fn_0(convert_elems) fplus_fwd_define_fn_0(convert_container) fplus_fwd_define_fn_0(convert_container_and_elems) fplus_fwd_define_fn_2(get_segment) fplus_fwd_define_fn_2(set_segment) fplus_fwd_define_fn_2(remove_segment) fplus_fwd_define_fn_2(insert_at) fplus_fwd_define_fn_1(elem_at_idx) fplus_fwd_define_fn_1(elem_at_idx_maybe) fplus_fwd_define_fn_1(elems_at_idxs) fplus_fwd_define_fn_1(transform) fplus_fwd_define_fn_1(transform_convert) fplus_fwd_define_fn_1(transform_inner) fplus_fwd_define_fn_0(reverse) fplus_fwd_define_fn_1(take) fplus_fwd_define_fn_1(take_exact) fplus_fwd_define_fn_1(take_cyclic) fplus_fwd_define_fn_1(drop) fplus_fwd_define_fn_1(take_last) fplus_fwd_define_fn_1(drop_last) fplus_fwd_define_fn_1(drop_exact) fplus_fwd_define_fn_1(take_while) fplus_fwd_define_fn_1(drop_while) fplus_fwd_define_fn_2(fold_left) fplus_fwd_define_fn_2(reduce) fplus_fwd_define_fn_1(fold_left_1) fplus_fwd_define_fn_1(reduce_1) fplus_fwd_define_fn_2(fold_right) fplus_fwd_define_fn_1(fold_right_1) fplus_fwd_define_fn_2(scan_left) fplus_fwd_define_fn_1(scan_left_1) fplus_fwd_define_fn_2(scan_right) fplus_fwd_define_fn_1(scan_right_1) fplus_fwd_define_fn_0(sum) fplus_fwd_define_fn_0(product) fplus_fwd_define_fn_1(append_elem) fplus_fwd_define_fn_1(prepend_elem) fplus_fwd_define_fn_1(append) fplus_fwd_define_fn_1(append_convert) fplus_fwd_define_fn_0(concat) fplus_fwd_define_fn_1(interweave) fplus_fwd_define_fn_0(unweave) fplus_fwd_define_fn_1(sort_by) fplus_fwd_define_fn_1(sort_on) fplus_fwd_define_fn_0(sort) fplus_fwd_define_fn_1(stable_sort_by) fplus_fwd_define_fn_1(stable_sort_on) fplus_fwd_define_fn_0(stable_sort) fplus_fwd_define_fn_2(partial_sort_by) fplus_fwd_define_fn_2(partial_sort_on) fplus_fwd_define_fn_1(partial_sort) fplus_fwd_define_fn_2(nth_element_by) fplus_fwd_define_fn_2(nth_element_on) fplus_fwd_define_fn_1(nth_element) fplus_fwd_define_fn_1(unique_by) fplus_fwd_define_fn_1(unique_on) fplus_fwd_define_fn_0(unique) fplus_fwd_define_fn_1(intersperse) fplus_fwd_define_fn_1(join) fplus_fwd_define_fn_1(join_elem) fplus_fwd_define_fn_1(is_elem_of_by) fplus_fwd_define_fn_1(is_elem_of) fplus_fwd_define_fn_1(nub_by) fplus_fwd_define_fn_1(nub_on) fplus_fwd_define_fn_0(nub) fplus_fwd_define_fn_1(all_unique_by_eq) fplus_fwd_define_fn_1(all_unique_on) fplus_fwd_define_fn_0(all_unique) fplus_fwd_define_fn_1(is_strictly_sorted_by) fplus_fwd_define_fn_1(is_strictly_sorted_on) fplus_fwd_define_fn_0(is_strictly_sorted) fplus_fwd_define_fn_1(is_sorted_by) fplus_fwd_define_fn_1(is_sorted_on) fplus_fwd_define_fn_0(is_sorted) fplus_fwd_define_fn_1(is_prefix_of) fplus_fwd_define_fn_1(is_suffix_of) fplus_fwd_define_fn_1(all_by) fplus_fwd_define_fn_0(all) fplus_fwd_define_fn_1(all_the_same_by) fplus_fwd_define_fn_1(all_the_same_on) fplus_fwd_define_fn_0(all_the_same) fplus_fwd_define_fn_2(numbers_step) fplus_fwd_define_fn_1(numbers) fplus_fwd_define_fn_0(singleton_seq) fplus_fwd_define_fn_0(all_idxs) fplus_fwd_define_fn_0(init) fplus_fwd_define_fn_0(tail) fplus_fwd_define_fn_0(head) fplus_fwd_define_fn_0(last) fplus_fwd_define_fn_0(mean_stddev) fplus_fwd_define_fn_1(count_occurrences_by) fplus_fwd_define_fn_0(count_occurrences) fplus_fwd_define_fn_2(lexicographical_less_by) fplus_fwd_define_fn_1(lexicographical_less) fplus_fwd_define_fn_0(lexicographical_sort) fplus_fwd_define_fn_1(replicate) fplus_fwd_define_fn_2(instead_of_if) fplus_fwd_define_fn_2(instead_of_if_empty) fplus_fwd_define_fn_0(is_ok) fplus_fwd_define_fn_0(is_error) fplus_fwd_define_fn_0(unsafe_get_ok) fplus_fwd_define_fn_0(unsafe_get_error) fplus_fwd_define_fn_1(ok_with_default) fplus_fwd_define_fn_0(ok) fplus_fwd_define_fn_0(error) fplus_fwd_define_fn_0(to_maybe) fplus_fwd_define_fn_1(from_maybe) fplus_fwd_define_fn_1(throw_on_error) fplus_fwd_define_fn_1(lift_result) fplus_fwd_define_fn_2(lift_result_both) fplus_fwd_define_fn_2(unify_result) fplus_fwd_define_fn_1(and_then_result) fplus_fwd_define_fn_1(keep_if) fplus_fwd_define_fn_1(drop_if) fplus_fwd_define_fn_1(without) fplus_fwd_define_fn_1(without_any) fplus_fwd_define_fn_1(keep_if_with_idx) fplus_fwd_define_fn_1(drop_if_with_idx) fplus_fwd_define_fn_1(keep_by_idx) fplus_fwd_define_fn_1(drop_by_idx) fplus_fwd_define_fn_1(keep_idxs) fplus_fwd_define_fn_1(drop_idxs) fplus_fwd_define_fn_1(drop_idx) fplus_fwd_define_fn_0(justs) fplus_fwd_define_fn_0(oks) fplus_fwd_define_fn_0(errors) fplus_fwd_define_fn_1(trim_left) fplus_fwd_define_fn_1(trim_token_left) fplus_fwd_define_fn_1(trim_right_by) fplus_fwd_define_fn_1(trim_right) fplus_fwd_define_fn_1(trim_token_right) fplus_fwd_define_fn_1(trim_by) fplus_fwd_define_fn_1(trim) fplus_fwd_define_fn_1(trim_token) fplus_fwd_define_fn_1(adjacent_keep_snd_if) fplus_fwd_define_fn_1(adjacent_drop_fst_if) fplus_fwd_define_fn_1(adjacent_drop_snd_if) fplus_fwd_define_fn_1(adjacent_keep_fst_if) fplus_fwd_define_fn_1(apply_to_pair) fplus_fwd_define_fn_2(zip_with) fplus_fwd_define_fn_3(zip_with_3) fplus_fwd_define_fn_4(zip_with_defaults) fplus_fwd_define_fn_1(zip) fplus_fwd_define_fn_0(unzip) fplus_fwd_define_fn_0(fst) fplus_fwd_define_fn_0(snd) fplus_fwd_define_fn_1(transform_fst) fplus_fwd_define_fn_1(transform_snd) fplus_fwd_define_fn_2(transform_pair) fplus_fwd_define_fn_0(swap_pair_elems) fplus_fwd_define_fn_0(swap_pairs_elems) fplus_fwd_define_fn_0(adjacent_pairs) fplus_fwd_define_fn_0(overlapping_pairs) fplus_fwd_define_fn_0(overlapping_pairs_cyclic) fplus_fwd_define_fn_0(enumerate) fplus_fwd_define_fn_4(inner_product_with) fplus_fwd_define_fn_2(inner_product) fplus_fwd_define_fn_2(first_mismatch_idx_by) fplus_fwd_define_fn_2(first_mismatch_by) fplus_fwd_define_fn_2(first_mismatch_idx_on) fplus_fwd_define_fn_2(first_mismatch_on) fplus_fwd_define_fn_2(first_mismatch_idx) fplus_fwd_define_fn_2(first_mismatch) fplus_fwd_define_fn_2(first_match_idx_by) fplus_fwd_define_fn_2(first_match_by) fplus_fwd_define_fn_2(first_match_idx_on) fplus_fwd_define_fn_2(first_match_on) fplus_fwd_define_fn_2(first_match_idx) fplus_fwd_define_fn_2(first_match) fplus_fwd_define_fn_2(is_in_interval) fplus_fwd_define_fn_2(is_in_interval_around) fplus_fwd_define_fn_2(is_in_open_interval) fplus_fwd_define_fn_2(is_in_open_interval_around) fplus_fwd_define_fn_2(is_in_closed_interval) fplus_fwd_define_fn_4(reference_interval) fplus_fwd_define_fn_2(clamp) fplus_fwd_define_fn_0(is_negative) fplus_fwd_define_fn_0(is_positive) fplus_fwd_define_fn_0(is_even) fplus_fwd_define_fn_0(is_odd) fplus_fwd_define_fn_0(abs) fplus_fwd_define_fn_1(abs_diff) fplus_fwd_define_fn_0(square) fplus_fwd_define_fn_0(cube) fplus_fwd_define_fn_0(sign) fplus_fwd_define_fn_0(sign_with_zero) fplus_fwd_define_fn_0(integral_cast_throw) fplus_fwd_define_fn_0(integral_cast_clamp) fplus_fwd_define_fn_0(round) fplus_fwd_define_fn_0(floor) fplus_fwd_define_fn_1(floor_to_int_mult) fplus_fwd_define_fn_1(ceil_to_int_mult) fplus_fwd_define_fn_0(ceil) fplus_fwd_define_fn_1(int_power) fplus_fwd_define_fn_2(min_2_on) fplus_fwd_define_fn_2(max_2_on) fplus_fwd_define_fn_1(min_2) fplus_fwd_define_fn_1(max_2) fplus_fwd_define_fn_0(deg_to_rad) fplus_fwd_define_fn_0(rad_to_deg) fplus_fwd_define_fn_2(normalize_min_max) fplus_fwd_define_fn_2(normalize_mean_stddev) fplus_fwd_define_fn_0(standardize) fplus_fwd_define_fn_1(histogram_using_intervals) fplus_fwd_define_fn_2(generate_consecutive_intervals) fplus_fwd_define_fn_3(histogram) fplus_fwd_define_fn_1(modulo_chain) fplus_fwd_define_fn_2(line_equation) fplus_fwd_define_fn_1(generate_by_idx) fplus_fwd_define_fn_1(repeat) fplus_fwd_define_fn_1(infixes) fplus_fwd_define_fn_3(carthesian_product_with_where) fplus_fwd_define_fn_2(carthesian_product_with) fplus_fwd_define_fn_2(carthesian_product_where) fplus_fwd_define_fn_1(carthesian_product) fplus_fwd_define_fn_1(carthesian_product_n) fplus_fwd_define_fn_1(permutations) fplus_fwd_define_fn_1(combinations) fplus_fwd_define_fn_1(combinations_with_replacement) fplus_fwd_define_fn_0(power_set) fplus_fwd_define_fn_2(iterate) fplus_fwd_define_fn_1(iterate_maybe) fplus_fwd_define_fn_1(adjacent_difference_by) fplus_fwd_define_fn_0(adjacent_difference) fplus_fwd_define_fn_0(rotate_left) fplus_fwd_define_fn_0(rotate_right) fplus_fwd_define_fn_0(rotations_left) fplus_fwd_define_fn_0(rotations_right) fplus_fwd_define_fn_2(fill_left) fplus_fwd_define_fn_2(fill_right) fplus_fwd_define_fn_0(inits) fplus_fwd_define_fn_0(tails) fplus_fwd_define_fn_1(find_first_by) fplus_fwd_define_fn_1(find_last_by) fplus_fwd_define_fn_1(find_first_idx_by) fplus_fwd_define_fn_1(find_last_idx_by) fplus_fwd_define_fn_1(find_first_idx) fplus_fwd_define_fn_1(find_last_idx) fplus_fwd_define_fn_1(find_all_idxs_by) fplus_fwd_define_fn_1(find_all_idxs_of) fplus_fwd_define_fn_1(find_all_instances_of_token) fplus_fwd_define_fn_1(find_all_instances_of_token_non_overlapping) fplus_fwd_define_fn_1(find_first_instance_of_token) fplus_fwd_define_fn_1(set_includes) fplus_fwd_define_fn_1(unordered_set_includes) fplus_fwd_define_fn_1(set_merge) fplus_fwd_define_fn_1(unordered_set_merge) fplus_fwd_define_fn_1(set_intersection) fplus_fwd_define_fn_1(unordered_set_intersection) fplus_fwd_define_fn_1(set_is_disjoint) fplus_fwd_define_fn_1(unordered_set_is_disjoint) fplus_fwd_define_fn_1(set_difference) fplus_fwd_define_fn_1(unordered_set_difference) fplus_fwd_define_fn_1(set_symmetric_difference) fplus_fwd_define_fn_1(unordered_set_symmetric_difference) fplus_fwd_define_fn_0(sets_intersection) fplus_fwd_define_fn_0(unordered_sets_intersection) fplus_fwd_define_fn_1(any_by) fplus_fwd_define_fn_0(any) fplus_fwd_define_fn_1(none_by) fplus_fwd_define_fn_0(none) fplus_fwd_define_fn_1(minimum_idx_by) fplus_fwd_define_fn_1(minimum_idx_by_maybe) fplus_fwd_define_fn_1(maximum_idx_by) fplus_fwd_define_fn_1(maximum_idx_by_maybe) fplus_fwd_define_fn_0(minimum_idx) fplus_fwd_define_fn_0(minimum_idx_maybe) fplus_fwd_define_fn_0(maximum_idx) fplus_fwd_define_fn_0(maximum_idx_maybe) fplus_fwd_define_fn_1(minimum_idx_on) fplus_fwd_define_fn_1(minimum_idx_on_maybe) fplus_fwd_define_fn_1(maximum_idx_on) fplus_fwd_define_fn_1(maximum_idx_on_maybe) fplus_fwd_define_fn_1(minimum_by) fplus_fwd_define_fn_1(minimum_by_maybe) fplus_fwd_define_fn_1(maximum_by) fplus_fwd_define_fn_1(maximum_by_maybe) fplus_fwd_define_fn_0(minimum) fplus_fwd_define_fn_0(minimum_maybe) fplus_fwd_define_fn_0(maximum) fplus_fwd_define_fn_0(maximum_maybe) fplus_fwd_define_fn_1(minimum_on) fplus_fwd_define_fn_1(minimum_on_maybe) fplus_fwd_define_fn_1(maximum_on) fplus_fwd_define_fn_1(maximum_on_maybe) fplus_fwd_define_fn_0(mean) fplus_fwd_define_fn_0(mean_obj_div_size_t) fplus_fwd_define_fn_0(mean_obj_div_double) fplus_fwd_define_fn_0(mean_using_doubles) fplus_fwd_define_fn_0(median) fplus_fwd_define_fn_1(all_unique_by_less) fplus_fwd_define_fn_0(all_unique_less) fplus_fwd_define_fn_1(is_infix_of) fplus_fwd_define_fn_1(is_subsequence_of) fplus_fwd_define_fn_1(count_if) fplus_fwd_define_fn_1(count) fplus_fwd_define_fn_1(is_unique_in_by) fplus_fwd_define_fn_1(is_unique_in) fplus_fwd_define_fn_1(is_permutation_of) fplus_fwd_define_fn_1(fill_pigeonholes_to) fplus_fwd_define_fn_0(fill_pigeonholes) fplus_fwd_define_fn_1(fill_pigeonholes_bool_to) fplus_fwd_define_fn_0(fill_pigeonholes_bool) fplus_fwd_define_fn_0(present_in_all) fplus_fwd_define_fn_1(elem_at_idx_or_nothing) fplus_fwd_define_fn_2(elem_at_idx_or_constant) fplus_fwd_define_fn_1(elem_at_idx_or_replicate) fplus_fwd_define_fn_1(elem_at_idx_or_wrap) fplus_fwd_define_fn_2(extrapolate_replicate) fplus_fwd_define_fn_2(extrapolate_wrap) fplus_fwd_define_fn_1(elem_at_float_idx) fplus_fwd_define_fn_0(pairs_to_map) fplus_fwd_define_fn_0(pairs_to_map_grouped) fplus_fwd_define_fn_0(map_to_pairs) fplus_fwd_define_fn_1(transform_map_values) fplus_fwd_define_fn_2(map_union_with) fplus_fwd_define_fn_1(map_union) fplus_fwd_define_fn_0(get_map_keys) fplus_fwd_define_fn_0(get_map_values) fplus_fwd_define_fn_0(swap_keys_and_values) fplus_fwd_define_fn_1(create_map) fplus_fwd_define_fn_1(create_map_with) fplus_fwd_define_fn_1(create_unordered_map) fplus_fwd_define_fn_1(create_unordered_map_with) fplus_fwd_define_fn_1(get_from_map) fplus_fwd_define_fn_1(get_from_map_unsafe) fplus_fwd_define_fn_2(get_from_map_with_def) fplus_fwd_define_fn_1(get_first_from_map) fplus_fwd_define_fn_1(get_first_from_map_unsafe) fplus_fwd_define_fn_2(get_first_from_map_with_def) fplus_fwd_define_fn_1(map_contains) fplus_fwd_define_fn_1(map_keep_if) fplus_fwd_define_fn_1(map_drop_if) fplus_fwd_define_fn_1(map_keep) fplus_fwd_define_fn_1(map_drop) fplus_fwd_define_fn_1(map_keep_if_value) fplus_fwd_define_fn_1(map_drop_if_value) fplus_fwd_define_fn_1(map_keep_values) fplus_fwd_define_fn_1(map_drop_values) fplus_fwd_define_fn_1(map_pluck) fplus_fwd_define_fn_1(choose) fplus_fwd_define_fn_2(choose_by) fplus_fwd_define_fn_1(choose_lazy) fplus_fwd_define_fn_2(choose_by_lazy) fplus_fwd_define_fn_1(choose_def) fplus_fwd_define_fn_2(choose_by_def) fplus_fwd_define_fn_1(choose_def_lazy) fplus_fwd_define_fn_2(choose_by_def_lazy) fplus_fwd_define_fn_1(group_by) fplus_fwd_define_fn_1(group_on) fplus_fwd_define_fn_1(group_on_labeled) fplus_fwd_define_fn_0(group) fplus_fwd_define_fn_1(group_globally_by) fplus_fwd_define_fn_1(group_globally_on) fplus_fwd_define_fn_1(group_globally_on_labeled) fplus_fwd_define_fn_0(group_globally) fplus_fwd_define_fn_1(cluster_by) fplus_fwd_define_fn_2(split_by) fplus_fwd_define_fn_1(split_by_keep_separators) fplus_fwd_define_fn_2(split) fplus_fwd_define_fn_2(split_one_of) fplus_fwd_define_fn_1(split_keep_separators) fplus_fwd_define_fn_1(split_at_idx) fplus_fwd_define_fn_2(insert_at_idx) fplus_fwd_define_fn_1(partition) fplus_fwd_define_fn_1(split_at_idxs) fplus_fwd_define_fn_1(split_every) fplus_fwd_define_fn_2(split_by_token) fplus_fwd_define_fn_1(run_length_encode_by) fplus_fwd_define_fn_0(run_length_encode) fplus_fwd_define_fn_0(run_length_decode) fplus_fwd_define_fn_1(span) fplus_fwd_define_fn_2(divvy) fplus_fwd_define_fn_1(aperture) fplus_fwd_define_fn_1(stride) fplus_fwd_define_fn_1(winsorize) fplus_fwd_define_fn_1(separate_on) fplus_fwd_define_fn_0(separate) fplus_fwd_define_fn_1(transform_with_idx) fplus_fwd_define_fn_1(transform_and_keep_justs) fplus_fwd_define_fn_1(transform_and_keep_oks) fplus_fwd_define_fn_1(transform_and_concat) fplus_fwd_define_fn_1(replicate_elems) fplus_fwd_define_fn_0(interleave) fplus_fwd_define_fn_0(transpose) fplus_fwd_define_fn_1(shuffle) fplus_fwd_define_fn_2(sample) fplus_fwd_define_fn_1(random_element) fplus_fwd_define_fn_2(random_elements) fplus_fwd_define_fn_1(apply_functions) fplus_fwd_define_fn_2(apply_function_n_times) fplus_fwd_define_fn_1(transform_parallelly) fplus_fwd_define_fn_2(transform_parallelly_n_threads) fplus_fwd_define_fn_2(reduce_parallelly) fplus_fwd_define_fn_3(reduce_parallelly_n_threads) fplus_fwd_define_fn_1(reduce_1_parallelly) fplus_fwd_define_fn_2(reduce_1_parallelly_n_threads) fplus_fwd_define_fn_1(keep_if_parallelly) fplus_fwd_define_fn_2(keep_if_parallelly_n_threads) fplus_fwd_define_fn_3(transform_reduce) fplus_fwd_define_fn_2(transform_reduce_1) fplus_fwd_define_fn_3(transform_reduce_parallelly) fplus_fwd_define_fn_4(transform_reduce_parallelly_n_threads) fplus_fwd_define_fn_2(transform_reduce_1_parallelly) fplus_fwd_define_fn_3(transform_reduce_1_parallelly_n_threads) fplus_fwd_define_fn_1(read_value_with_default) fplus_fwd_define_fn_2(replace_if) fplus_fwd_define_fn_2(replace_elem_at_idx) fplus_fwd_define_fn_2(replace_elems) fplus_fwd_define_fn_2(replace_tokens) fplus_fwd_define_fn_0(show) fplus_fwd_define_fn_3(show_cont_with_frame_and_newlines) fplus_fwd_define_fn_3(show_cont_with_frame) fplus_fwd_define_fn_1(show_cont_with) fplus_fwd_define_fn_0(show_cont) fplus_fwd_define_fn_0(show_maybe) fplus_fwd_define_fn_0(show_result) fplus_fwd_define_fn_2(show_float) fplus_fwd_define_fn_3(show_float_fill_left) fplus_fwd_define_fn_2(show_fill_left) fplus_fwd_define_fn_2(show_fill_right) fplus_fwd_define_fn_0(is_letter_or_digit) fplus_fwd_define_fn_0(is_whitespace) fplus_fwd_define_fn_0(is_line_break) fplus_fwd_define_fn_0(clean_newlines) fplus_fwd_define_fn_1(split_words) fplus_fwd_define_fn_1(split_lines) fplus_fwd_define_fn_0(trim_whitespace_left) fplus_fwd_define_fn_0(trim_whitespace_right) fplus_fwd_define_fn_0(trim_whitespace) fplus_fwd_define_fn_0(to_lower_case) fplus_fwd_define_fn_1(to_lower_case_loc) fplus_fwd_define_fn_0(to_upper_case) fplus_fwd_define_fn_1(to_upper_case_loc) fplus_fwd_define_fn_2(to_string_fill_left) fplus_fwd_define_fn_2(to_string_fill_right) fplus_fwd_define_fn_1(trees_from_sequence) fplus_fwd_define_fn_1(are_trees_equal) fplus_fwd_define_fn_0(tree_size) fplus_fwd_define_fn_0(tree_depth) fplus_fwd_define_fn_0(flatten_tree_depth_first) fplus_fwd_define_fn_0(flatten_tree_breadth_first) fplus_fwd_define_fn_0(show_timed) fplus_fwd_define_fn_0(make_timed_function) fplus_fwd_define_fn_0(make_timed_void_function) fplus_fwd_flip_define_fn_1(is_equal) fplus_fwd_flip_define_fn_1(is_not_equal) fplus_fwd_flip_define_fn_1(is_less) fplus_fwd_flip_define_fn_1(is_less_or_equal) fplus_fwd_flip_define_fn_1(is_greater) fplus_fwd_flip_define_fn_1(is_greater_or_equal) fplus_fwd_flip_define_fn_1(xor_bools) fplus_fwd_flip_define_fn_1(throw_on_nothing) fplus_fwd_flip_define_fn_1(as_just_if) fplus_fwd_flip_define_fn_1(lift_maybe) fplus_fwd_flip_define_fn_1(and_then_maybe) fplus_fwd_flip_define_fn_1(elem_at_idx) fplus_fwd_flip_define_fn_1(elem_at_idx_maybe) fplus_fwd_flip_define_fn_1(elems_at_idxs) fplus_fwd_flip_define_fn_1(transform) fplus_fwd_flip_define_fn_1(transform_convert) fplus_fwd_flip_define_fn_1(transform_inner) fplus_fwd_flip_define_fn_1(take) fplus_fwd_flip_define_fn_1(take_exact) fplus_fwd_flip_define_fn_1(take_cyclic) fplus_fwd_flip_define_fn_1(drop) fplus_fwd_flip_define_fn_1(take_last) fplus_fwd_flip_define_fn_1(drop_last) fplus_fwd_flip_define_fn_1(drop_exact) fplus_fwd_flip_define_fn_1(take_while) fplus_fwd_flip_define_fn_1(drop_while) fplus_fwd_flip_define_fn_1(fold_left_1) fplus_fwd_flip_define_fn_1(reduce_1) fplus_fwd_flip_define_fn_1(fold_right_1) fplus_fwd_flip_define_fn_1(scan_left_1) fplus_fwd_flip_define_fn_1(scan_right_1) fplus_fwd_flip_define_fn_1(append_elem) fplus_fwd_flip_define_fn_1(prepend_elem) fplus_fwd_flip_define_fn_1(append) fplus_fwd_flip_define_fn_1(append_convert) fplus_fwd_flip_define_fn_1(interweave) fplus_fwd_flip_define_fn_1(sort_by) fplus_fwd_flip_define_fn_1(sort_on) fplus_fwd_flip_define_fn_1(stable_sort_by) fplus_fwd_flip_define_fn_1(stable_sort_on) fplus_fwd_flip_define_fn_1(partial_sort) fplus_fwd_flip_define_fn_1(nth_element) fplus_fwd_flip_define_fn_1(unique_by) fplus_fwd_flip_define_fn_1(unique_on) fplus_fwd_flip_define_fn_1(intersperse) fplus_fwd_flip_define_fn_1(join) fplus_fwd_flip_define_fn_1(join_elem) fplus_fwd_flip_define_fn_1(is_elem_of_by) fplus_fwd_flip_define_fn_1(is_elem_of) fplus_fwd_flip_define_fn_1(nub_by) fplus_fwd_flip_define_fn_1(nub_on) fplus_fwd_flip_define_fn_1(all_unique_by_eq) fplus_fwd_flip_define_fn_1(all_unique_on) fplus_fwd_flip_define_fn_1(is_strictly_sorted_by) fplus_fwd_flip_define_fn_1(is_strictly_sorted_on) fplus_fwd_flip_define_fn_1(is_sorted_by) fplus_fwd_flip_define_fn_1(is_sorted_on) fplus_fwd_flip_define_fn_1(is_prefix_of) fplus_fwd_flip_define_fn_1(is_suffix_of) fplus_fwd_flip_define_fn_1(all_by) fplus_fwd_flip_define_fn_1(all_the_same_by) fplus_fwd_flip_define_fn_1(all_the_same_on) fplus_fwd_flip_define_fn_1(numbers) fplus_fwd_flip_define_fn_1(count_occurrences_by) fplus_fwd_flip_define_fn_1(lexicographical_less) fplus_fwd_flip_define_fn_1(replicate) fplus_fwd_flip_define_fn_1(ok_with_default) fplus_fwd_flip_define_fn_1(from_maybe) fplus_fwd_flip_define_fn_1(throw_on_error) fplus_fwd_flip_define_fn_1(lift_result) fplus_fwd_flip_define_fn_1(and_then_result) fplus_fwd_flip_define_fn_1(keep_if) fplus_fwd_flip_define_fn_1(drop_if) fplus_fwd_flip_define_fn_1(without) fplus_fwd_flip_define_fn_1(without_any) fplus_fwd_flip_define_fn_1(keep_if_with_idx) fplus_fwd_flip_define_fn_1(drop_if_with_idx) fplus_fwd_flip_define_fn_1(keep_by_idx) fplus_fwd_flip_define_fn_1(drop_by_idx) fplus_fwd_flip_define_fn_1(keep_idxs) fplus_fwd_flip_define_fn_1(drop_idxs) fplus_fwd_flip_define_fn_1(drop_idx) fplus_fwd_flip_define_fn_1(trim_left) fplus_fwd_flip_define_fn_1(trim_token_left) fplus_fwd_flip_define_fn_1(trim_right_by) fplus_fwd_flip_define_fn_1(trim_right) fplus_fwd_flip_define_fn_1(trim_token_right) fplus_fwd_flip_define_fn_1(trim_by) fplus_fwd_flip_define_fn_1(trim) fplus_fwd_flip_define_fn_1(trim_token) fplus_fwd_flip_define_fn_1(adjacent_keep_snd_if) fplus_fwd_flip_define_fn_1(adjacent_drop_fst_if) fplus_fwd_flip_define_fn_1(adjacent_drop_snd_if) fplus_fwd_flip_define_fn_1(adjacent_keep_fst_if) fplus_fwd_flip_define_fn_1(apply_to_pair) fplus_fwd_flip_define_fn_1(zip) fplus_fwd_flip_define_fn_1(transform_fst) fplus_fwd_flip_define_fn_1(transform_snd) fplus_fwd_flip_define_fn_1(abs_diff) fplus_fwd_flip_define_fn_1(floor_to_int_mult) fplus_fwd_flip_define_fn_1(ceil_to_int_mult) fplus_fwd_flip_define_fn_1(int_power) fplus_fwd_flip_define_fn_1(min_2) fplus_fwd_flip_define_fn_1(max_2) fplus_fwd_flip_define_fn_1(histogram_using_intervals) fplus_fwd_flip_define_fn_1(modulo_chain) fplus_fwd_flip_define_fn_1(generate_by_idx) fplus_fwd_flip_define_fn_1(repeat) fplus_fwd_flip_define_fn_1(infixes) fplus_fwd_flip_define_fn_1(carthesian_product) fplus_fwd_flip_define_fn_1(carthesian_product_n) fplus_fwd_flip_define_fn_1(permutations) fplus_fwd_flip_define_fn_1(combinations) fplus_fwd_flip_define_fn_1(combinations_with_replacement) fplus_fwd_flip_define_fn_1(iterate_maybe) fplus_fwd_flip_define_fn_1(adjacent_difference_by) fplus_fwd_flip_define_fn_1(find_first_by) fplus_fwd_flip_define_fn_1(find_last_by) fplus_fwd_flip_define_fn_1(find_first_idx_by) fplus_fwd_flip_define_fn_1(find_last_idx_by) fplus_fwd_flip_define_fn_1(find_first_idx) fplus_fwd_flip_define_fn_1(find_last_idx) fplus_fwd_flip_define_fn_1(find_all_idxs_by) fplus_fwd_flip_define_fn_1(find_all_idxs_of) fplus_fwd_flip_define_fn_1(find_all_instances_of_token) fplus_fwd_flip_define_fn_1(find_all_instances_of_token_non_overlapping) fplus_fwd_flip_define_fn_1(find_first_instance_of_token) fplus_fwd_flip_define_fn_1(set_includes) fplus_fwd_flip_define_fn_1(unordered_set_includes) fplus_fwd_flip_define_fn_1(set_merge) fplus_fwd_flip_define_fn_1(unordered_set_merge) fplus_fwd_flip_define_fn_1(set_intersection) fplus_fwd_flip_define_fn_1(unordered_set_intersection) fplus_fwd_flip_define_fn_1(set_is_disjoint) fplus_fwd_flip_define_fn_1(unordered_set_is_disjoint) fplus_fwd_flip_define_fn_1(set_difference) fplus_fwd_flip_define_fn_1(unordered_set_difference) fplus_fwd_flip_define_fn_1(set_symmetric_difference) fplus_fwd_flip_define_fn_1(unordered_set_symmetric_difference) fplus_fwd_flip_define_fn_1(any_by) fplus_fwd_flip_define_fn_1(none_by) fplus_fwd_flip_define_fn_1(minimum_idx_by) fplus_fwd_flip_define_fn_1(minimum_idx_by_maybe) fplus_fwd_flip_define_fn_1(maximum_idx_by) fplus_fwd_flip_define_fn_1(maximum_idx_by_maybe) fplus_fwd_flip_define_fn_1(minimum_idx_on) fplus_fwd_flip_define_fn_1(minimum_idx_on_maybe) fplus_fwd_flip_define_fn_1(maximum_idx_on) fplus_fwd_flip_define_fn_1(maximum_idx_on_maybe) fplus_fwd_flip_define_fn_1(minimum_by) fplus_fwd_flip_define_fn_1(minimum_by_maybe) fplus_fwd_flip_define_fn_1(maximum_by) fplus_fwd_flip_define_fn_1(maximum_by_maybe) fplus_fwd_flip_define_fn_1(minimum_on) fplus_fwd_flip_define_fn_1(minimum_on_maybe) fplus_fwd_flip_define_fn_1(maximum_on) fplus_fwd_flip_define_fn_1(maximum_on_maybe) fplus_fwd_flip_define_fn_1(all_unique_by_less) fplus_fwd_flip_define_fn_1(is_infix_of) fplus_fwd_flip_define_fn_1(is_subsequence_of) fplus_fwd_flip_define_fn_1(count_if) fplus_fwd_flip_define_fn_1(count) fplus_fwd_flip_define_fn_1(is_unique_in_by) fplus_fwd_flip_define_fn_1(is_unique_in) fplus_fwd_flip_define_fn_1(is_permutation_of) fplus_fwd_flip_define_fn_1(fill_pigeonholes_to) fplus_fwd_flip_define_fn_1(fill_pigeonholes_bool_to) fplus_fwd_flip_define_fn_1(elem_at_idx_or_nothing) fplus_fwd_flip_define_fn_1(elem_at_idx_or_replicate) fplus_fwd_flip_define_fn_1(elem_at_idx_or_wrap) fplus_fwd_flip_define_fn_1(elem_at_float_idx) fplus_fwd_flip_define_fn_1(transform_map_values) fplus_fwd_flip_define_fn_1(map_union) fplus_fwd_flip_define_fn_1(create_map) fplus_fwd_flip_define_fn_1(create_map_with) fplus_fwd_flip_define_fn_1(create_unordered_map) fplus_fwd_flip_define_fn_1(create_unordered_map_with) fplus_fwd_flip_define_fn_1(get_from_map) fplus_fwd_flip_define_fn_1(get_from_map_unsafe) fplus_fwd_flip_define_fn_1(get_first_from_map) fplus_fwd_flip_define_fn_1(get_first_from_map_unsafe) fplus_fwd_flip_define_fn_1(map_contains) fplus_fwd_flip_define_fn_1(map_keep_if) fplus_fwd_flip_define_fn_1(map_drop_if) fplus_fwd_flip_define_fn_1(map_keep) fplus_fwd_flip_define_fn_1(map_drop) fplus_fwd_flip_define_fn_1(map_keep_if_value) fplus_fwd_flip_define_fn_1(map_drop_if_value) fplus_fwd_flip_define_fn_1(map_keep_values) fplus_fwd_flip_define_fn_1(map_drop_values) fplus_fwd_flip_define_fn_1(map_pluck) fplus_fwd_flip_define_fn_1(choose) fplus_fwd_flip_define_fn_1(choose_lazy) fplus_fwd_flip_define_fn_1(choose_def) fplus_fwd_flip_define_fn_1(choose_def_lazy) fplus_fwd_flip_define_fn_1(group_by) fplus_fwd_flip_define_fn_1(group_on) fplus_fwd_flip_define_fn_1(group_on_labeled) fplus_fwd_flip_define_fn_1(group_globally_by) fplus_fwd_flip_define_fn_1(group_globally_on) fplus_fwd_flip_define_fn_1(group_globally_on_labeled) fplus_fwd_flip_define_fn_1(cluster_by) fplus_fwd_flip_define_fn_1(split_by_keep_separators) fplus_fwd_flip_define_fn_1(split_keep_separators) fplus_fwd_flip_define_fn_1(split_at_idx) fplus_fwd_flip_define_fn_1(partition) fplus_fwd_flip_define_fn_1(split_at_idxs) fplus_fwd_flip_define_fn_1(split_every) fplus_fwd_flip_define_fn_1(run_length_encode_by) fplus_fwd_flip_define_fn_1(span) fplus_fwd_flip_define_fn_1(aperture) fplus_fwd_flip_define_fn_1(stride) fplus_fwd_flip_define_fn_1(winsorize) fplus_fwd_flip_define_fn_1(separate_on) fplus_fwd_flip_define_fn_1(transform_with_idx) fplus_fwd_flip_define_fn_1(transform_and_keep_justs) fplus_fwd_flip_define_fn_1(transform_and_keep_oks) fplus_fwd_flip_define_fn_1(transform_and_concat) fplus_fwd_flip_define_fn_1(replicate_elems) fplus_fwd_flip_define_fn_1(shuffle) fplus_fwd_flip_define_fn_1(random_element) fplus_fwd_flip_define_fn_1(apply_functions) fplus_fwd_flip_define_fn_1(transform_parallelly) fplus_fwd_flip_define_fn_1(reduce_1_parallelly) fplus_fwd_flip_define_fn_1(keep_if_parallelly) fplus_fwd_flip_define_fn_1(read_value_with_default) fplus_fwd_flip_define_fn_1(show_cont_with) fplus_fwd_flip_define_fn_1(split_words) fplus_fwd_flip_define_fn_1(split_lines) fplus_fwd_flip_define_fn_1(to_lower_case_loc) fplus_fwd_flip_define_fn_1(to_upper_case_loc) fplus_fwd_flip_define_fn_1(trees_from_sequence) fplus_fwd_flip_define_fn_1(are_trees_equal) } // namespace fwd } // namespace fplus libfplus-0.2.13/include_all_in_one/make_all_in_one.py000066400000000000000000000032121376322245400226730ustar00rootroot00000000000000#!/usr/bin/env python import os from sys import version_info THIS_DIR = os.path.dirname(os.path.realpath(__file__)) FPLUS_INCLUDE_DIR = os.path.realpath(THIS_DIR + "/../include/fplus") def is_fplus_include_line(code_line): return ( code_line.startswith("#include ", "")[:-1] ) ALLREADY_INCLUDED_FILES = [] def parse_one_file(filename): if filename in ALLREADY_INCLUDED_FILES: return "" ALLREADY_INCLUDED_FILES.append(filename) parsed_result = "\n//\n" + "// " + filename + "\n//\n\n" if version_info[0] >= 3: f = open(FPLUS_INCLUDE_DIR + "/" + filename, "r", encoding='utf-8', errors='ignore') else: f = open(FPLUS_INCLUDE_DIR + "/" + filename, "r") lines = f.readlines() for code_line in lines: if is_fplus_include_line(code_line): new_file = extract_fplus_include_file(code_line) parsed_result = parsed_result + parse_one_file(new_file) else: if not "#pragma once" in code_line: parsed_result = parsed_result + code_line f.close() return parsed_result print("-- Generating include_all_in_one") content = "#pragma once\n" + parse_one_file("fplus.hpp") if version_info[0] >= 3: f = open(THIS_DIR + "/include/fplus/fplus.hpp", "w", encoding='utf-8', errors='ignore') else: f = open(THIS_DIR + "/include/fplus/fplus.hpp", "w") f.write(content) f.close() libfplus-0.2.13/logo/000077500000000000000000000000001376322245400143655ustar00rootroot00000000000000libfplus-0.2.13/logo/fplus.png000066400000000000000000000272011376322245400162260ustar00rootroot00000000000000PNG  IHDR@]bKGD pHYs  tIME6tEXtCommentCreated with GIMPW IDATxyxT'd&;ى@( "Ah@TWz)-m}۫EyK*ť lQ,EH@BHX63fL ̙33'r1̜,y=( B|LBB B B!$B!J$U@<"9'AAJU*OZ / "8€ !$B( 0_"""""ENhiZZ 7"bb 4D,(+Z "īLD<(bױ0 !6˗˗EʼnHIX!$B|*Zvab!É8( (/w@]=݄P1Fcǀ3gDl&B(H/څl(BA"44Dc08rhh(BA"ǘ)JPQV])JPQ(55Q c,( %B(HY !$B|JKSXPQ55JKi%BA"D;wPQvS BA"XPQ._DPRB!$B,@PQBPQY PQ*+i%BA"D;"EvO06ޝVbfh 9>}4  !$0E45^47>8X@@E wtf)>\$B(Hb28sRvgӉ?DHE0rEr߻Z[  ( =_DqqRS{~uu/B(H\# >^r#BA"oZ@Q(UVV+$BA"/ RjPS{D(H "#k%BA"ďHIQyF#vD! *Jy( #)IEA"$Bh{rS"pN ODe[u5 WJL)TU{C(HZWkf"6VE gASmG(HjH块 n;BA"$%B+P3bb8 gT Py hhێP+궣D(H@@vP1AAw^Fzn;B(H(@eDV 7 QyLyd JyfێP"@@plPWC(H "EH&H'eZqEUUUGss3Z[[a6aX`\ڵkyǻ1z?._V}jwl)DQD~~>p1 vdAX"# tⲒ{CLu5(H$7V| v;AA_x'VtۑO֬YC1"dQ[Z_${޽{y#QV' LF[O'H}Zێ ٳMMM}D1MEB/HcZGnB I]]XD1XbzbێP$s):Qȵ&>ܹsFFFbƌ9r$ht|rN_K&$ ć__|kD6Z[E44 z=`6P8I}I7}tJ#$466J*EdE􉌶#$HuI/$Ruz{#B(H.bX$t[DV*+G IjJ*'%a2ێPQ_I H |(8uN8gϢ---0LPtGRRӑ FÊII;BCCqETTT@ףͰX,0 FFZVEtt4h$%%!>>^BAR}vt72 0 ɓ'}vT*L03f5\ÊHcVUAvAp8ީ9---JKKN"%%))):t(222j8AEv~_/5xO|J7FA@vv6Ν`9>-ҥ΂+{νk"&Lcǎ᫯BQQWI`` QFaԨQH` ZHijر~G#عs'h" <$}5*1h=TWW?ɓ'|_(,,Daa!e5xm۶ᣏ>r[:RSS^z Ǐg;D8pK.B ɋ:t|&Mbe%RuJJI796,"66h4PT00hnnF}}=pene" I9kifd ?GvvU%[nǚ5kܲrGAk{A*))߁GqޛZZFHHڶ Gő#GPVVFJh!y[z)9m'NX0LWXXFuD*YDhЅHKS餤$ RN Iu{2=U`` &L^xx9sH.#}H46]vR0| TSRR\ru:_+ /]">ɓ}seK"Ugm'U6mڄ/9 !OffUaR/ ܮ_:(I߆ l2r ,KuXX*F)T+)==ݭ73^{5.GA"r#2Rq+ΖNw=x`⋘;w.t:ί W换 9ikk Khrumf֬Yx71w\:z(oNDV}=+5jP{bP[6:fŠ+xbL<^=u֡yHK R:=Hy8 !(/w_F@VWL̄(8wN:"{T0f3vzbDeY}K$p]͆tQKLh" djٹAsrr21c G,..FQQ$رc.   ^n2ܞD݉jh0a)u2d]x*VPJHH-Ǒ|CPRRѥ /8{l*--TiY$ ht[|mY,&]tAׂTsۂt%q7l6زe $ﳸ'0ʒܴYtIٓ3L&&";ZKt]55>m(@J'bٲen3 wEQU5 $T3zT4K -< P+^ : >m6[vrٳ1aIz=;?.;w^L4ɭ}۷Or!C(V.;?A*XJ$$$wB+Vgy>}:8r9_fmx*SB0Vzm ǏT6&&+QvQQQʝ8qB1NHLPYypHuytBCbZg=5\# mfG'P?Oe7n(ljjWO8ӧ%̠IF<{W먝%yy_*ml4?e SPP{\СC$W9P!u5[ՊM6I('"հ0iV^UyTTNAQu…"}vKqf94R-b9 ݷZXX%%%_"x xM%ݶm.͚FEIHٹ3VM ׿VD;s+/G^^ǖXnė0v ~lƛo|SLq`͚5n>|r 8ZV$Hٌ˗c;vSe fh'&Fzҹ N/O?ݯZZZܷܬ{6Ç pqA!##>|[jI$!QZf vڅl!:: OݻQ\\g{A0rH|47`ʕ+#--  *[8ԭOqm!++ 111jhllDyy9pAL|ʽt ϟyf 11 Bbb"pAVCRfl6BG,Dx#RP2Yc qzMF,H9sgΜq{NziLLt0JsYi96l .0PJ%lVV4mgP^^rMecHo<2n8/u!"< ::g5Rd\-E6t:222PSە ӽ~\V'zF=LS(Z 9^!:]k[򞡛n)HD.Kw} }<[BPxǯXpr1{l 6Lǎ<m]7٥pp̹QkJ#Gb֬YD(HDNߣjXhD`` ~ ))I ;{׏1h ̞}o6m_W>%~ߎcbSOaСް0Gff,3D33DBB2^ Zf7* ^XJuۍ3= E$+^{F%mt%IIIկ~%9' O?ݻwcÆ n1`שּׁI٬ز寰ٺNs1Axbb"~_;EcPr$mmbt:͛kBA"21k,$''G0sLu]ֻ ;;7t[ٳؽ_?$૯VkT &G$@ݸ…oSk4X,KXٸ;IP7G|ؾ}K/[om=wh4:u*N: hllhlz4S bРX;q6FM @NNnwu3QPUuy\jaaa˜1c0zh3"?r1RWQmDłGɓ8{,zC#TD)GE+å%".^,FY B Ʉt:c6lƎiVeBCEpzuuu/ j <<QQQGRRRRRhPBA 0D<(-V+;'ETU)?^NG ރQvDWU^(w RWW{DB(HO"˔ReUX)DGC)HDFYG= +! {*}BA"~'VOMRۮmPc6Qy"<mTt !e(.{Aupe MMt S*+xV"={e涣D(HOijܬ<(2jCV5q$BA"~R yEF h#$B|?:Q@}=vDAKDy0޵8IP!JMmhtqqBA"&R }>Sd2 b Pj"ո8xeD#$үD*@SfێPH?lQ[DDFjSd6mG(HRY lU"H'*蝵kײ< +J~55v]@Lh!RFGj2KV"@g%$ҏ`f$YBA"(5F#b圏V+ ,LyQm55{HyHՓJEn;n;BA"%3fZPAD]+*JAێPHH` L(HDfAp'HUުRϏRt (5j|<=S,U(`XbSH`}\,X`4JJyOF(x93D I0x . _?>cwbbcv [rVvį0~?GFHHǏw{\Պ\ )) !!!… qAÙkp%[cҥBHHBBBKBM~r1,]SLAbb"4 3gO>-ރp|nnn+uR\t 0zh#((QQQ3~p80cÊqgvJ+LXz5zhZb8tS'Ng?c]>KK,M7݄@R!..ӦMÚ5k`d;_Aw͛7Cdee@f% IDAT]o*~_U6<<GСC;}_YY{yyy=[wsLoe{̘1]>p@lٲ媆&^:]ˮ#G_ϹFcӉ?^9rӦM@J1cȑ;>m əLE (xpj5<3xqXСC6m{=ޞ'ظq#4{7xb_U>COꫯO ;So}/r766^lƬYz#_܌n***fY֯_ MDMߋ_ݰa׻9C-ZԫŅ TEGdoE*˗/c=bF9s ,jł p?1bmۆF ۷gϾj۴4,YCmm-L&***n: 4[nR?9_7ng?[DFFbӦMhjjٳg1k,o;vt_v->ܹs~z >}oFWիW9cʕt.]+W"88PZZիWR/x}v NO<q; ǢE v#. ɛHs|~뭷PUUٌz>|+Vرce9V۰N竡0vPٳ#55jÆ ի1gw|[[~ Cpp0&MWm[XX_~&Mrq}>pqDL6 fӳ _~%0am«׿;VTTMtmԩkDٛoٱ/|/[nEz (7tPQE&ewi+wsͩ}wMRw!Cvme?\,**m6ϐZ۷z?W벫r OmnΝ]c8v駲\sKK@ x~@7n\gϞ-W\)CCd+*rI={Vқ'FGG;D%%%W 50`@'S]jw֭[QTTL-44MMM(-qPO?9s 66 #Faܸ.Ceedkg˖-./ƠA~W_ $$qgi+Wݥ+Ȁn˗cN[{/'P__|ؕbxhjj©S0b#!!列3jb -(D+ 6h4bڴi>>;tMjhh/^Td=t<Ǯ:~q[w\߾ُwu9>\-W3o:R9rW~Eu)466b@Euھ`ۜx|S yaٲeEQQv}n_Ϝ9s@U-6{wsŎЌ1Blٲi&̟?C AHȏ)S`8>cL0k֬#oWAc=g}ƅ |rFXX ;z7|ɓ'cDKK ꫯСC7aOϧӝ1?cHQ̙33fL>_|/gϞi>+b…Fw358[/'Nɓo⩧U@Ν+/~1&+:^;BBBuVL4Ig/>;u."nAA*,, 7܀Vٞ=x׻t{uK9t&Ju9hr RѡCVQ]]-_RSSFdd$qu?17a^^>#̜9 PTj>|8ӸʕHXDy~Ǎ֩q+9YDr22R"SNŮ]vZ̟?_1'X=)F)@Q{%P&gΜݻhdv E!w"U+J%6Vy"(J<EsQDf<$.Pj>I:aj,]TqH F[=;;뾴*+Qʒ,/d,O (:R^/2JzY봶 hn_h!IY0.NE+PWn:t:F u8A2d0hldqXB!! ! !BA"BA"B+\V$E6IENDB`libfplus-0.2.13/logo/fplus.xcf000066400000000000000000000627421376322245400162330ustar00rootroot00000000000000gimp xcf fileBBG gimp-commentCreated with GIMPgimp-image-grid(style solid) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) $7^ad0lFunctionalPlus C++ library     gimp-text-layer(markup "FunctionalPlus C++ library") (font "Monospace") (font-size 18.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.000000 0.000000 0.000000)) (justify left) (box-mode dynamic) (box-width 515.000000) (box-height 199.000000) (box-unit pixels) (hinting yes) _ll < @@@B 4 / /77 b,XԖ7 y"  J !d8&k Q%  !      "N   Fh6#`  $}i -f]ٜ;k@@@<<<ӄb,/\--y"   T rmmrJ !ԁD' {|Q% "#}  * "#  {|' ^rmmrdO    --+r ӄI@@@@֣E ]  > 1  11 = ]k3Q%أF  W# #"N ?  FۑK r ) )$}99-f)w@@@Vޠ<5I552 +r3s:B   2p  6    q     (5  p  F   p   :0'm  ;N!"ޡ<@@@K   $ $$$ f6+  Y5 $#E ;    ABZ    BA5 (#") S3 h4T/a+TRI5tyׄ;V}E ; ! E lV ZZm q ' c^  Aa", Y 6 B '\6Q *4 '^&$%%+%G1[-fplus::     5zgimp-text-layer^(markup "fplus::") (font "Monospace Bold") (font-size 18.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.000000 0.000000 0.000000)) (justify left) (box-mode fixed) (box-width 515.000000) (box-height 199.000000) (box-unit pixels) (hinting yes) $$$+rug%i .!#9#######$ V1f &J%M##"`! !n!  Z s1* . x/ 4/ / / 1 1 1 $+ 1 1 1 1 1 1 1 1 1 1                s 2 2 2 2 2 2 2 2 2 2 AὒP /r -> j0oEU=Gg%~%  h' $a^ h[  C9  d Z ]  9 4  ^Z B g  mn GH ()   @//////////1 1 1 1 1 1 1 1 1 1 1 "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "  <S7. J " Y  v  i  7    g  5 z  =    M        |   l +  U   %  P  ˭oP&.R*r&'((!!!!!!!ЈO.  %=`!2!w X 8888I6z)3 ܚ[/ݝ]2 ڕE.",*t( @++++++++++++++++++ @@@@Y                             999999999999999999999    (( GG mn    h]Z D  7 2   ` V _ A7  eY  f&"_a% %f <m6:y&F  Rװ=!7777777777777777777 "  "  "  "  "  "       +  Q  y    ]  * 8  Qb q1, b  J  g2]gb i< \ p;< T  /  x x E 2r    :  e 3 W 4 6 <s \ 4όH |4Gզ~\='.      &   _: Xѡj.<\x'&&n%$%&a(_=+Z y-~ / /8     Y   \   :` J{ e"#T%ũ[&++++++++++++++++++ @@@6YrrrrKa%0λ~     lgimp-text-layerP(markup "λ") (font "Monospace Bold") (font-size 18.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.000000 0.000000 0.000000)) (justify left) (box-mode dynamic) (box-unit pixels) (hinting yes) &b&~^^&.55'<DDMWW;\Y]G]m~}~~|{|x z~}~wz!~~|z'~zz)~yz+~z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,`~ ~|~~(y|~~,|~~.u~ 0}~ 1}~ 2} 3 3p~4|5 5{66|6U~7~7|8~8}8z9~9}9q~:~:~:v~;;~;q~;<|;f<~;ym~}~~|{|x z~}~wz!~~|z'~zz)~yz+~z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,`~ ~|~~(y|~~,|~~.u~ 0}~ 1}~ 2} 3 3p~4|5 5{66|6U~7~7|8~8}8z9~9}9q~:~:~:v~;;~;q~;<|;f<~;y !&)+,,,,,,,,,,,,,,,,,,,,,,,,(,. 0 1 2 3 345566677888999:::;;;;;;<; 􋇁zsldQ;& ڳg@!ј^ &/)t+,,,,,,,,,,,,,,,,,,,,,,,,L LNSXdt(E,(. 0g 1W 2m 3 34O55966K67|7 88k899c9::Z: ;;R;;;I;<;%@~}<|:~p8~|7~`5s4 |3 ~}2 ~}1 ~{0 z/|.v-~--|+q*~*})y(~(~{'~'}&@%~%U$~$%~#~#y"#~f!~!"{ ~ ~ |~ z~{   } ~!!{""@"~"~#f#~#~@~}<|:~p8~|7~`5s4 |3 ~}2 ~}1 ~{0 z/|.v-~--|+q*~*})y(~(~{'~'}&@%~%U$~$%~#~#y"#~f!~!"{ ~ ~ |~ z~{   } ~!!{""@"~"~#f#~#~@<:87543 2 1 0 / .-,,+**)(('&&%%$$###""!!!      !!!"""##@0<":8A754$3 :2 :1 50 1/ . -,,C+**])((3'&Y&%`%$f$#J##)"" !Z!! v  (-3 8  !=!!"B""#H#@@@@<~:@~:|;:t:}:~9}98t~8}8~7|76@~6|6~5t5~5 4{4~3t~3~ 3 2x 2~ 1@ 1~ 1~ 0y 0} 0~ /} /~ .f~ .}.-x-~,U~,|,+y~+}+*{*)f)})~(x('U~'|'~&y~&~&%{%~$f$~$<~:@~:|;:t:}:~9}98t~8}8~7|76@~6|6~5t5~5 4{4~3t~3~ 3 2x 2~ 1@ 1~ 1~ 0y 0} 0~ /} /~ .f~ .}.-x-~,U~,|,+y~+}+*{*)f)})~(x('U~'|'~&y~&~&%{%~$f$~$<::;:::99888776665554433 3 2 2 1 1 1 0 0 0 / / . ..--,,,+++**)))(('''&&&%%$$$<::K;::s:9898 8_87$766K655r54743 3^ 3 2# 2 1 1J 1 0 0q 0 /6 / . .].-#-,,I,++q+*5*) )])(#(''I'&&p&%5%$ $\$#~m$|%%q%~%~%~j&}'&~m'}((q(~(~(~y)}*)~z*~*~+{+~+~,x,}--y-~-~.}.~//z /~ /~ 0{ 1 1 1} 1~ 2U 2| 3 2~@ 3~ 3~ 3~U4~4~~#`5|{${~"~f ~$~ }z#~ ~x~#v ~#} ~|$ ~%p }}#} v|$ %y ~~#~#~m$|%%q%~%~%~j&}'&~m'}((q(~(~(~y)}*)~z*~*~+{+~+~,x,}--y-~-~.}.~//z /~ /~ 0{ 1 1 1} 1~ 2U 2| 3 2~@ 3~ 3~ 3~U4~4~~#`5|{${~"~f ~$~ }z#~ ~x~#v ~#} ~|$ ~%p }}#} v|$ %y ~~#~#$$$%%%&&&'''((()))***++,,,---.../ / / 0 0 0 1 1 1 2 2 2 3 3 344"$#" # # " # # " # # " ##$M$$%R%% &W&& ']''(b(()g))*l**+r+,!,w,-%-|-.*..// / / 05 0 0 1: 1 1 2? 2 2 3D 3 34J4"#O3#3"  #T k-# "  ~#Y @&# " yw#_  # " Np#d@@@@#|#~"U~"~"!s~!}!~ } ~q~}x~U|!s~} "{!#q~!}"~"x#%U~"~|$~#}y$~#}%z{$~#~q~"~U~#~%||#~~#~U~#`~$%zy#~}#}~#m}$%}q$}#}%t|$~#~U$U|#~$~xy#~}#~~"~mz$%zq#~}#} #|#~"U~"~"!s~!}!~ } ~q~}x~U|!s~} "{!#q~!}"~"x#%U~"~|$~#}y$~#}%z{$~#~q~"~U~#~%||#~~#~U~#`~$%zy#~}#}~#m}$%}q$}#}%t|$~#~U$U|#~$~xy#~}#~~"~mz$%zq#~}#} ##"""!!!    !!!"""#"##"#"##"#""#"##"#"##"#""#"#""#"##"# #"#""H"!!o! 4 [!Go  3!!![""!"#"G##<"n#u"3##J"Z#"  "#X"F##-"m#e" 2##:"Y#s" "#H"E#""m#V"1##+"Y#c @v~"~ ~~#z |~#}~ z$~ ~"~v~q }#~ q#~z %w~ ~#~} m~"~p~#|~#}}t$~#y}|#~~z`#~)~~&~@f&~~%|~$~U~#~~#q{##z"~{ }!w~} ~y~x ~ ~ v!!}!z""~"y#~#~#w$~$}$t%%~%`~&~&~&U~'~'|'@((})~)~ @v~"~ ~~#z |~#}~ z$~ ~"~v~q }#~ q#~z %w~ ~#~} m~"~p~#|~#}}t$~#y}|#~~z`#~)~~&~@f&~~%|~$~U~#~~#q{##z"~{ }!w~} ~y~x ~ ~ v!!}!z""~"y#~#~#w$~$}$t%%~%`~&~&~&U~'~'|'@((})~)~ " "# " " # " " # "##"##"'&%$$#""!     !!!"""###$$$%%%&&&'''((() " "$j#i "\ " c#n "1 " \#ti "#"V#y> "#'wO#~"'LH&%$"B$#Z";"!/ 5 g.'x!  r !!k!""d"##^#$$W$ %%P%&&J&''C'((<()}~z~~{~}~~U|~f~~m~~q|t~~~m} ~x }  ~s ~ ~ u }  v ~ ~ x ~ ~ | }  y }~z~~{~}~~U|~f~~m~~q|t~~~m} ~x }  ~s ~ ~ u }  v ~ ~ x ~ ~ | }  y              ,17<AFLQ V [ a   f   k   p  v $ {  ) ~"~vx~#%}%|#}~"~y ~"~vx~#%}%|#}~"~y ""#"#" " "#8"E#q" )}**~*z+~+)}**~*z+~+)***++)6***/++ ~ } ~  } ~ } ~  }       .   4Nc'1Layer     _E_eaaa__```!`1`A`Q`a`q````````aaa!a1aAaQaaaqaa            ii44} Background      bx}bd dd$}bbc cc,c<cLc\clc|cccccccc@@@@@@@@]_./Selection Mask ddeeeeNeReVeZe^ebefejenereveze~eeeeeeeeeeeeeee   ii44libfplus-0.2.13/test/000077500000000000000000000000001376322245400144045ustar00rootroot00000000000000libfplus-0.2.13/test/CMakeLists.txt000066400000000000000000000105731376322245400171520ustar00rootroot00000000000000if (FPLUS_UNITTEST_USE_CONAN) # if using conan for the build, first install doctest via conan: # > cd build/ # > conan install .. -obuild_unittest=True # > cmake .. -DFPLUS_BUILD_UNITTEST=ON -DFPLUS_UNITTEST_USE_CONAN=ON include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup() else() find_package(doctest CONFIG REQUIRED) endif() set(link_libraries fplus ${CMAKE_THREAD_LIBS_INIT}) if (NOT FPLUS_UNITTEST_USE_CONAN) list(APPEND link_libraries doctest::doctest) endif() function(add_test_suite name) add_executable(${name} ${name}.cpp) add_test(NAME ${name} COMMAND ${name}) target_compile_options(${name} PRIVATE ${COMPILE_OPTIONS}) target_link_libraries(${name} PRIVATE ${link_libraries}) endfunction() add_test_suite(show_versions) add_test_suite(benchmark_session_test) add_test_suite(compare_test) add_test_suite(composition_test) add_test_suite(container_common_test) add_test_suite(container_properties_test) add_test_suite(container_traits_test) add_test_suite(curry_test) add_test_suite(extrapolate_test) add_test_suite(filter_test) add_test_suite(function_traits_test) add_test_suite(fwd_test) add_test_suite(generate_test) add_test_suite(interpolate_test) add_test_suite(invoke_test) add_test_suite(maps_test) add_test_suite(maybe_test) add_test_suite(numeric_test) add_test_suite(optimize_test) add_test_suite(pairs_test) add_test_suite(queue_test) add_test_suite(raii_test) add_test_suite(read_test) add_test_suite(readme_examples_test) add_test_suite(result_test) add_test_suite(replace_test) add_test_suite(search_test) add_test_suite(sets_test) add_test_suite(shared_ref_test) add_test_suite(show_test) add_test_suite(side_effects_test) add_test_suite(split_test) add_test_suite(stopwatch_test) add_test_suite(stringtools_test) add_test_suite(stringtools_utf8_test) add_test_suite(stringtools_cp1251_test) add_test_suite(stringtools_cp1253_test) add_test_suite(transform_test) add_test_suite(timed_test) add_test_suite(tree_test) add_test_suite(udemy_course_test) add_test_suite(variant_test) if(NOT MSVC) target_compile_options(stringtools_cp1251_test PRIVATE -Wno-invalid-source-encoding ) target_compile_options(stringtools_cp1253_test PRIVATE -Wno-invalid-source-encoding ) target_compile_options(stringtools_utf8_test PRIVATE -Wno-invalid-source-encoding ) endif() add_custom_target(unittest show_versions COMMAND benchmark_session_test COMMAND compare_test COMMAND composition_test COMMAND container_common_test COMMAND container_properties_test COMMAND container_traits_test COMMAND curry_test COMMAND extrapolate_test COMMAND filter_test COMMAND function_traits_test COMMAND fwd_test COMMAND generate_test COMMAND interpolate_test COMMAND invoke_test COMMAND maps_test COMMAND maybe_test COMMAND numeric_test COMMAND optimize_test COMMAND pairs_test COMMAND queue_test COMMAND raii_test COMMAND read_test COMMAND readme_examples_test COMMAND result_test COMMAND replace_test COMMAND search_test COMMAND sets_test COMMAND shared_ref_test COMMAND show_test COMMAND side_effects_test COMMAND split_test COMMAND stopwatch_test COMMAND stringtools_test COMMAND stringtools_cp1251_test COMMAND stringtools_cp1253_test COMMAND stringtools_utf8_test COMMAND timed_test COMMAND transform_test COMMAND tree_test COMMAND udemy_course_test COMMAND variant_test COMMENT "Running unittests\n\n" VERBATIM ) libfplus-0.2.13/test/benchmark_session_test.cpp000066400000000000000000000146531376322245400216550ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include #include // This is an example on how to use benchmark_session in order to bench separate parts of an algorithm // We need to instantiate a session into which the stats will be collected fplus::benchmark_session my_benchmark_session; // antic C style qsort (will be benchmarked against std::sort) void qsort_vec_int(std::vector & v) { auto cmp = [](const void * a, const void * b) { return ( *(static_cast(a)) - *(static_cast(b)) ); }; qsort (v.data(), v.size(), sizeof(int), cmp); } // Benchmarked function : several sub parts of this function are benchmarked separately void benchmark_example() { using Ints = std::vector; // Example 1 : benchmark by replacing a function // // We want to benchmark the following code : // Ints ascending_numbers = fplus::numbers(0, 1000); // // So, first we make an alternate version of the function "fplus::numbers" // Since fplus::numbers is a template function, we need to specify // that the actual version we want to benchmark // is "fplus::numbers>" // // numbers_bench will our alternate version and it // has the same signature as fplus::numbers>, // except that it also stores stats into the benchmark session, // under the name "numbers" // // Note that make_benchmark_function *will add side effects* to the function // (since it stores data into the benchmark session at each call) auto numbers_bench = make_benchmark_function( my_benchmark_session, "numbers", fplus::numbers> ); // Then, we replace the original code "Ints ascending_numbers = fplus::numbers(0, 1000);" // by a code that uses the benchmarked function Ints ascending_numbers = numbers_bench(0, 100000); // Example 2: benchmark by replacing an expression // Below, we will benchmark an expression // The original expression we want to benchmark was: // Ints shuffled_numbers = fplus::shuffle(std::mt19937::default_seed, ascending_numbers); // // In order to do so, we just copy/paste this expression // into "bench_expression" like shown below. // This expression will then be benchmarked with the name "shuffle" // // Notes : // - benchmark_expression is a preprocessor macro that uses an immediately invoked lambda (IIL) // - the expression can be paster as-is, and it is possible to not remove the ";" // (although it also works if it is not present) Ints shuffled_numbers = benchmark_expression( my_benchmark_session, "shuffle", fplus::shuffle(std::mt19937::default_seed, ascending_numbers); ); // Example 3: also benchmark by replacing an expression // The original expression was // const auto sorted_numbers = fplus::sort(shuffled_numbers); const auto sorted_numbers = benchmark_expression( my_benchmark_session, "sort_shuffled_sequence", fplus::sort(shuffled_numbers); ); // Verify that the sort has worked assert(sorted_numbers == ascending_numbers); // In this toy example, we will compare the performance // of sorting a shuffled sequence versus sorting a reversed sequence Ints descending_numbers = fplus::reverse(ascending_numbers); // this call is not benchmarked // here we benchmark the call to fplus::sort(descending_numbers) const auto sorted_numbers2 = benchmark_expression( my_benchmark_session, "sort_reverse_sequence", fplus::sort(descending_numbers); ); // Verify that the sort has worked assert(sorted_numbers2 == ascending_numbers); // benchmark qsort benchmark_void_expression(my_benchmark_session, "qsort_reverse_sequence", qsort_vec_int(descending_numbers) ); } TEST_CASE("benchmark_example") { // Example 4 : benchmark by replacing a function // We also want to benchmark the "benchmark_example" in its entirety auto benchmark_example_bench = make_benchmark_void_function( my_benchmark_session, "benchmark_example", benchmark_example); // For the sake of this test, we will run the benchmarked function several times fplus::execute_n_times(10, [&]() { benchmark_example_bench(); }); // A call to : // // std::cout << fplus::show(my_benchmark_session.report()); // // Would output something like // // Function |Nb calls|Total time|Av. time |Deviation | // ----------------------+--------+----------+-----------+----------+ // benchmark_example | 10| 136.393ms|13639.255ns|2209.289ns| // sort_shuffled_sequence| 10| 57.006ms| 5700.557ns| 855.817ns| // shuffle | 10| 49.040ms| 4903.998ns| 785.540ns| // qsort_reverse_sequence| 10| 24.777ms| 2477.678ns| 343.918ns| // sort_reverse_sequence | 10| 2.308ms| 230.782ns| 87.104ns| // numbers | 10| 2.000ms| 199.965ns| 103.334ns| //////////// Unit tests assertions below //////////////////////////// // test report_list() { const auto reports = my_benchmark_session.report_list(); REQUIRE_EQ(reports.size(), 6); const auto & one_report = reports.at("benchmark_example"); REQUIRE_EQ(one_report.nb_calls, 10); REQUIRE(one_report.average_time == doctest::Approx( one_report.total_time / static_cast(one_report.nb_calls))); } // test report() { const auto & report = my_benchmark_session.report(); const auto & lines = fplus::split_lines(false, report); REQUIRE_EQ(lines.size(), 8); const auto & lines_sizes = fplus::transform([](const std::string & s) { return s.size(); }, lines ); REQUIRE( fplus::all_the_same(lines_sizes) ); const auto & check_nb_columns = fplus::transform([](const std::string & s) { return (fplus::count('|', s) + fplus::count('+', s) ) == 5; }, lines ); REQUIRE(fplus::all(check_nb_columns)); } } libfplus-0.2.13/test/compare_test.cpp000066400000000000000000000137041376322245400176020ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { bool int_less(int x, int y) { return x < y; } bool int_less_eq(int x, int y) { return x <= y; } auto generic_less = [](auto x, auto y) { return x < y; }; auto generic_less_eq = [](auto x, auto y) { return x <= y; }; auto squareGeneric = [](auto x) { return x * x; }; } TEST_CASE("compare_test, is_equal_to") { using namespace fplus; REQUIRE(is_equal_to(2)(2)); REQUIRE_FALSE(is_equal_to(2)(3)); } TEST_CASE("compare_test, is_not_equal_to") { using namespace fplus; REQUIRE_FALSE(is_not_equal_to(2)(2)); REQUIRE(is_not_equal_to(2)(3)); } TEST_CASE("compare_test, is_less") { using namespace fplus; REQUIRE_FALSE(is_less(2, 2)); REQUIRE(is_less(2, 3)); REQUIRE_FALSE(is_less(3, 2)); REQUIRE(is_less_than(3)(2)); } TEST_CASE("compare_test, is_less_or_equal") { using namespace fplus; REQUIRE(is_less_or_equal(2, 2)); REQUIRE(is_less_or_equal(2, 3)); REQUIRE_FALSE(is_less_or_equal(3, 2)); REQUIRE(is_less_or_equal_than(3)(2)); REQUIRE(is_less_or_equal_by_and_by(squareGeneric, squareGeneric)(2, 2)); REQUIRE(is_less_or_equal_by_than(squareGeneric, 5)(2)); } TEST_CASE("compare_test, is_less_by") { using namespace fplus; auto square = [](int x) { return x * x; }; REQUIRE(is_less_by_and_by(squareGeneric, square)(2, -3)); REQUIRE(is_less_by(squareGeneric)(2, -3)); } TEST_CASE("compare_test, is_less_by_than") { using namespace fplus; auto square = [](int x) { return x * x; }; REQUIRE(is_less_by_than(square, 5)(2)); REQUIRE(is_less_by_than(squareGeneric, 5)(2)); } TEST_CASE("compare_test, is_greater") { using namespace fplus; REQUIRE_FALSE(is_greater(2, 2)); REQUIRE_FALSE(is_greater(2, 3)); REQUIRE(is_greater(3, 2)); REQUIRE_FALSE(is_greater_than(3)(2)); REQUIRE(is_greater_by_and_by(squareGeneric, squareGeneric)(3, -2)); } TEST_CASE("compare_test, is_greater_or_equal") { using namespace fplus; REQUIRE(is_greater_or_equal(2, 2)); REQUIRE_FALSE(is_greater_or_equal(2, 3)); REQUIRE(is_greater_or_equal(3, 2)); REQUIRE_FALSE(is_greater_or_equal_than(3)(2)); REQUIRE(is_greater_or_equal_by_and_by(squareGeneric, squareGeneric)(3, -3)); REQUIRE(is_greater_or_equal_by(squareGeneric)(3, -3)); REQUIRE(is_greater_or_equal_by_than(squareGeneric, 3)(-3)); } TEST_CASE("compare_test, is_equal_by") { using namespace fplus; auto square = [](int x) { return x * x; }; REQUIRE(is_equal_by_and_by(square, square)(2, -2)); REQUIRE(is_equal_by_and_by(squareGeneric, square)(2, -2)); REQUIRE(is_equal_by(square)(2, -2)); REQUIRE(is_not_equal_by_and_by(square, squareGeneric)(2, 3)); REQUIRE(is_equal_by(squareGeneric)(2, -2)); REQUIRE(is_not_equal_by(square)(2, 3)); REQUIRE(is_not_equal_by(squareGeneric)(2, 3)); REQUIRE(is_equal_by_to(squareGeneric, 4)(2)); REQUIRE(is_not_equal_by_to(squareGeneric, 5)(2)); } TEST_CASE("compare_test, always") { using namespace fplus; REQUIRE_EQ(identity(2), 2); REQUIRE_EQ(always(2)(5), 2); REQUIRE_EQ(always_arg_1_of_2(2, 5), 2); REQUIRE_EQ(always_arg_2_of_2(2, 5), 5); } TEST_CASE("compare_test, xor_bools") { using namespace fplus; REQUIRE(xor_bools(false, false) == false); REQUIRE(xor_bools(true, false) == true); REQUIRE(xor_bools(false, true) == true); REQUIRE(xor_bools(true, true) == false); } TEST_CASE("compare_test, ord_to_eq") { using namespace fplus; REQUIRE(ord_to_eq(int_less)(1, 2) == false); REQUIRE(ord_to_eq(int_less)(2, 2) == true); REQUIRE(ord_to_eq(int_less)(2, 1) == false); REQUIRE(ord_to_eq(generic_less)(2, 1) == false); } TEST_CASE("compare_test, ord_to_not_eq") { using namespace fplus; REQUIRE(ord_to_not_eq(int_less)(1, 2) == true); REQUIRE(ord_to_not_eq(int_less)(2, 2) == false); REQUIRE(ord_to_not_eq(int_less)(2, 1) == true); REQUIRE(ord_to_not_eq(generic_less)(2, 1) == true); } TEST_CASE("compare_test, ord_eq_to_eq") { using namespace fplus; REQUIRE(ord_eq_to_eq(int_less_eq)(1, 2) == false); REQUIRE(ord_eq_to_eq(int_less_eq)(2, 2) == true); REQUIRE(ord_eq_to_eq(int_less_eq)(2, 1) == false); REQUIRE(ord_eq_to_eq(generic_less_eq)(2, 1) == false); } TEST_CASE("compare_test, ord_eq_to_not_eq") { using namespace fplus; REQUIRE(ord_eq_to_not_eq(int_less_eq)(1, 2) == true); REQUIRE(ord_eq_to_not_eq(int_less_eq)(2, 2) == false); REQUIRE(ord_eq_to_not_eq(int_less_eq)(2, 1) == true); } TEST_CASE("compare_test, lexicographical_less") { using namespace fplus; REQUIRE(lexicographical_less(std::vector{0,1,2,2,4,5}, std::vector{0,1,2,3,4,5})); REQUIRE(lexicographical_less(std::vector{}, std::vector{1})); REQUIRE_FALSE(lexicographical_less(std::vector{1}, std::vector{})); REQUIRE(lexicographical_less(std::string("012245"), std::string("012345"))); REQUIRE(lexicographical_less(std::string("012245"), std::string("01234"))); REQUIRE(lexicographical_less(std::string("01234"), std::string("012345"))); REQUIRE_FALSE(lexicographical_less(std::string("012345"), std::string("012245"))); REQUIRE_FALSE(lexicographical_less(std::string("01234"), std::string("012245"))); REQUIRE_FALSE(lexicographical_less(std::string("012345"), std::string("01234"))); } TEST_CASE("compare_test, lexicographical_sort") { using namespace fplus; std::vector xs = {"012245", "012345", "01234"}; std::vector xs_lex_sorted = {"012245", "01234", "012345"}; REQUIRE_EQ(lexicographical_sort(xs), xs_lex_sorted); } libfplus-0.2.13/test/composition_test.cpp000066400000000000000000000214371376322245400205210ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { int APlusTwoTimesBFunc(int a, int b) { return a + 2 * b; } typedef std::deque IntDeq; typedef std::deque IntContCont; typedef IntDeq IntCont; typedef IntCont Row; std::uint64_t fibo(std::uint64_t n) { if (n < 2) return n; else return fibo(n-1) + fibo(n-2); } // version using continuation passing style (CPS) std::uint64_t fibo_cont(const std::function& cont, std::uint64_t n) { if (n < 2) return n; else return cont(n-1) + cont(n-2); } } class CompositionTestState { public: explicit CompositionTestState(int x) : x_(x) {} void Add(int y) { x_ += y; } int Get() const { return x_; } private: int x_; }; TEST_CASE("composition_test, forward_apply") { using namespace fplus; REQUIRE_EQ(forward_apply(3, square), 9); REQUIRE_EQ(forward_apply(3, [](auto x) { return x * x; }), 9); } TEST_CASE("composition_test, lazy") { using namespace fplus; const auto square_3_stub = lazy(square, 3); REQUIRE_EQ(square_3_stub(), 9); REQUIRE_EQ(lazy([](auto x) { return x * x; }, 3)(), 9); } TEST_CASE("composition_test, fixed") { using namespace fplus; const auto lazy_3 = fixed(3); REQUIRE_EQ(lazy_3(), 3); } TEST_CASE("composition_test, parameter_binding") { using namespace fplus; Row row = {1,2,3}; typedef IntContCont Mat; Mat mat; auto square = [](int x){ return x*x; }; auto squareRowElems = bind_1st_of_2(transform, square); auto add = [](auto x, auto y) { return x + y; }; auto add4 = bind_1st_of_2(add, 4); REQUIRE_EQ(add4(2), 6); Row squaredRow = squareRowElems(row); REQUIRE_EQ(squaredRow, IntCont({1,4,9})); auto int_division = [](int x, int y) { return x / y; }; REQUIRE_EQ(bind_2nd_of_2(int_division, 2)(6), 3); REQUIRE_EQ(bind_2nd_of_2([](auto x, auto y) { return x / y; }, 2)(6), 3); auto add3 = [](int x, int y, int z) { return x + y + z; }; auto genericAdd3 = [](auto x, auto y, auto z) { return x + y + z; }; REQUIRE_EQ(bind_1st_of_3(add3, 3)(30, 9), 42); REQUIRE_EQ(bind_1st_of_3(genericAdd3, 3)(30, 9), 42); REQUIRE_EQ(bind_1st_and_2nd_of_3(add3, 3, 5)(7), 15); REQUIRE_EQ(bind_1st_and_2nd_of_3(genericAdd3, 3, 5)(7), 15); REQUIRE_EQ(bind_2nd_and_3rd_of_3(add3, 3, 5)(7), 15); REQUIRE_EQ(bind_2nd_and_3rd_of_3(genericAdd3, 3, 5)(7), 15); } TEST_CASE("composition_test, compose") { using namespace fplus; auto square = [](int x){ return x*x; }; REQUIRE_EQ((compose(square, square)(2)), 16); REQUIRE_EQ((compose(square, square, square)(2)), 256); REQUIRE_EQ((compose(square, square, square, square)(2)), 65536); REQUIRE_EQ((compose(square, square, square, square, square)(1)), 1); REQUIRE_EQ((compose(std::multiplies<>{}, square)(4, 2)), 64); } TEST_CASE("composition_test, flip") { using namespace fplus; auto APlusTwoTimesB = [](int a, int b) { return a + 2 * b; }; auto TwoTimesAPlusB = [](int a, int b) { return 2 * a + b; }; auto Minus = [](auto a, auto b, auto c) { return a - b - c; }; REQUIRE_EQ((flip(APlusTwoTimesB)(2, 1)), 5); REQUIRE_EQ((flip(TwoTimesAPlusB)(1, 2)), 5); REQUIRE_EQ((flip(Minus)(1, 2, 3)), 0); } TEST_CASE("composition_test, logical") { using namespace fplus; auto is1 = [](int x) { return x == 1; }; auto is2 = [](auto x) { return x == 2; }; REQUIRE_FALSE((logical_not(is1)(1))); REQUIRE((logical_not(is1)(2))); REQUIRE((logical_not(std::equal_to<>{})(2, 3))); REQUIRE((logical_or(is1, is2)(1))); REQUIRE((logical_or(is1, is2)(2))); REQUIRE_FALSE((logical_and(is1, is2)(1))); REQUIRE((logical_and([](auto x){ return x == 1; }, is1)(1))); REQUIRE_FALSE((logical_xor(is1, is1)(1))); REQUIRE((logical_xor(is2, is1)(1))); REQUIRE_FALSE((logical_xor(is2, is2)(1))); } TEST_CASE("composition_test, apply_to_pair") { using namespace fplus; auto APlusTwoTimesB = [](int a, int b) { return a + 2 * b; }; auto APlusTwoTimesBGenericLambda = [](auto a, auto b) { return a + 2 * b; }; REQUIRE_EQ((apply_to_pair(APlusTwoTimesB, std::make_pair(1, 2))), 5); REQUIRE_EQ((apply_to_pair(APlusTwoTimesBGenericLambda, std::make_pair(1, 2))), 5); REQUIRE_EQ((apply_to_pair(APlusTwoTimesBFunc, std::make_pair(1, 2))), 5); } TEST_CASE("composition_test, state") { using namespace fplus; CompositionTestState state(1); REQUIRE_EQ(state.Get(), 1); auto stateAdd = std::mem_fn(&CompositionTestState::Add); stateAdd(state, 2); REQUIRE_EQ(state.Get(), 3); //auto stateAddBoundFPP = Bind1of2(stateAdd, &state); // crashes VC2015 compiler //stateAddBoundFPP(3); auto stateAddBoundStl = std::bind(&CompositionTestState::Add, std::placeholders::_1, std::placeholders::_2); stateAddBoundStl(state, 3); REQUIRE_EQ(state.Get(), 6); } TEST_CASE("composition_test, memoize") { using namespace fplus; auto f = memoize(square); REQUIRE_EQ(f(2), 4); REQUIRE_EQ(f(2), 4); REQUIRE_EQ(f(3), 9); REQUIRE_EQ(f(3), 9); std::size_t g_call_cnt = 0; auto g = memoize([&g_call_cnt](int x) { ++g_call_cnt; return x * x; }); REQUIRE_EQ(g(2), 4); REQUIRE_EQ(g(2), 4); REQUIRE_EQ(g(3), 9); REQUIRE_EQ(g(3), 9); REQUIRE_EQ(g_call_cnt, 2); const auto add = [](int x, int y) { return x + y; }; auto add_memo = memoize_binary(add); REQUIRE_EQ(add_memo(2, 3), 5); REQUIRE_EQ(add_memo(2, 3), 5); REQUIRE_EQ(add_memo(1, 2), 3); REQUIRE_EQ(add_memo(1, 2), 3); const auto fibo_memo = memoize_recursive(fibo_cont); for (std::uint64_t n = 0; n < 10; ++n) { REQUIRE_EQ(fibo_memo(n), fibo(n)); } } TEST_CASE("composition_test, constructor_as_function") { using namespace fplus; struct foo { foo(int a, int b) : a_(a), b_(2*b) {} int a_; int b_; }; const auto create_foo = constructor_as_function; const auto my_foo = create_foo(1,2); REQUIRE_EQ(my_foo.a_, 1); REQUIRE_EQ(my_foo.b_, 4); struct two_ctors { two_ctors(int, int) : val_(1) {} two_ctors(std::initializer_list) : val_(2) {} int val_; }; const two_ctors one_or_two = constructor_as_function(3, 4); REQUIRE_EQ(one_or_two.val_, 1); } TEST_CASE("composition_test, constructor_as_function") { const std::vector xs = {1,2,3,4,5}; const auto ys = fplus::keep_if( fplus::compose( fplus::square, fplus::is_greater_or_equal_than(3)), xs); REQUIRE_EQ(ys.size(), 4); } TEST_CASE("composition_test, get_mem") { struct foo { int bar_; }; const std::vector foos = {{1},{2},{3}}; const auto bars = fplus::transform(fplus_get_mem(bar_), foos); REQUIRE_EQ(bars, std::vector({1,2,3})); REQUIRE(fplus::all_unique_on(fplus_get_c_mem_t(foo, bar_, int), foos)); } TEST_CASE("composition_test, get_ptr_mem") { struct foo { foo(int bar) : bar_(bar) {} int bar_; }; const std::vector> foo_ptrs = { std::make_shared(1), std::make_shared(2), std::make_shared(3)}; const auto bars = fplus::transform(fplus_get_ptr_mem(bar_), foo_ptrs); REQUIRE_EQ(bars, std::vector({1,2,3})); REQUIRE(fplus::all_unique_on( fplus_get_c_ptr_mem_t(std::shared_ptr, bar_, int), foo_ptrs)); } TEST_CASE("composition_test, mem_func") { struct foo { int bar_; int bar() const { return bar_; } }; const std::vector foos = {{1},{2},{3}}; const auto bars = fplus::transform(fplus_mem_fn(bar), foos); REQUIRE_EQ(bars, std::vector({1,2,3})); REQUIRE(fplus::all_unique_on(fplus_c_mem_fn_t(foo, bar, int), foos)); } TEST_CASE("composition_test, ptr_mem_func") { struct foo { foo(int bar) : bar_(bar) {} int bar_; int bar() const { return bar_; } }; const std::vector> foo_ptrs = { std::make_shared(1), std::make_shared(2), std::make_shared(3)}; const auto bars = fplus::transform(fplus_ptr_mem_fn(bar), foo_ptrs); REQUIRE_EQ(bars, std::vector({1,2,3})); REQUIRE(fplus::all_unique_on( fplus_c_ptr_mem_fn_t(std::shared_ptr, bar, int), foo_ptrs)); }libfplus-0.2.13/test/container_common_test.cpp000066400000000000000000000726721376322245400215170ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include #include namespace { auto squareLambda = [](int x) -> int { return x*x; }; auto is_even_int = [](int x){ return x % 2 == 0; }; auto is_even_size_t = [](std::size_t x){ return x % 2 == 0; }; auto is_odd_int = [](int x){ return x % 2 == 1; }; typedef std::pair IntPair; typedef std::vector IntPairs; typedef std::vector IntVector; typedef std::vector IntVectors; typedef std::vector BoolVector; typedef std::vector IdxVector; typedef std::array IntArray5; typedef std::deque IntDeque; IntVector xs = {1,2,2,3,2}; IntArray5 xs_arr = {{1,2,2,3,2}}; IntDeque xs_deque = {1,2,2,3,2}; IntVector xs_reverse = {2,3,2,2,1}; IntVector xsSorted = {1,2,2,2,3}; IntVector xs2Times = {1,2,2,3,2,1,2,2,3,2}; typedef std::list IntList; typedef std::vector IntLists; IntLists intLists = { { 1 },{ 2, 2 },{ 3 },{ 2 } }; IntList intList = { 1,2,2,3,2 }; IntList intListSorted = { 1,2,2,2,3 }; auto int_mod_10 = [](int x) -> int { return x % 10; }; auto is2 = fplus::is_equal_to(2); auto is3 = fplus::is_equal_to(3); auto is4 = fplus::is_equal_to(4); auto abs_diff_less_or_equal_3 = [](int a, int b) { return fplus::abs(b - a) <= 3; }; typedef std::vector string_vec; IntVector vec0123({0,1,2,3}); IntList list0123({0,1,2,3}); std::string ABC_("ABC"); std::string XY("XY"); std::string ABCD("ABCD"); struct ExplicitFromIntStruct { explicit ExplicitFromIntStruct(int x) : x_(x) {} int x_; }; bool operator == (const ExplicitFromIntStruct &lhs, const ExplicitFromIntStruct & rhs) { return lhs.x_ == rhs.x_; } } TEST_CASE("container_common_test, group") { using namespace fplus; typedef std::vector>> LabeledGroups; REQUIRE_EQ(group(intList), intLists); REQUIRE_EQ(group(xs), IntVectors({IntVector({1}),IntVector({2,2}),IntVector({3}),IntVector({2})})); REQUIRE_EQ(group_on([](auto x) { return x % 10; }, IntVector({12,22,34})), IntVectors({IntVector({12,22}),IntVector({34})})); REQUIRE_EQ(group_on_labeled(int_mod_10, IntVector({12,22,34})), LabeledGroups({{2, IntVector({12,22})}, {4, IntVector({34})}})); REQUIRE_EQ(group_on_labeled(int_mod_10, IntVector({12,22,34})), LabeledGroups({{2, IntVector({12,22})}, {4, IntVector({34})}})); REQUIRE_EQ(group_globally(xs), IntVectors({IntVector({1}),IntVector({2,2,2}),IntVector({3})})); REQUIRE_EQ(group_globally_on(int_mod_10, IntVector({12,34,22})), IntVectors({IntVector({12,22}),IntVector({34})})); REQUIRE_EQ(group_globally_on_labeled(int_mod_10, IntVector({12,34,22})), LabeledGroups({{2, IntVector({12,22})}, {4, IntVector({34})}})); REQUIRE_EQ(group_by(abs_diff_less_or_equal_3, IntVector({2,3,6,4,22,21,8,5})), IntVectors({{2,3,6,4},{22,21},{8,5}})); } TEST_CASE("container_common_test, separate") { using namespace fplus; IntVector values = {1, 2, 2, 3, 3, 4, 4, 4}; REQUIRE_EQ(separate(values), IntVectors({IntVector({1, 2, 3, 4}),IntVector({2, 3, 4}),IntVector({4})})); REQUIRE_EQ(separate_on(int_mod_10, IntVector({12,22,34})), IntVectors({IntVector({12,34}),IntVector({22})})); } TEST_CASE("container_common_test, singleton_seq") { using namespace fplus; REQUIRE_EQ(singleton_seq(3), IntVector({3})); } TEST_CASE("container_common_test, filter") { using namespace fplus; REQUIRE_EQ(keep_if(is_even_int, xs), IntVector({2,2,2})); REQUIRE_EQ(drop_if(is_even_int, xs), IntVector({1,3})); REQUIRE_EQ(keep_if(is_even_int, intList), IntList({ 2,2,2 })); REQUIRE_EQ(drop_if(is_even_int, intList), IntList({ 1,3 })); REQUIRE_EQ(without(2, intList), IntList({ 1,3 })); REQUIRE_EQ(without_any(IntVector({2,3}), intList), IntList({ 1 })); auto sumis_even = [&](std::size_t x, int y) { return is_even_int(static_cast(x) + y); }; REQUIRE_EQ(keep_by_idx(is_even_size_t, xs), IntVector({ 1,2,2 })); REQUIRE_EQ(keep_if_with_idx(sumis_even, xs), IntVector({ 2,3,2 })); REQUIRE_EQ(drop_if_with_idx(sumis_even, xs), IntVector({ 1,2 })); } TEST_CASE("container_common_test, trim") { using namespace fplus; REQUIRE_EQ(trim_left(1, intList), IntList({2,2,3,2})); REQUIRE_EQ(trim_right(2, intList), IntList({1,2,2,3})); REQUIRE_EQ(trim(0, IntVector({0,2,4,5,6,7,8,0,0})), IntVector({2,4,5,6,7,8})); REQUIRE_EQ(trim_token_left(IntVector({1,2}), xs), IntVector({2,3,2})); REQUIRE_EQ(trim_token_left(IntVector({0,1,2}), IntVector({0,1,2,0,1,2,7,5,9})), IntVector({7,5,9})); REQUIRE_EQ(trim_token_right(IntVector({3,2}), xs), IntVector({1,2,2})); REQUIRE_EQ(trim_token(IntVector({0,1}), IntVector({0,1,7,8,9,0,1})), IntVector({7,8,9})); } TEST_CASE("container_common_test, cluster") { using namespace fplus; REQUIRE_EQ(cluster_by(abs_diff_less_or_equal_3, IntVector({2,3,6,4,12,11,20,23,8,4})), IntVectors({{2,3,6,4,12,11,8,4},{20,23}})); } TEST_CASE("container_common_test, run_length_encode") { using namespace fplus; typedef std::pair rle_pair_int; typedef std::vector rle_list_int; IntVector rle_input = {1,2,2,2,2,3,3,2}; rle_list_int rle_result = { std::make_pair(1, 1), std::make_pair(4, 2), std::make_pair(2, 3), std::make_pair(1, 2)}; REQUIRE_EQ(run_length_encode(rle_input), rle_result); REQUIRE_EQ(run_length_decode(rle_result), rle_input); } TEST_CASE("container_common_test, keep_idxs") { using namespace fplus; REQUIRE_EQ(keep_idxs(IdxVector({1, 3}), xs), IntVector({2,3})); REQUIRE_EQ(keep_idxs(IdxVector({3, 1}), xs), IntVector({2,3})); REQUIRE_EQ(keep_idxs(IdxVector({1, 1, 3}), xs), IntVector({2,3})); REQUIRE_EQ(keep_idxs(IdxVector({1, 3, 7}), xs), IntVector({2,3})); REQUIRE_EQ(drop_idxs(IdxVector({1, 3}), xs), IntVector({1,2,2})); } TEST_CASE("container_common_test, is_equal") { using namespace fplus; REQUIRE_EQ(is_equal_by_and_by(is_even_int, is_even_int)(2, 4), true); REQUIRE_EQ(is_equal_by_and_by(is_even_int, is_even_int)(1, 2), false); REQUIRE_EQ(is_equal_by_and_by(is_odd_int, is_even_int)(1, 2), true); REQUIRE_EQ(is_equal_to(2)(2), true); REQUIRE_EQ(is_equal_to(1)(2), false); } TEST_CASE("container_common_test, is_empty") { using namespace fplus; REQUIRE_EQ(is_empty(xs), false); REQUIRE_EQ(is_empty(IntVector()), true); REQUIRE_EQ(is_not_empty(xs), true); REQUIRE_EQ(is_not_empty(IntVector()), false); } TEST_CASE("container_common_test, convert") { using namespace fplus; REQUIRE_EQ(convert_container(xs), intList); typedef std::vector FloatVector; REQUIRE_EQ(convert_elems(xs), FloatVector({1.0f,2.0f,2.0f,3.0f,2.0f})); typedef std::vector ExplicitFromIntStructs; ExplicitFromIntStructs explicitFromIntStructs = { ExplicitFromIntStruct(1), ExplicitFromIntStruct(2), ExplicitFromIntStruct(2), ExplicitFromIntStruct(3), ExplicitFromIntStruct(2) }; REQUIRE_EQ(convert_elems(xs), explicitFromIntStructs); } TEST_CASE("container_common_test, append_elem") { using namespace fplus; IntVector values = {1,2}; REQUIRE_EQ(append_elem(3, values), IntVector({1,2,3})); REQUIRE_EQ(append_elem(3, IntVector({1,2})), IntVector({1,2,3})); } TEST_CASE("container_common_test, prepend_elem") { using namespace fplus; IntVector values = {2,3}; REQUIRE_EQ(prepend_elem(1, values), IntVector({1,2,3})); REQUIRE_EQ(prepend_elem(1, IntVector({2,3})), IntVector({1,2,3})); } TEST_CASE("container_common_test, append") { using namespace fplus; std::vector xs_empty; REQUIRE_EQ(append(xs, xs), xs2Times); REQUIRE_EQ(append(xs, xs_arr), xs2Times); REQUIRE_EQ(append(xs, xs_empty), xs); } TEST_CASE("container_common_test, append_convert") { using namespace fplus; REQUIRE_EQ(append_convert(xs_arr, xs_arr), xs2Times); REQUIRE_EQ(append_convert(xs_arr, xs_deque), xs2Times); } TEST_CASE("container_common_test, interweave") { using namespace fplus; REQUIRE_EQ(interweave(IntVector({1,3}), IntVector({2,4})), IntVector({1,2,3,4})); REQUIRE_EQ(interweave(IntVector({1,3,5,7}), IntVector({2,4})), IntVector({1,2,3,4,5,7})); REQUIRE_EQ(unweave(IntVector({0,1,2,3})), std::make_pair(IntVector({0,2}), IntVector({1,3}))); REQUIRE_EQ(unweave(IntVector({0,1,2,3,4})), std::make_pair(IntVector({0,2,4}), IntVector({1,3}))); } TEST_CASE("container_common_test, concat") { using namespace fplus; std::vector> emptyIntVecs; std::vector emptyIntVec; REQUIRE_EQ(concat(emptyIntVecs), emptyIntVec); REQUIRE_EQ(concat(intLists), intList); REQUIRE_EQ(concat(IntVectors(2, xs)), xs2Times); } TEST_CASE("container_common_test, repeat") { using namespace fplus; REQUIRE_EQ(repeat(2, xs), xs2Times); } TEST_CASE("container_common_test, replicate") { using namespace fplus; REQUIRE_EQ(replicate(2, xs), IntVectors({xs, xs})); } TEST_CASE("container_common_test, infixes") { using namespace fplus; REQUIRE_EQ(infixes(3, xs), IntVectors({ IntVector({1, 2, 2}), IntVector({2, 2, 3}), IntVector({2, 3, 2})})); } TEST_CASE("container_common_test, carthesian_product") { using namespace fplus; typedef std::pair char_pair; typedef std::vector char_pair_vec; auto twoCharsToString = [](std::string::value_type x, std::string::value_type y) { std::string result; result += x; result += y; return result; }; auto alwaysTrueCharAndChar = [](std::string::value_type, std::string::value_type) { return true; }; REQUIRE_EQ(carthesian_product_with(twoCharsToString, ABC_, XY), string_vec({"AX", "AY", "BX", "BY", "CX", "CY"})); REQUIRE_EQ(carthesian_product_where(alwaysTrueCharAndChar, ABC_, XY), char_pair_vec({{'A','X'}, {'A','Y'}, {'B','X'}, {'B','Y'}, {'C','X'}, {'C','Y'}})); auto charAndCharSumIsEven = [&](std::string::value_type x, std::string::value_type y) { return is_even_int(x + y); }; REQUIRE_EQ(carthesian_product_with_where(twoCharsToString, charAndCharSumIsEven, ABC_, XY), string_vec({"AY", "BX", "CY"})); REQUIRE_EQ(carthesian_product_where(charAndCharSumIsEven, ABC_, XY), char_pair_vec({{'A','Y'}, {'B','X'}, {'C','Y'}})); REQUIRE_EQ(carthesian_product(ABC_, XY), char_pair_vec({{'A','X'}, {'A','Y'}, {'B','X'}, {'B','Y'}, {'C','X'}, {'C','Y'}})); REQUIRE_EQ(carthesian_product_n(2, ABCD), string_vec({"AA", "AB", "AC", "AD", "BA", "BB", "BC", "BD", "CA", "CB", "CC", "CD", "DA", "DB", "DC", "DD"})); REQUIRE_EQ(carthesian_product_n(2, vec0123), IntVectors({{0,0}, {0,1}, {0,2}, {0,3}, {1,0}, {1,1}, {1,2}, {1,3}, {2,0}, {2,1}, {2,2}, {2,3}, {3,0}, {3,1}, {3,2}, {3,3}})); REQUIRE_EQ(carthesian_product_n(0, vec0123), IntVectors({IntVector()})); } TEST_CASE("container_common_test, combination") { using namespace fplus; typedef std::vector> intListVec; REQUIRE_EQ(combinations(2, ABCD), string_vec({"AB", "AC", "AD", "BC", "BD", "CD"})); REQUIRE_EQ(combinations(1, ABCD), string_vec({"A", "B", "C", "D"})); REQUIRE_EQ(combinations(3, ABCD), string_vec({"ABC", "ABD", "ACD", "BCD"})); REQUIRE_EQ(combinations(2, vec0123), IntVectors({{0,1}, {0,2}, {0,3}, {1,2}, {1,3}, {2,3}})); REQUIRE_EQ(combinations(2, list0123), intListVec({{0,1}, {0,2}, {0,3}, {1,2}, {1,3}, {2,3}})); REQUIRE_EQ(combinations(0, IntVector()), IntVectors({IntVector()})); REQUIRE_EQ(combinations(0, vec0123), IntVectors({IntVector()})); REQUIRE_EQ(combinations(0, ABCD), string_vec({""})); REQUIRE_EQ(combinations_with_replacement(2, ABCD), string_vec({"AA", "AB", "AC", "AD", "BB", "BC", "BD", "CC", "CD", "DD"})); REQUIRE_EQ(combinations_with_replacement(0, vec0123), IntVectors({IntVector()})); } TEST_CASE("container_common_test, permutations") { using namespace fplus; REQUIRE_EQ(permutations(2, ABCD), string_vec({"AB", "AC", "AD", "BA", "BC", "BD", "CA", "CB", "CD", "DA", "DB", "DC"})); REQUIRE_EQ(permutations(0, vec0123), IntVectors({IntVector()})); } TEST_CASE("container_common_test, power_set") { using namespace fplus; REQUIRE_EQ(power_set(std::string("xyz")), string_vec({"", "x", "y", "z", "xy", "xz", "yz", "xyz"})); } TEST_CASE("container_common_test, rotations") { using namespace fplus; REQUIRE_EQ(rotations_left(std::string("abcd")), string_vec({"abcd", "bcda", "cdab", "dabc"})); REQUIRE_EQ(rotations_right(std::string("abcd")), string_vec({"abcd", "dabc", "cdab", "bcda"})); } TEST_CASE("container_common_test, fill") { using namespace fplus; REQUIRE_EQ(fill_left(0, 6, IntVector({1,2,3,4})), IntVector({0,0,1,2,3,4})); REQUIRE_EQ(fill_right(0, 6, IntList({1,2,3,4})), IntList({1,2,3,4,0,0})); REQUIRE_EQ(fill_left(' ', 6, std::string("12")), std::string(" 12")); } TEST_CASE("container_common_test, intersperse") { using namespace fplus; REQUIRE_EQ(intersperse(0, xs), IntVector({1,0,2,0,2,0,3,0,2})); } TEST_CASE("container_common_test, fold") { using namespace fplus; REQUIRE_EQ(fold_left(std::plus<>(), 100, xs), 110); REQUIRE_EQ(fold_left_1(std::plus<>(), xs), 10); REQUIRE_EQ(fold_right(std::plus<>(), 100, xs), 110); REQUIRE_EQ(fold_right_1(std::plus<>(), xs), 10); auto appendXToStrForFoldL = [](const std::string& str, int x) { return str + std::to_string(x); }; auto appendXToStrForFoldR = [](int x, const std::string& str) { return str + std::to_string(x); }; std::string emptyString; REQUIRE_EQ(fold_left(appendXToStrForFoldL, emptyString, xs), "12232"); REQUIRE_EQ(fold_right(appendXToStrForFoldR, emptyString, xs), "23221"); } TEST_CASE("container_common_test, reduce") { using namespace fplus; REQUIRE_EQ(reduce(std::plus(), 100, xs), 110); REQUIRE_EQ(reduce_1(std::plus(), xs), 10); } TEST_CASE("container_common_test, scan") { using namespace fplus; REQUIRE_EQ(scan_left(std::plus<>(), 20, xs), IntVector({ 20,21,23,25,28,30 })); REQUIRE_EQ(scan_right(std::plus<>(), 20, xs), IntVector({ 30,29,27,25,22,20 })); REQUIRE_EQ(scan_left_1(std::plus<>(), xs), IntVector({ 1,3,5,8,10 })); REQUIRE_EQ(scan_right_1(std::plus<>(), xs), IntVector({ 10,9,7,5,2 })); } TEST_CASE("container_common_test, join") { using namespace fplus; REQUIRE_EQ(join(std::string(", "), std::vector({"a", "b", "sea"})), std::string("a, b, sea")); REQUIRE_EQ(join(IntList({0}), intLists), IntList({1,0,2,2,0,3,0,2})); } TEST_CASE("container_common_test, join_elem") { using namespace fplus; REQUIRE_EQ(join_elem(',', std::vector({"a", "b", "sea"})), std::string("a,b,sea")); REQUIRE_EQ(join_elem(0, intLists), IntList({1,0,2,2,0,3,0,2})); } TEST_CASE("container_common_test, all") { using namespace fplus; REQUIRE_EQ(all(BoolVector()), true); REQUIRE_EQ(all(BoolVector({true})), true); REQUIRE_EQ(all(BoolVector({false})), false); REQUIRE_EQ(all(BoolVector({true, true})), true); REQUIRE_EQ(all(BoolVector({true, false})), false); REQUIRE_EQ(all_by(is_even_int, IntVector()), true); REQUIRE_EQ(all_by(is_even_int, IntVector({2})), true); REQUIRE_EQ(all_by(is_even_int, IntVector({1})), false); REQUIRE_EQ(all_by(is_even_int, IntVector({2, 2})), true); REQUIRE_EQ(all_by(is_even_int, IntVector({2, 1})), false); } TEST_CASE("container_common_test, size") { using namespace fplus; REQUIRE_EQ(fplus::size_of_cont(xs), 5); REQUIRE_EQ(fplus::size_of_cont(IntVector()), 0); REQUIRE_EQ(is_not_empty(xs), true); } TEST_CASE("container_common_test, sort") { using namespace fplus; REQUIRE_EQ(sort(reverse(xs)), xsSorted); REQUIRE_EQ(sort(reverse(intList)), intListSorted); REQUIRE_EQ(sort_by(std::greater(), xs), reverse(xsSorted)); REQUIRE_EQ(sort_on(int_mod_10, IntVector({26,3,14})), IntVector({3,14,26})); REQUIRE_EQ(sort_on(size_of_cont, IntVectors({{1,2,3},{4,5}})), IntVectors({{4,5},{1,2,3}})); } TEST_CASE("container_common_test, stable_sort") { using namespace fplus; REQUIRE_EQ(stable_sort(reverse(xs)), xsSorted); REQUIRE_EQ(stable_sort(reverse(intList)), intListSorted); REQUIRE_EQ(stable_sort_by(std::greater(), xs), reverse(xsSorted)); REQUIRE_EQ(stable_sort_on(int_mod_10, IntVector({26,3,14})), IntVector({3,14,26})); REQUIRE_EQ(stable_sort_on(size_of_cont, IntVectors({{1,2,3},{4,5}})), IntVectors({{4,5},{1,2,3}})); } TEST_CASE("container_common_test, partial_sort") { using namespace fplus; REQUIRE_EQ(partial_sort(2, xs_reverse), IntVector({1,2})); REQUIRE_EQ(partial_sort(2, reverse(xs)), IntVector({1,2})); } TEST_CASE("container_common_test, reverse") { using namespace fplus; REQUIRE_EQ(reverse(IntVector({1,2,2,3,2})), xs_reverse); REQUIRE_EQ(reverse(xs), xs_reverse); } TEST_CASE("container_common_test, nth_element") { using namespace fplus; REQUIRE_EQ(nth_element(0, xs), 1); REQUIRE_EQ(nth_element(1, xs), 2); REQUIRE_EQ(nth_element(2, xs), 2); REQUIRE_EQ(nth_element(3, xs), 2); REQUIRE_EQ(nth_element(4, xs), 3); REQUIRE_EQ(nth_element(1, xs_arr), 2); } TEST_CASE("container_common_test, unique") { using namespace fplus; REQUIRE_EQ(unique(xs), IntVector({1,2,3,2})); auto IsEqualByis_even = [&](int a, int b) { return is_even_int(a) == is_even_int(b); }; REQUIRE_EQ(unique_by(IsEqualByis_even, xs), IntVector({1,2,3,2})); REQUIRE_EQ(unique_on(int_mod_10, IntVector({2,22,3})), IntVector({2, 3})); } TEST_CASE("container_common_test, all_the_same") { using namespace fplus; REQUIRE_EQ(all_the_same(IntVector()), true); REQUIRE_EQ(all_the_same(IntVector({1})), true); REQUIRE_EQ(all_the_same(IntVector({1,1,1})), true); REQUIRE_EQ(all_the_same(IntVector({1,2,1})), false); REQUIRE_EQ(all_the_same_on(int_mod_10, IntVector({3,13,33})), true); REQUIRE_EQ(all_the_same_on(int_mod_10, IntVector({3,14,33})), false); struct foo {}; const auto foo_to_int = [](const foo&) -> int { return 3; }; REQUIRE(fplus::all_the_same_on(foo_to_int, std::vector({}))); } TEST_CASE("container_common_test, all_unique") { using namespace fplus; REQUIRE_EQ(all_unique(IntVector()), true); REQUIRE_EQ(all_unique(IntVector({1})), true); REQUIRE_EQ(all_unique(IntVector({1,2,1})), false); REQUIRE_EQ(all_unique(IntVector({1,2,3})), true); REQUIRE_EQ(all_unique_less(IntVector()), true); REQUIRE_EQ(all_unique_less(IntVector({1})), true); REQUIRE_EQ(all_unique_less(IntVector({1,2,1})), false); REQUIRE_EQ(all_unique_less(IntVector({1,2,3})), true); REQUIRE_EQ(all_unique_on(int_mod_10, IntVector({3,14,35})), true); REQUIRE_EQ(all_unique_on(int_mod_10, IntVector({3,14,33})), false); } TEST_CASE("container_common_test, is_sorted") { using namespace fplus; REQUIRE_EQ(is_sorted(IntVector()), true); REQUIRE_EQ(is_sorted(IntVector({1})), true); REQUIRE_EQ(is_sorted(IntVector({1,2,3})), true); REQUIRE_EQ(is_sorted(IntVector({1,2,2})), true); REQUIRE_EQ(is_sorted(IntVector({1,2,1})), false); REQUIRE_EQ(is_sorted_on(int_mod_10, IntVector({51,32,43})), true); REQUIRE_EQ(is_sorted_on(int_mod_10, IntVector({15,23})), false); REQUIRE_EQ(is_strictly_sorted(IntVector({1,2,3})), true); REQUIRE_EQ(is_strictly_sorted(IntVector({1,2,2})), false); REQUIRE_EQ(is_strictly_sorted(IntVector({1,2,1})), false); REQUIRE_EQ(is_strictly_sorted_on(int_mod_10, IntVector({51,32,43})), true); REQUIRE_EQ(is_strictly_sorted_on(int_mod_10, IntVector({51,32,43,63})), false); REQUIRE_EQ(is_strictly_sorted_on(int_mod_10, IntVector({15,23})), false); } TEST_CASE("container_common_test, find") { using namespace fplus; REQUIRE_EQ(find_first_by(is3, xs), just(3)); REQUIRE_EQ(find_first_by(is4, xs), nothing()); REQUIRE_EQ(find_first_idx_by(is2, xs), just(1)); REQUIRE_EQ(find_first_idx_by(is4, xs), nothing()); REQUIRE_EQ(find_first_idx(2, xs), just(1)); REQUIRE_EQ(find_first_idx(4, xs), nothing()); REQUIRE_EQ(find_last_by(is3, xs), just(3)); REQUIRE_EQ(find_last_by(is4, xs), nothing()); REQUIRE_EQ(find_last_idx_by(is2, xs), just(4)); REQUIRE_EQ(find_last_idx_by(is4, xs), nothing()); REQUIRE_EQ(find_last_idx(2, xs), just(4)); REQUIRE_EQ(find_last_idx(4, xs), nothing()); } TEST_CASE("container_common_test, is_elem_of") { using namespace fplus; REQUIRE_EQ(is_elem_of(2, xs), true); REQUIRE_EQ(is_elem_of(4, xs), false); } TEST_CASE("container_common_test, elem_at_idx") { using namespace fplus; REQUIRE_EQ(elem_at_idx(2, xs), 2); } TEST_CASE("container_common_test, elem_at_idx_maybe") { using namespace fplus; REQUIRE_EQ(elem_at_idx_maybe(2, xs), maybe(2)); REQUIRE_EQ(elem_at_idx_maybe(9, xs), nothing()); } TEST_CASE("container_common_test, find_token") { using namespace fplus; REQUIRE_EQ(find_all_instances_of_token(std::string("Plus"), std::string("C Plus Plus is a nice language,") + std::string(" and FunctionalPlus makes it even nicer.")), std::vector({ 2, 7, 46 })); REQUIRE_EQ(find_all_instances_of_token(std::string("xx"), std::string("bxxxxc")), std::vector({ 1, 2, 3 })); REQUIRE_EQ(find_all_instances_of_token(std::string("xy"), std::string("xyaaa")), std::vector({ 0 })); REQUIRE_EQ(find_all_instances_of_token(std::string("xy"), std::string("aaaxy")), std::vector({ 3 })); REQUIRE_EQ(find_all_instances_of_token(std::string("xx"), std::string("xxxxx")), std::vector({ 0, 1, 2, 3 })); REQUIRE_EQ(find_all_instances_of_token_non_overlapping(std::string("xx"), std::string("xxxx")), std::vector({ 0, 2 })); REQUIRE_EQ(find_all_instances_of_token(IntVector({}), IntVector({})), IdxVector({0})); REQUIRE_EQ(find_all_instances_of_token(IntVector({}), IntVector({1})), IdxVector({0,1})); REQUIRE_EQ(find_all_instances_of_token_non_overlapping(IntVector({}), IntVector({})), IdxVector({0})); REQUIRE_EQ(find_all_instances_of_token_non_overlapping(IntVector({}), IntVector({1})), IdxVector({0,1})); REQUIRE_EQ(find_first_instance_of_token(IntVector({}), IntVector({})), just(0)); REQUIRE_EQ(find_first_instance_of_token(IntVector({}), IntVector({1,2})), just(0)); REQUIRE_EQ(find_first_instance_of_token(std::string("haha"), std::string("oh, hahaha!")), just(4)); } TEST_CASE("container_common_test, insert_at_idx") { using namespace fplus; REQUIRE_EQ(insert_at_idx(2, 0, IntVector({1,2,3,4})), IntVector({1,2,0,3,4})); } TEST_CASE("container_common_test, segment") { using namespace fplus; IntList v789 = { 7,8,9 }; REQUIRE_EQ(set_segment(1, v789, IntList({ 1,2,2,3,2 })), IntList({ 1,7,8,9,2 })); REQUIRE_EQ(set_segment(1, v789, intList), IntList({ 1,7,8,9,2 })); REQUIRE_EQ(get_segment(2, 5, IntList({ 0,1,2,3,4,5,6,7,8 })), IntList({ 2,3,4 })); REQUIRE_EQ(get_segment(2, 15, IntList({ 0,1,2,3,4,5,6,7,8 })), IntList({ 2,3,4,5,6,7,8 })); REQUIRE_EQ(get_segment(5, 2, IntList({ 0,1,2,3,4,5,6,7,8 })), IntList({})); REQUIRE_EQ(get_segment(12, 15, IntList({ 0,1,2,3,4,5,6,7,8 })), IntList({})); REQUIRE_EQ(get_segment(15, 12, IntList({ 0,1,2,3,4,5,6,7,8 })), IntList({})); REQUIRE_EQ(replace_elems(2, 5, xs), IntVector({1,5,5,3,5})); REQUIRE_EQ(replace_tokens(std::string("123"), std::string("_"), std::string("--123----123123")), std::string("--_----__")); REQUIRE_EQ(take(2, xs), IntVector({ 1,2 })); REQUIRE_EQ(take_last(2, xs), IntVector({ 3,2 })); REQUIRE_EQ(take_exact(2, xs), IntVector({ 1,2 })); REQUIRE_EQ(drop(2, xs), IntVector({ 2,3,2 })); REQUIRE_EQ(drop_last(2, xs), IntVector({ 1,2,2 })); REQUIRE_EQ(drop_exact(2, xs), IntVector({ 2,3,2 })); REQUIRE_EQ(take(999, xs), xs); REQUIRE_EQ(drop(999, xs), IntVector()); REQUIRE_EQ(take_while(is_odd_int, xs), IntVector({ 1 })); REQUIRE_EQ(take_while(always(true), xs), xs); REQUIRE_EQ(drop_while(always(false), xs), xs); REQUIRE_EQ(drop_while(is_odd_int, xs), IntVector({ 2,2,3,2 })); REQUIRE_EQ(span(is_odd_int, xs), std::make_pair(IntVector({ 1 }), IntVector({ 2,2,3,2 }))); REQUIRE_EQ(set_segment(2, IntVector({8,9}), xs), IntVector({1,2,8,9,2})); REQUIRE_EQ(set_segment(2, IntVector({8,9}), IntVector({1,2,2,3,2})), IntVector({1,2,8,9,2})); REQUIRE_EQ(remove_segment(1, 3, xs), IntVector({1,3,2})); REQUIRE_EQ(remove_segment(1, 3, IntVector({1,2,2,3,2})), IntVector({1,3,2})); REQUIRE_EQ(take_cyclic(5, IntVector({0,1,2,3})), IntVector({0,1,2,3,0})); REQUIRE_EQ(take_cyclic(7, IntVector({0,1,2,3})), IntVector({0,1,2,3,0,1,2})); REQUIRE_EQ(take_cyclic(7, IntVector({0,1})), IntVector({0,1,0,1,0,1,0})); REQUIRE_EQ(take_cyclic(2, IntVector({0,1,2,3})), IntVector({0,1})); REQUIRE_EQ(take_cyclic(3, IntVector({0})), IntVector({0,0,0})); REQUIRE_EQ(take_while(is_even, IntVector({ 4, 3 })), IntVector({4})); REQUIRE_EQ(drop_while(is_even, IntVector({ 4, 8 })), IntVector()); } TEST_CASE("container_common_test, keep_if") { using namespace fplus; REQUIRE_EQ(keep_if(is2, xs), IntVector({ 2,2,2 })); REQUIRE_EQ(keep_if(is3, xs), IntVector({ 3 })); REQUIRE_EQ(keep_if(is4, xs), IntVector()); } TEST_CASE("container_common_test, find_all_idxs_of") { using namespace fplus; REQUIRE_EQ(find_all_idxs_of('h', std::string("oh, ha!")), std::vector({ 1, 4 })); REQUIRE_EQ(find_all_idxs_of(2, xs), IdxVector({ 1,2,4 })); } TEST_CASE("container_common_test, generate") { using namespace fplus; int countUpCounter = 0; auto countUp = [countUpCounter]() mutable { return countUpCounter++; }; REQUIRE_EQ(generate(countUp, 3), IntVector({ 0,1,2 })); auto square_size_t_return_int = [&](std::size_t x) -> int { return squareLambda(static_cast(x)); }; REQUIRE_EQ(generate_by_idx(square_size_t_return_int, 3), IntVector({ 0,1,4 })); } TEST_CASE("container_common_test, nub") { using namespace fplus; REQUIRE_EQ(nub(xs), IntVector({ 1,2,3 })); auto bothEven = is_equal_by(is_even_int); REQUIRE_EQ(nub_by(bothEven, xs), IntVector({ 1,2 })); REQUIRE_EQ(nub_on(int_mod_10, IntVector({12,32,15})), IntVector({12,15})); } TEST_CASE("container_common_test, coucount_occurrences_bynt_occurrences_on") { using namespace fplus; typedef std::map IntSizeTMap; IntSizeTMap OccurrencesResult = {{1, 1}, {2, 3}, {3, 1}}; std::vector double_values = {1.1, 2.3, 2.7, 3.6, 2.4}; const auto f = floor; REQUIRE_EQ(count_occurrences_by(f, double_values), OccurrencesResult); } TEST_CASE("container_common_test, count_occurrences") { using namespace fplus; typedef std::map IntSizeTMap; IntSizeTMap OccurrencesResult = {{1, 1}, {2, 3}, {3, 1}}; REQUIRE_EQ(count_occurrences(xs), OccurrencesResult); } TEST_CASE("container_common_test, insert_at") { using namespace fplus; REQUIRE_EQ(insert_at(2, IntVector({8,9}), xs), IntVector({1,2,8,9,2,3,2})); } TEST_CASE("container_common_test, head") { using namespace fplus; REQUIRE_EQ(head(xs), 1); } TEST_CASE("container_common_test, last") { using namespace fplus; REQUIRE_EQ(last(xs), 2); } TEST_CASE("container_common_test, tail") { using namespace fplus; REQUIRE_EQ(init(xs), IntVector({1,2,2,3})); REQUIRE_EQ(init(IntVector({1,2,2,3,2})), IntVector({1,2,2,3})); REQUIRE_EQ(tail(xs), IntVector({2,2,3,2})); REQUIRE_EQ(tail(IntVector({1,2,2,3,2})), IntVector({2,2,3,2})); REQUIRE_EQ(inits(xs), IntVectors({{},{1},{1,2},{1,2,2},{1,2,2,3},{1,2,2,3,2}})); REQUIRE_EQ(tails(xs), IntVectors({{1,2,2,3,2},{2,2,3,2},{2,3,2},{3,2},{2},{}})); } TEST_CASE("container_common_test, iterate") { using namespace fplus; auto times_two = [](int x) { return 2*x; }; REQUIRE_EQ(iterate(times_two, 0, 3), IntVector({})); REQUIRE_EQ(iterate(times_two, 1, 3), IntVector({3})); REQUIRE_EQ(iterate(times_two, 2, 3), IntVector({3,6})); REQUIRE_EQ(iterate(times_two, 5, 3), IntVector({3,6,12,24,48})); } TEST_CASE("container_common_test, divvy") { using namespace fplus; REQUIRE_EQ(divvy(5, 2, IntVector({0,1,2,3,4,5,6,7,8,9})), IntVectors({{0,1,2,3,4},{2,3,4,5,6},{4,5,6,7,8}})); } TEST_CASE("container_common_test, aperture") { using namespace fplus; REQUIRE_EQ(aperture(5, IntVector({0,1,2,3,4,5,6})), IntVectors({{0,1,2,3,4},{1,2,3,4,5},{2,3,4,5,6}})); REQUIRE_EQ(divvy(5, 1, IntVector({0,1,2,3,4,5,6})), IntVectors({{0,1,2,3,4},{1,2,3,4,5},{2,3,4,5,6}})); } TEST_CASE("container_common_test, stride") { using namespace fplus; REQUIRE_EQ(stride(3, IntVector({0,1,2,3,4,5,6,7})), IntVector({0,3,6})); REQUIRE_EQ(divvy(1, 3, IntVector({0,1,2,3,4,5,6,7})), IntVectors({{0},{3},{6}})); } TEST_CASE("composition_test, instead_of_if_empty") { using namespace fplus; std::vector xs_empty; std::vector xs_full = {1,2,3}; std::vector xs_alt = {0}; REQUIRE_EQ(instead_of_if_empty(xs_alt, xs_empty), xs_alt); REQUIRE_EQ(instead_of_if_empty(xs_alt, xs_full), xs_full); } libfplus-0.2.13/test/container_properties_test.cpp000066400000000000000000000160531376322245400224120ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { auto is_even_int = [](int x){ return x % 2 == 0; }; typedef std::vector IntVector; typedef std::vector BoolVector; IntVector xs = {1,2,2,3,2}; } TEST_CASE("container_properties_test, any") { using namespace fplus; REQUIRE_EQ(any(BoolVector()), false); REQUIRE_EQ(any(BoolVector({true})), true); REQUIRE_EQ(any(BoolVector({false})), false); REQUIRE_EQ(any(BoolVector({false, false})), false); REQUIRE_EQ(any(BoolVector({true, false})), true); REQUIRE_EQ(any_by(is_even_int, IntVector()), false); REQUIRE_EQ(any_by(is_even_int, IntVector({2})), true); REQUIRE_EQ(any_by(is_even_int, IntVector({1})), false); REQUIRE_EQ(any_by(is_even_int, IntVector({1, 1})), false); REQUIRE_EQ(any_by(is_even_int, IntVector({2, 1})), true); } TEST_CASE("container_properties_test, none") { using namespace fplus; REQUIRE_EQ(none(BoolVector()), true); REQUIRE_EQ(none(BoolVector({true})), false); REQUIRE_EQ(none(BoolVector({false})), true); REQUIRE_EQ(none(BoolVector({false, false})), true); REQUIRE_EQ(none(BoolVector({true, false})), false); REQUIRE_EQ(none_by(is_even_int, IntVector()), true); REQUIRE_EQ(none_by(is_even_int, IntVector({2})), false); REQUIRE_EQ(none_by(is_even_int, IntVector({1})), true); REQUIRE_EQ(none_by(is_even_int, IntVector({1, 1})), true); REQUIRE_EQ(none_by(is_even_int, IntVector({2, 1})), false); } TEST_CASE("container_properties_test, minmax") { using namespace fplus; auto negateInt = [](int i) -> int { return -i; }; REQUIRE_EQ(minimum(xs), 1); REQUIRE_EQ(maximum(xs), 3); REQUIRE_EQ(minimum_by(std::greater(), xs), 3); REQUIRE_EQ(maximum_by(std::greater(), xs), 1); REQUIRE_EQ(minimum_on(negateInt, xs), 3); REQUIRE_EQ(maximum_on(negateInt, xs), 1); REQUIRE_EQ(minimum_idx(xs), 0); REQUIRE_EQ(maximum_idx(xs), 3); REQUIRE_EQ(minimum_idx_by(std::greater(), xs), 3); REQUIRE_EQ(maximum_idx_by(std::greater(), xs), 0); REQUIRE_EQ(minimum_idx_on(negateInt, xs), 3); REQUIRE_EQ(maximum_idx_on(negateInt, xs), 0); } TEST_CASE("container_properties_test, minmax_maybe") { using namespace fplus; auto negateInt = [](int i) -> int { return -i; }; REQUIRE_EQ(minimum_maybe(xs), maybe(1)); REQUIRE_EQ(maximum_maybe(xs), maybe(3)); REQUIRE_EQ(minimum_by_maybe(std::greater(), xs), maybe(3)); REQUIRE_EQ(maximum_by_maybe(std::greater(), xs), maybe(1)); REQUIRE_EQ(minimum_on_maybe(negateInt, xs), maybe(3)); REQUIRE_EQ(maximum_on_maybe(negateInt, xs), maybe(1)); REQUIRE_EQ(minimum_idx_maybe(xs), maybe(0)); REQUIRE_EQ(maximum_idx_maybe(xs), maybe(3)); REQUIRE_EQ(minimum_idx_by_maybe(std::greater(), xs), maybe(3)); REQUIRE_EQ(maximum_idx_by_maybe(std::greater(), xs), maybe(0)); } TEST_CASE("container_properties_test, mean") { using namespace fplus; std::vector uchars = {200, 202}; typedef std::vector DoubleVector; REQUIRE_EQ(sum(xs), 10); REQUIRE_EQ(product(xs), 24); REQUIRE_EQ(mean(xs), 2); REQUIRE_EQ(mean_using_doubles(uchars), 201); REQUIRE_EQ(median(IntVector({ 3 })), 3); REQUIRE_EQ(median(IntVector({ 3, 5 })), 4); REQUIRE(is_in_interval(3.49f, 3.51f, median(IntVector({ 3, 4 })))); REQUIRE(is_in_interval(3.49, 3.51, mean(DoubleVector({ 3, 4 })))); REQUIRE_EQ(median(IntVector({ 3, 9, 5 })), 5); REQUIRE_EQ(median(xs), 2); REQUIRE_EQ(sum(convert_container_and_elems>(std::string("hello"))), 532); REQUIRE(is_in_interval(5.99, 6.01, mean_stddev(DoubleVector({ 4, 8 })).first)); REQUIRE(is_in_interval(1.99, 2.01, mean_stddev(DoubleVector({ 4, 8 })).second)); REQUIRE(is_in_interval(3.749f, 3.751f, mean_stddev(IntVector({ 1, 3, 7, 4 })).first)); REQUIRE(is_in_interval(2.16f, 2.17f, mean_stddev(IntVector({ 1, 3, 7, 4 })).second)); } TEST_CASE("container_properties_test, all_unique_less") { using namespace fplus; REQUIRE_FALSE(all_unique_less(IntVector({1,2,3,2}))); REQUIRE(all_unique_less(IntVector({4,2,1,3}))); } TEST_CASE("container_properties_test, infix") { using namespace fplus; REQUIRE_EQ(is_infix_of(IntVector({}), IntVector({})), true); REQUIRE_EQ(is_infix_of(IntVector({}), IntVector({1,2})), true); REQUIRE_EQ(is_infix_of(IntVector({2,3}), xs), true); REQUIRE_EQ(is_infix_of(IntVector({2,3}), xs), true); REQUIRE_EQ(is_infix_of(IntVector({2,1}), xs), false); REQUIRE_EQ(is_prefix_of(IntVector({ 1,2 }), xs), true); REQUIRE_EQ(is_prefix_of(IntVector({ 2,2 }), xs), false); REQUIRE_EQ(is_suffix_of(IntVector({ 3,2 }), xs), true); REQUIRE_EQ(is_suffix_of(IntVector({ 2,2 }), xs), false); } TEST_CASE("container_properties_test, subsequence") { using namespace fplus; REQUIRE_EQ(is_subsequence_of(IntVector(), IntVector()), true); REQUIRE_EQ(is_subsequence_of(IntVector(), xs), true); REQUIRE_EQ(is_subsequence_of(IntVector({ 1,3 }), xs), true); REQUIRE_EQ(is_subsequence_of(IntVector({ 3,1 }), xs), false); } TEST_CASE("container_properties_test, count") { using namespace fplus; REQUIRE_EQ(count(2, xs), 3); } TEST_CASE("container_properties_test, is_unique_in") { using namespace fplus; REQUIRE_FALSE(is_unique_in(2, xs)); REQUIRE(is_unique_in(3, xs)); } TEST_CASE("container_properties_test, is_permutation_of") { using namespace fplus; REQUIRE(is_permutation_of(IntVector({2,3,1}), IntVector({1,2,3}))); REQUIRE_FALSE(is_permutation_of(IntVector({2,3,2}), IntVector({1,2,3}))); REQUIRE_FALSE(is_permutation_of(IntVector({2,3}), IntVector({1,2,3}))); REQUIRE_FALSE(is_permutation_of(IntVector({2,3,1}), IntVector({1,23}))); } TEST_CASE("container_properties_test, fill_pigeonholes") { const std::vector ys = { 0, 1, 3, 1 }; REQUIRE_EQ(fplus::fill_pigeonholes_to(5, ys), std::vector({1,2,0,1,0})); REQUIRE_EQ(fplus::fill_pigeonholes_to(3, ys), std::vector({1,2,0})); REQUIRE_EQ(fplus::fill_pigeonholes(ys), std::vector({1,2,0,1})); REQUIRE_EQ(fplus::fill_pigeonholes_bool_to(3, ys), std::vector({1,1,0})); REQUIRE_EQ(fplus::fill_pigeonholes_bool_to(5, ys), std::vector({1,1,0,1,0})); REQUIRE_EQ(fplus::fill_pigeonholes_bool(ys), std::vector({1,1,0,1})); } TEST_CASE("container_properties_test, present_in_all") { using namespace fplus; const std::vector> xss = { {4,1,2}, {5,2,1}, {2,4,1} }; REQUIRE_EQ(present_in_all(xss), IntVector({1,2})); }libfplus-0.2.13/test/container_traits_test.cpp000066400000000000000000000024561376322245400215260ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include TEST_CASE("container_traits_test, static_asserts") { using namespace fplus; const auto unary_f = [](int x) -> double { return static_cast(x); }; const auto binary_f = [](int x, int y) -> double { return static_cast(x + y); }; static_assert(std::is_same< internal::same_cont_new_t< std::vector, double>::type, std::vector>::value, "No."); static_assert(std::is_same< internal::same_cont_new_t_from_unary_f< std::vector, decltype(unary_f)>::type, std::vector>::value, "No."); static_assert(std::is_same< internal::same_cont_new_t_from_binary_f< std::vector, decltype(binary_f), int, int>::type, std::vector>::value, "No."); } libfplus-0.2.13/test/curry_test.cpp000066400000000000000000000014371376322245400173200ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include namespace { } TEST_CASE("curry_test, show_fill_left") { using namespace fplus; REQUIRE_EQ(show_fill_left(' ', 4, 3), " 3"); const auto result_old_style = show_fill_left(' ', 4, 3); const auto result_new_style = curry::show_fill_left(' ')(std::size_t(4))(3); //const auto result_new_style = curry__show_fill_left(' ')(std::size_t(4))(3); REQUIRE_EQ(result_old_style, result_new_style); } libfplus-0.2.13/test/extrapolate_test.cpp000066400000000000000000000046401376322245400205030ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { typedef std::vector IntVector; IntVector xs = {1,2,2,3,2}; } TEST_CASE("extrapolate_test, elem_at_idx_or_nothing") { using namespace fplus; REQUIRE_EQ(elem_at_idx_or_nothing(-4, xs), nothing()); REQUIRE_EQ(elem_at_idx_or_nothing(14, xs), nothing()); REQUIRE_EQ(elem_at_idx_or_nothing(3, xs), maybe(3)); } TEST_CASE("extrapolate_test, elem_at_idx_or_constant") { using namespace fplus; REQUIRE_EQ(elem_at_idx_or_constant(4, -4, xs), 4); REQUIRE_EQ(elem_at_idx_or_constant(4, 14, xs), 4); REQUIRE_EQ(elem_at_idx_or_constant(4, 3, xs), 3); } TEST_CASE("extrapolate_test, elem_at_idx_or_replicate") { using namespace fplus; REQUIRE_EQ(elem_at_idx_or_replicate(-2, xs), 1); REQUIRE_EQ(elem_at_idx_or_replicate(-1, xs), 1); REQUIRE_EQ(elem_at_idx_or_replicate(0, xs), 1); REQUIRE_EQ(elem_at_idx_or_replicate(4, xs), 2); REQUIRE_EQ(elem_at_idx_or_replicate(5, xs), 2); REQUIRE_EQ(elem_at_idx_or_replicate(6, xs), 2); } TEST_CASE("extrapolate_test, elem_at_idx_or_wrap") { using namespace fplus; REQUIRE_EQ(elem_at_idx_or_wrap(-2, xs), 3); REQUIRE_EQ(elem_at_idx_or_wrap(-1, xs), 2); REQUIRE_EQ(elem_at_idx_or_wrap(0, xs), 1); REQUIRE_EQ(elem_at_idx_or_wrap(4, xs), 2); REQUIRE_EQ(elem_at_idx_or_wrap(5, xs), 1); REQUIRE_EQ(elem_at_idx_or_wrap(6, xs), 2); } TEST_CASE("extrapolate_test, elem_at_idx_or_wrap") { using namespace fplus; REQUIRE_EQ(elem_at_idx_or_wrap(-2, xs), 3); REQUIRE_EQ(elem_at_idx_or_wrap(-1, xs), 2); REQUIRE_EQ(elem_at_idx_or_wrap(0, xs), 1); REQUIRE_EQ(elem_at_idx_or_wrap(4, xs), 2); REQUIRE_EQ(elem_at_idx_or_wrap(5, xs), 1); REQUIRE_EQ(elem_at_idx_or_wrap(6, xs), 2); } TEST_CASE("extrapolate_test, extrapolate_replicate") { using namespace fplus; REQUIRE_EQ(extrapolate_replicate(2, 2, xs), IntVector({1,1,1,2,2,3,2,2,2})); } TEST_CASE("extrapolate_test, extrapolate_wrap") { using namespace fplus; REQUIRE_EQ(extrapolate_wrap(2, 2, xs), IntVector({3,2,1,2,2,3,2,1,2})); } libfplus-0.2.13/test/filter_test.cpp000066400000000000000000000173661376322245400174510ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { auto is_even = [](auto value) { return (value % 2 == 0); }; auto is_even_size_t = [](auto value) { return ( value % 2 == 0 ); }; auto accept_with_index = [](std::size_t index, auto value) { return ( index % 2 == 0 ) && ( value >= 10 ); }; typedef std::vector IntVector; typedef std::vector IntVectors; } TEST_CASE("filter_test, keep_if") { const std::vector v = { 1, 2, 3, 2, 4, 5 }; auto result = fplus::keep_if(is_even, v); REQUIRE_EQ(result, std::vector({2, 2, 4})); const auto keep_evens = fplus::bind_1st_of_2( fplus::keep_if, is_even); REQUIRE_EQ( fplus::transform(keep_evens, IntVectors({{1,3,4},{1,2}})), IntVectors({{4},{2}})); } TEST_CASE("filter_test, keep_if_r_value") { auto result = fplus::keep_if(is_even, std::vector({1,2,3,2,4,5})); REQUIRE_EQ(result, std::vector({2, 2, 4})); } TEST_CASE("filter_test, drop_if") { const std::vector v = { 1, 2, 3, 2, 4, 5 }; auto result = fplus::drop_if(is_even, v); REQUIRE_EQ(result, std::vector({1, 3, 5})); } TEST_CASE("filter_test, without") { using namespace fplus; typedef std::vector Ints; REQUIRE_EQ(without(1, Ints({1,2,3})), Ints({2,3})); REQUIRE_EQ(without(5, Ints({1,2,3})), Ints({1,2,3})); REQUIRE_EQ(without(5, Ints({})), Ints({})); } TEST_CASE("filter_test, keep_if_with_idx") { const std::vector v = { 1, 20, 30, 4, 50, 60, 7 }; auto result = fplus::keep_if_with_idx(accept_with_index, v); REQUIRE_EQ(result, std::vector({30, 50})); } TEST_CASE("filter_test, drop_if_with_idx") { const std::vector v = { 1, 20, 30, 4, 50, 60, 7 }; auto result = fplus::drop_if_with_idx(accept_with_index, v); REQUIRE_EQ(result, std::vector({1, 20, 4, 60, 7})); } TEST_CASE("filter_test, keep_by_idx") { const std::vector v = { 11, 17, 3, 8, 49, 6 }; auto result = fplus::keep_by_idx(is_even_size_t, v); REQUIRE_EQ(result, std::vector({11, 3, 49})); auto result_rvalue = fplus::keep_by_idx(is_even_size_t, std::vector({ 11, 17, 3, 8, 49, 6 })); REQUIRE_EQ(result_rvalue, std::vector({11, 3, 49})); } TEST_CASE("filter_test, drop_by_idx") { const std::vector v = { 11, 17, 3, 8, 49, 6 }; auto result = fplus::drop_by_idx(is_even_size_t, v); REQUIRE_EQ(result, std::vector({17, 8, 6})); } TEST_CASE("filter_test, keep_idxs") { const std::vector v = { 1, 2, 3, 4, 5, 6, 7 }; const std::vector indices = { 2, 5 }; auto result = fplus::keep_idxs(indices, v); REQUIRE_EQ(result, std::vector({3, 6})); } TEST_CASE("filter_test, drop_idx") { const std::vector v = { 1, 2, 3, 4, 5, 6, 7 }; auto result = fplus::drop_idx(2, v); REQUIRE_EQ(result, std::vector({1, 2, 4, 5, 6, 7})); } TEST_CASE("filter_test, drop_idxs") { const std::vector v = { 1, 2, 3, 4, 5, 6, 7 }; const std::vector indices = { 2, 5 }; auto result = fplus::drop_idxs(indices, v); REQUIRE_EQ(result, std::vector({1, 2, 4, 5, 7})); } TEST_CASE("filter_test, justs") { using fplus::maybe; using fplus::just; using fplus::nothing; const std::vector> v = { just(1), nothing(), just(2) }; auto result = fplus::justs(v); REQUIRE_EQ(result, std::vector({1, 2})); } TEST_CASE("filter_test, oks") { using fplus::ok; using fplus::error; const std::vector> v = { ok(1), error(std::string("abc")), ok(2) }; auto result = fplus::oks(v); REQUIRE_EQ(result, std::vector({1, 2})); } TEST_CASE("filter_test, errors") { using fplus::ok; using fplus::error; const std::vector> v = { ok(1), error(std::string("abc")), ok(2) }; auto result = fplus::errors(v); REQUIRE_EQ(result, std::vector({"abc"})); } TEST_CASE("filter_test, trim_left") { const std::vector v = { 0, 0, 0, 5, 6, 7, 8, 6, 4 }; auto result = fplus::trim_left(0, v); REQUIRE_EQ(result, std::vector({5, 6, 7, 8, 6, 4})); } TEST_CASE("filter_test, trim_token_left") { const std::vector v = { 0, 1, 2, 0, 1, 2, 7, 5, 9 }; const std::vector token = { 0, 1, 2 }; auto result = fplus::trim_token_left(token, v); REQUIRE_EQ(result, std::vector({7, 5, 9})); } TEST_CASE("filter_test, trim_right_by") { const std::vector v = { 0, 2, 4, 5, 6, 7, 8, 6, 4 }; auto result = fplus::trim_right_by(is_even, v); REQUIRE_EQ(result, std::vector({0, 2, 4, 5, 6, 7})); } TEST_CASE("filter_test, trim_right_by_trims_all") { const std::vector v = { 4, 8 }; auto result = fplus::trim_right_by(is_even, v); REQUIRE(result.empty()); } TEST_CASE("filter_test, trim_right") { const std::vector v = { 0, 2, 4, 5, 6, 7, 8, 4, 4 }; auto result = fplus::trim_right(4, v); REQUIRE_EQ(result, std::vector({0, 2, 4, 5, 6, 7, 8})); } TEST_CASE("filter_test, trim_token_right") { const std::vector v = { 7, 5, 9, 0, 1, 2, 0, 1, 2 }; const std::vector token = { 0, 1, 2 }; auto result = fplus::trim_token_right(token, v); REQUIRE_EQ(result, std::vector({7, 5, 9})); } TEST_CASE("filter_test, trim_by") { const std::vector v = { 0, 2, 4, 5, 6, 7, 8, 6, 4 }; auto result = fplus::trim_by(is_even, v); REQUIRE_EQ(result, std::vector({5, 6, 7})); } TEST_CASE("filter_test, trim") { const std::vector v = { 0, 2, 4, 5, 6, 7, 8, 0, 0 }; auto result = fplus::trim(0, v); REQUIRE_EQ(result, std::vector({2, 4, 5, 6, 7, 8})); } TEST_CASE("filter_test, trim_token") { const std::vector v = { 0, 1, 7, 8, 9, 0, 1 }; const std::vector token = { 0, 1 }; auto result = fplus::trim_token(token, v); REQUIRE_EQ(result, std::vector({7, 8, 9})); } TEST_CASE("filter_test, adjacent_keep_snd_if") { const std::vector v = { 0, 1, 7, 8, 9, 0, 1 }; REQUIRE_EQ(fplus::adjacent_keep_snd_if(std::greater<>(), v), std::vector({0,0})); REQUIRE_EQ(fplus::adjacent_keep_snd_if(std::less(), v), std::vector({0,1,7,8,9,1})); } TEST_CASE("filter_test, adjacent_drop_snd_if") { const std::vector v = { 0, 1, 7, 8, 9, 0, 1 }; REQUIRE_EQ(fplus::adjacent_drop_snd_if(std::less<>(), v), std::vector({0,0})); REQUIRE_EQ(fplus::adjacent_drop_snd_if(std::greater(), v), std::vector({0,1,7,8,9,1})); } TEST_CASE("filter_test, adjacent_drop_fst_if") { const std::vector v = { 0, 1, 7, 8, 9, 0, 1 }; REQUIRE_EQ(fplus::adjacent_drop_fst_if(std::less<>(), v), std::vector({9,1})); REQUIRE_EQ(fplus::adjacent_drop_fst_if(std::greater(), v), std::vector({0,1,7,8,0,1})); } TEST_CASE("filter_test, adjacent_keep_fst_if") { const std::vector v = { 0, 1, 7, 8, 9, 0, 1 }; REQUIRE_EQ(fplus::adjacent_keep_fst_if(std::greater<>(), v), std::vector({9,1})); REQUIRE_EQ(fplus::adjacent_keep_fst_if(std::less(), v), std::vector({0,1,7,8,0,1})); } libfplus-0.2.13/test/function_traits_test.cpp000066400000000000000000000114251376322245400213650ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { typedef std::deque IntDeq; typedef std::deque IntContCont; typedef IntDeq IntCont; typedef IntCont Row; } std::string CcI2SFree(const std::string& str, int x) { return str + std::to_string(x); } auto CcI2SLambda = [](const std::string& str, int x) { return CcI2SFree(str, x); }; std::function CcI2SStdFunction = CcI2SLambda; std::string (*CcI2SFunctionPointer)(const std::string&, int) = &CcI2SFree; struct CcI2SStrct { std::string operator() (const std::string& str, int x) { return CcI2SFree(str, x); } std::string nonCMemF (const std::string& str, int x) { return CcI2SFree(str, x); } std::string cnstMemF (const std::string& str, int x) const { return CcI2SFree(str, x); } static std::string sttcMemF (const std::string& str, int x) { return CcI2SFree(str, x); } }; TEST_CASE("function_traits_test, static_asserts") { using namespace fplus; static_assert(std::is_same< utils::function_traits::arg<0>::type, const std::string&>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<1>::type, int>::value, "No."); static_assert(std::is_same< utils::function_traits::result_type, std::string>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<0>::type, const std::string&>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<1>::type, int>::value, "No."); static_assert(std::is_same< utils::function_traits::result_type, std::string>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<0>::type, const std::string&>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<1>::type, int>::value, "No."); static_assert(std::is_same< utils::function_traits::result_type, std::string>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<0>::type, const std::string&>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<1>::type, int>::value, "No."); static_assert(std::is_same< utils::function_traits::result_type, std::string>::value, "No."); CcI2SStrct ccI2SStrct; ccI2SStrct("dummy call to avoid unused variable warnings", 0); static_assert(std::is_same< utils::function_traits::arg<0>::type, const std::string&>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<1>::type, int>::value, "No."); static_assert(std::is_same< utils::function_traits::result_type, std::string>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<0>::type, const std::string&>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<1>::type, int>::value, "No."); static_assert(std::is_same< utils::function_traits::result_type, std::string>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<0>::type, const std::string&>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<1>::type, int>::value, "No."); static_assert(std::is_same< utils::function_traits::result_type, std::string>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<0>::type, const std::string&>::value, "No."); static_assert(std::is_same< utils::function_traits::arg<1>::type, int>::value, "No."); static_assert(std::is_same< utils::function_traits::result_type, std::string>::value, "No."); } libfplus-0.2.13/test/fwd_test.cpp000066400000000000000000000157301376322245400167350ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include namespace { typedef std::vector IntVector; bool is_odd_int(int x) { return (x % 2 == 1); } bool is_even_int(int x) { return (x % 2 == 0); } int times_3(int x) { return 3 * x; } int as_string_length(int i) { return static_cast(std::to_string(i).size()); } const auto times_3_lambda = [](int x){return times_3(x);}; const auto is_odd_int_lambda = [](int x){return is_odd_int(x);}; const auto as_string_length_lambda = [](int x){return as_string_length(x);}; int (*times_3_fn_ptr)(int) = ×_3; struct times_3_struct { int operator() (const int x) { return times_3(x); } static int sttcMemF(int x) { return times_3(x); } }; std::function times_3_std_function = times_3_lambda; } TEST_CASE("fwd_test, apply") { using namespace fplus; const auto result_old_style = sum( transform(as_string_length, drop_if(is_odd_int, transform(times_3, numbers(0, 10))))); const auto result_new_style = fwd::apply( numbers(0, 10) , fwd::transform(times_3) , fwd::drop_if(is_odd_int) , fwd::transform(as_string_length) , fwd::sum()); REQUIRE_EQ(result_old_style, result_new_style); } TEST_CASE("fwd_test, compose") { using namespace fplus; const auto function_chain_old_style = compose( bind_1st_of_2(transform&, std::vector>, times_3), bind_1st_of_2(drop_if&>, is_odd_int), bind_1st_of_2(transform&>, as_string_length_lambda), sum>); const auto function_chain_new_style = fwd::compose( fwd::transform(times_3), fwd::drop_if(is_odd_int_lambda), fwd::transform(as_string_length), fwd::sum()); const auto xs = numbers(0, 10); REQUIRE_EQ(function_chain_old_style(xs), function_chain_new_style(xs)); } TEST_CASE("fwd_test, and_then_maybe") { using namespace fplus; const auto sqrtToMaybeInt = [](int x) -> fplus::maybe { return x < 0 ? fplus::nothing() : fplus::just(fplus::round(sqrt(static_cast(x)))); }; REQUIRE_EQ( fwd::apply(just(4) , fwd::and_then_maybe(sqrtToMaybeInt)) , just(2)); } TEST_CASE("fwd_test, fold_left") { using namespace fplus; const auto fold_result_old_style = fold_left(std::plus(), 0, numbers(0, 10)); const auto fold_result_new_style = fwd::apply( numbers(0, 10) , fwd::fold_left(std::plus(), 0)); REQUIRE_EQ(fold_result_old_style, fold_result_new_style); } TEST_CASE("fwd_test, transform_nested") { using namespace fplus; typedef std::vector ints; const std::vector nested_ints = {{1,2,3},{4,5,6}}; const auto nested_transformed_old_style = transform( bind_1st_of_2(transform&, std::vector>, times_3), nested_ints); const auto nested_transformed_new_style = fwd::apply( nested_ints , fwd::transform(fwd::transform(times_3_lambda))); REQUIRE_EQ(nested_transformed_old_style, nested_transformed_new_style); } TEST_CASE("fwd_test, different_function_types_apply") { using namespace fplus; const std::vector xs = {1,2,3}; const auto result = transform(times_3, xs); REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3)), result); REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3_lambda)), result); REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3_std_function)), result); REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3_fn_ptr)), result); REQUIRE_EQ(fwd::apply(xs, fwd::transform(×_3_struct::sttcMemF)), result); REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3_struct())), result); } TEST_CASE("fwd_test, different_function_types_compose") { using namespace fplus; const std::vector xs = {1,2,3}; const auto result = transform(times_3, transform(times_3, xs)); REQUIRE_EQ(fwd::transform(fwd::compose(times_3, times_3))(xs), result); REQUIRE_EQ(fwd::transform(fwd::compose(times_3_lambda, times_3_lambda))(xs), result); REQUIRE_EQ(fwd::transform(fwd::compose(times_3_std_function, times_3_std_function))(xs), result); REQUIRE_EQ(fwd::transform(fwd::compose(×_3_struct::sttcMemF, ×_3_struct::sttcMemF))(xs), result); REQUIRE_EQ(fwd::transform(fwd::compose(times_3_fn_ptr, times_3_fn_ptr))(xs), result); //const auto times_3_instance = times_3_struct(); //REQUIRE_EQ(fwd::transform(fwd::compose(times_3_instance, times_3_instance))(xs), result); } std::list collatz_seq(int x) { std::list result; while (x > 1) { result.push_back(x); if (x % 2 == 0) x = x / 2; else x = 3 * x + 1; } result.push_back(x); return result; } TEST_CASE("fwd_test, collatz") { using namespace fplus; auto collatz_dict = fwd::apply( fplus::numbers(0, 20) , fwd::create_map_with(fwd::compose( collatz_seq, fwd::show_cont_with(" => "))) ); } TEST_CASE("fwd_test, fwd_flip") { using namespace fplus; std::vector> idxs = {{0,1,2}, {2,0}}; const std::vector xs = {0,10,20}; const std::vector ys = fwd::transform_and_concat(fwd::flip::elems_at_idxs(xs))(idxs); const std::vector result = {0,10,20,20,0}; REQUIRE_EQ(ys, result); } TEST_CASE("fwd_test, keep_if") { const std::vector v = { 1, 2, 3, 2, 4, 5 }; auto result = fplus::fwd::keep_if(is_even_int)(v); REQUIRE_EQ(result, std::vector({2, 2, 4})); } TEST_CASE("fwd_test, keep_if_r_value") { auto result = fplus::fwd::keep_if(is_even_int)(std::vector({1,2,3,2,4,5})); REQUIRE_EQ(result, std::vector({2, 2, 4})); } TEST_CASE("fwd_test, zip_with") { using namespace fplus; const auto multiply_int = [](int x, int y) -> int { return x * y; }; const auto multiply_generic = [](auto x, auto y){ return x * y; }; IntVector xs = {1,2,3,4,2}; IntVector ys = {2,2,3,1}; IntVector xs_mult_ys = {2,4,9,4}; REQUIRE_EQ(fwd::zip_with(multiply_int, ys)(xs), xs_mult_ys); REQUIRE_EQ(fwd::zip_with(multiply_generic, ys)(xs), xs_mult_ys); } TEST_CASE("fwd_test, append") { using namespace fplus; IntVector xs = {1,2,3,4,2}; IntVector ys = {2,2,3,1}; IntVector xs_append_ys = {1,2,3,4,2,2,2,3,1}; REQUIRE_EQ(fwd::append(xs)(ys), xs_append_ys); } libfplus-0.2.13/test/generate_test.cpp000066400000000000000000000270161376322245400177470ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include #include namespace { auto compare_not_eq = [](auto c1, auto c2) { return c1 != c2; }; auto as_pair = [](auto c1, auto c2) { return std::make_pair(c1, c2); }; } TEST_CASE("generate_test, generate") { int value = 0; auto f = [&] { return value++; }; auto result = fplus::generate>(f, 6); REQUIRE_EQ(result, std::vector({0, 1, 2, 3, 4 ,5})); } TEST_CASE("generate_test, generate_by_idx") { auto f = [](std::size_t value) { return value + 10; }; auto result = fplus::generate_by_idx>(f, 6); REQUIRE_EQ(result, std::vector({10, 11, 12, 13, 14 ,15})); auto f2 = [f](auto value) { return f(value); }; auto result2 = fplus::generate_by_idx>(f2, 6); REQUIRE_EQ(result2, result); } TEST_CASE("generate_test, repeat") { const std::vector v = { 1, 2 }; auto result = fplus::repeat(3, v); REQUIRE_EQ(result, std::vector({1, 2, 1, 2, 1, 2})); } TEST_CASE("generate_test, replicate") { auto result = fplus::replicate(3, 1); REQUIRE_EQ(result, std::vector({1, 1, 1})); } TEST_CASE("generate_test, infixes") { const std::vector v = { 1, 2, 3, 4, 5, 6 }; auto result = fplus::infixes(3, v); REQUIRE_EQ(4u, result.size()); REQUIRE_EQ(result[0], std::vector({1, 2, 3})); REQUIRE_EQ(result[1], std::vector({2, 3, 4})); REQUIRE_EQ(result[2], std::vector({3, 4, 5})); REQUIRE_EQ(result[3], std::vector({4, 5, 6})); } TEST_CASE("generate_test, infixes_with_size_less_than_length") { const std::vector v = { 1, 2 }; auto result = fplus::infixes(3, v); REQUIRE(result.empty()); } TEST_CASE("generate_test, carthesian_product_with_where") { const std::vector v1 = { 'A', 'B', 'C' }; const std::vector v2 = { 'X', 'Y' }; auto result = fplus::carthesian_product_with_where(as_pair, compare_not_eq, v1, v2); std::vector> expected = { {'A', 'X'}, {'A', 'Y'}, {'B', 'X'}, {'B', 'Y'}, {'C', 'X'}, {'C', 'Y'}}; REQUIRE_EQ(result, expected); } TEST_CASE("generate_test, carthesian_product_with") { const std::vector v1 = { 'A', 'B', 'C' }; const std::vector v2 = { 'X', 'Y' }; auto result = fplus::carthesian_product_with(as_pair, v1, v2); std::vector> expected = { {'A', 'X'}, {'A', 'Y'}, {'B', 'X'}, {'B', 'Y'}, {'C', 'X'}, {'C', 'Y'}}; REQUIRE_EQ(result, expected); } TEST_CASE("generate_test, carthesian_product_where") { const std::vector v1 = { 'A', 'B', 'C' }; const std::vector v2 = { 'X', 'Y' }; auto result = fplus::carthesian_product_where(compare_not_eq, v1, v2); std::vector> expected = { {'A', 'X'}, {'A', 'Y'}, {'B', 'X'}, {'B', 'Y'}, {'C', 'X'}, {'C', 'Y'}}; REQUIRE_EQ(result, expected); } TEST_CASE("generate_test, carthesian_product") { const std::vector v1 = { 'A', 'B', 'C' }; const std::vector v2 = { 'X', 'Y' }; auto result = fplus::carthesian_product(v1, v2); std::vector> expected = { {'A', 'X'}, {'A', 'Y'}, {'B', 'X'}, {'B', 'Y'}, {'C', 'X'}, {'C', 'Y'}}; REQUIRE_EQ(result, expected); } TEST_CASE("generate_test, carthesian_product_n") { const std::vector v = { 'A', 'B' }; auto result = fplus::carthesian_product_n(2, v); REQUIRE_EQ(4u, result.size()); REQUIRE_EQ(result[0], std::vector({'A', 'A'})); REQUIRE_EQ(result[1], std::vector({'A', 'B'})); REQUIRE_EQ(result[2], std::vector({'B', 'A'})); REQUIRE_EQ(result[3], std::vector({'B', 'B'})); } TEST_CASE("generate_test, carthesian_product_n_with_power_0") { const std::vector v = { 'A', 'B' }; auto result = fplus::carthesian_product_n(0, v); REQUIRE_EQ(1u, result.size()); REQUIRE(result[0].empty()); } TEST_CASE("generate_test, permutations") { const std::vector v = { 'A', 'B' }; auto result = fplus::permutations(2, v); REQUIRE_EQ(2u, result.size()); REQUIRE_EQ(result[0], std::vector({'A', 'B'})); REQUIRE_EQ(result[1], std::vector({'B', 'A'})); } TEST_CASE("generate_test, permutations_with_power_0") { const std::vector v = { 'A', 'B' }; auto result = fplus::permutations(0, v); REQUIRE_EQ(1u, result.size()); REQUIRE(result[0].empty()); } TEST_CASE("generate_test, combinations") { const std::vector v = { 'A', 'B', 'C' }; auto result = fplus::combinations(2, v); REQUIRE_EQ(3u, result.size()); REQUIRE_EQ(result[0], std::vector({'A', 'B'})); REQUIRE_EQ(result[1], std::vector({'A', 'C'})); REQUIRE_EQ(result[2], std::vector({'B', 'C'})); } TEST_CASE("generate_test, combinations_with_power_0") { const std::vector v = { 'A', 'B' }; auto result = fplus::combinations(0, v); REQUIRE_EQ(1u, result.size()); REQUIRE(result[0].empty()); } TEST_CASE("generate_test, combinations_with_replacement") { const std::vector v = { 'A', 'B' }; auto result = fplus::combinations_with_replacement(2, v); REQUIRE_EQ(3u, result.size()); REQUIRE_EQ(result[0], std::vector({'A', 'A'})); REQUIRE_EQ(result[1], std::vector({'A', 'B'})); REQUIRE_EQ(result[2], std::vector({'B', 'B'})); } TEST_CASE("generate_test, combinations_with_replacement_with_power_0") { const std::vector v = { 'A', 'B' }; auto result = fplus::combinations_with_replacement(0, v); REQUIRE_EQ(1u, result.size()); REQUIRE(result[0].empty()); } TEST_CASE("generate_test, power_set") { const std::vector v = { 'x', 'y' }; auto result = fplus::power_set(v); REQUIRE_EQ(4u, result.size()); REQUIRE(result[0].empty()); REQUIRE_EQ(result[1], std::vector({'x'})); REQUIRE_EQ(result[2], std::vector({'y'})); REQUIRE_EQ(result[3], std::vector({'x', 'y'})); } TEST_CASE("generate_test, iterate") { auto f = [](auto value) { return value * 2; }; auto result = fplus::iterate(f, 5, 3); REQUIRE_EQ(5u, result.size()); REQUIRE_EQ(result, std::vector({3, 6, 12, 24, 48})); } TEST_CASE("generate_test, iterate_with_size_0") { auto f = [](auto value) { return value * 2; }; auto result = fplus::iterate(f, 0, 3); REQUIRE(result.empty()); } TEST_CASE("generate_test, iterate_maybe") { const auto next_collatz_value = [](auto x) -> fplus::maybe { if (x <= 1) return {}; if (x % 2 == 0) return x / 2; else return 3 * x + 1; }; const auto result = fplus::iterate_maybe(next_collatz_value, 5); REQUIRE_EQ(result, std::vector({5, 16, 8, 4, 2, 1})); } TEST_CASE("generate_test, adjecent_difference") { const std::vector v = { 0, 4, 1, 2, 5 }; auto result = fplus::adjacent_difference(v); REQUIRE_EQ(result, std::vector({0, 4, -3, 1, 3})); } TEST_CASE("generate_test, rotate_left") { const std::vector v = { 'x', 'y', 'z' }; auto result = fplus::rotate_left(v); REQUIRE_EQ(result, std::vector({'y', 'z', 'x'})); } TEST_CASE("generate_test, rotate_left_with_empty") { const std::vector v = { }; auto result = fplus::rotate_left(v); REQUIRE(result.empty()); } TEST_CASE("generate_test, rotate_right") { const std::vector v = { 'x', 'y', 'z' }; auto result = fplus::rotate_right(v); REQUIRE_EQ(result, std::vector({'z', 'x', 'y'})); } TEST_CASE("generate_test, rotate_right_with_empty") { const std::vector v = { }; auto result = fplus::rotate_right(v); REQUIRE(result.empty()); } TEST_CASE("generate_test, rotations_left") { const std::vector v = { 'a', 'b', 'c', 'd' }; auto result = fplus::rotations_left(v); REQUIRE_EQ(4u, result.size()); REQUIRE_EQ(result[0], std::vector({'a', 'b', 'c', 'd'})); REQUIRE_EQ(result[1], std::vector({'b', 'c', 'd', 'a'})); REQUIRE_EQ(result[2], std::vector({'c', 'd', 'a', 'b'})); REQUIRE_EQ(result[3], std::vector({'d', 'a', 'b', 'c'})); } TEST_CASE("generate_test, rotations_left_with_empty") { const std::vector v = { }; auto result = fplus::rotations_left(v); REQUIRE(result.empty()); } TEST_CASE("generate_test, rotations_right") { const std::vector v = { 'a', 'b', 'c', 'd' }; auto result = fplus::rotations_right(v); REQUIRE_EQ(4u, result.size()); REQUIRE_EQ(result[0], std::vector({'a', 'b', 'c', 'd'})); REQUIRE_EQ(result[1], std::vector({'d', 'a', 'b', 'c'})); REQUIRE_EQ(result[2], std::vector({'c', 'd', 'a', 'b'})); REQUIRE_EQ(result[3], std::vector({'b', 'c', 'd', 'a'})); } TEST_CASE("generate_test, rotations_right_with_empty") { const std::vector v = { }; auto result = fplus::rotations_right(v); REQUIRE(result.empty()); } TEST_CASE("generate_test, fill_left") { const std::vector v = { 1, 2, 3, 4 }; auto result = fplus::fill_left(0, 6, v); REQUIRE_EQ(result, std::vector({0, 0, 1, 2, 3, 4})); } TEST_CASE("generate_test, fill_left_with_min_size") { const std::vector v = { 1, 2, 3, 4 }; auto result = fplus::fill_left(0, 4, v); REQUIRE_EQ(result, std::vector({1, 2, 3, 4})); } TEST_CASE("generate_test, fill_right") { const std::vector v = { 1, 2, 3, 4 }; auto result = fplus::fill_right(0, 6, v); REQUIRE_EQ(result, std::vector({1, 2, 3, 4, 0, 0})); } TEST_CASE("generate_test, fill_right_with_min_size") { const std::vector v = { 1, 2, 3, 4 }; auto result = fplus::fill_right(0, 4, v); REQUIRE_EQ(result, std::vector({1, 2, 3, 4})); } TEST_CASE("generate_test, inits") { const std::vector v = { 0, 1, 2, 3 }; auto result = fplus::inits(v); REQUIRE_EQ(5u, result.size()); REQUIRE_EQ(result[0], std::vector()); REQUIRE_EQ(result[1], std::vector({0})); REQUIRE_EQ(result[2], std::vector({0, 1})); REQUIRE_EQ(result[3], std::vector({0, 1, 2})); REQUIRE_EQ(result[4], std::vector({0, 1, 2, 3})); } TEST_CASE("generate_test, tails") { const std::vector v = { 0, 1, 2, 3 }; auto result = fplus::tails(v); REQUIRE_EQ(5u, result.size()); REQUIRE_EQ(result[0], std::vector({0, 1, 2, 3})); REQUIRE_EQ(result[1], std::vector({1, 2, 3})); REQUIRE_EQ(result[2], std::vector({2, 3})); REQUIRE_EQ(result[3], std::vector({3})); REQUIRE(result[4].empty()); } TEST_CASE("generate_test, inner_product") { const std::vector xs = { 1, 2, 3 }; const std::vector ys = { 4, 5, 6 }; const auto plus = [](int x, int y) { return x + y; }; const auto mult = [](int x, int y) { return x * y; }; REQUIRE_EQ(fplus::inner_product(0, xs, ys), 32); REQUIRE_EQ(fplus::inner_product_with(plus, mult, 0, xs, ys), 32); REQUIRE_EQ(fplus::inner_product_with(std::plus<>{}, std::multiplies<>{}, 0, xs, ys), 32); } TEST_CASE("generate_test, numbers") { typedef std::vector ints; REQUIRE_EQ(fplus::numbers(2, 5), ints({2,3,4})); REQUIRE_EQ(fplus::numbers_step(0, 6, 2), ints({0,2,4})); } libfplus-0.2.13/test/get_locale.hpp000066400000000000000000000004671376322245400172220ustar00rootroot00000000000000#include template std::locale get_locale(T &&name) try { return std::locale(name); } catch (const std::runtime_error &e) { std::stringstream s; s << "Couldn't acquire locale: " << e.what() << ". Is '" << name << "' supported on your system?"; FAIL(s.str()); throw; } libfplus-0.2.13/test/interpolate_test.cpp000066400000000000000000000026261376322245400205030ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { typedef std::vector FloatVector; FloatVector xs = {1.0, 2.0, 6.0}; } TEST_CASE("interpolate_test, interpolate_linear") { using namespace fplus; REQUIRE(is_in_interval_around(0.0001, 1.0, elem_at_float_idx(-1.7, xs))); REQUIRE(is_in_interval_around(0.0001, 1.0, elem_at_float_idx(-0.2, xs))); REQUIRE(is_in_interval_around(0.0001, 1.0, elem_at_float_idx( 0.0, xs))); REQUIRE(is_in_interval_around(0.0001, 1.2, elem_at_float_idx( 0.2, xs))); REQUIRE(is_in_interval_around(0.0001, 1.7, elem_at_float_idx( 0.7, xs))); REQUIRE(is_in_interval_around(0.0001, 2.0, elem_at_float_idx( 1.0, xs))); REQUIRE(is_in_interval_around(0.0001, 4.0, elem_at_float_idx( 1.5, xs))); REQUIRE(is_in_interval_around(0.0001, 5.6, elem_at_float_idx( 1.9, xs))); REQUIRE(is_in_interval_around(0.0001, 6.0, elem_at_float_idx( 2.0, xs))); REQUIRE(is_in_interval_around(0.0001, 6.0, elem_at_float_idx( 2.1, xs))); REQUIRE(is_in_interval_around(0.0001, 6.0, elem_at_float_idx( 2.8, xs))); } libfplus-0.2.13/test/invoke_test.cpp000066400000000000000000000476341376322245400174600ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include #include #include #include #include using namespace fplus; #if defined(_MSC_VER) && (_MSC_VER < 1910) // only one test fails to compile with MSVC 2015, all tests pass with MSVC 2017 #define FPLUS_MSVC2015_BYPASS_FAILING_TESTS #endif namespace { template struct identity { using type = T; }; template auto cv_qualifiers(identity) { return std::tuple, identity, identity, identity>{}; } template auto ref_qualifiers(identity) { return std::tuple, identity>{}; } template auto ref_qualifiers(identity) { return std::tuple, identity>{}; } template auto ref_qualifiers(identity) { return std::tuple, identity>{}; } template auto ref_qualifiers(identity) { return std::tuple, identity>{}; } template auto ref_qualifiers(identity) { return std::tuple, identity>{}; } template auto all_qualifiers(identity f) { auto a = cv_qualifiers(f); // make_index_sequence would work, but no need auto b = ref_qualifiers(std::get<0>(a)); auto c = ref_qualifiers(std::get<1>(a)); auto d = ref_qualifiers(std::get<2>(a)); auto e = ref_qualifiers(std::get<3>(a)); return std::make_tuple(std::get<0>(b), std::get<1>(b), std::get<0>(c), std::get<1>(c), std::get<0>(d), std::get<1>(d), std::get<0>(e), std::get<1>(e)); } template struct all_invocable; template struct all_invocable, Class, FuncArgs...> { using Tuple = std::tuple; // two ::type, becaus of identity wrapper template using Elem = typename std::tuple_element::type::type; static_assert(sizeof...(TupleArgs) == 8, "all_invocable applies to each cv-ref qualified overloads"); // Class& because `&` functions can only be invokej on lvalue references static constexpr bool value = internal::conjunction< internal::is_invocable, Class&, FuncArgs...>, internal::is_invocable, Class, FuncArgs...>, internal::is_invocable, Class&, FuncArgs...>, internal::is_invocable, Class, FuncArgs...>, internal::is_invocable, Class&, FuncArgs...>, internal::is_invocable, Class, FuncArgs...>, internal::is_invocable, Class&, FuncArgs...>, internal::is_invocable, Class, FuncArgs...>>::value; }; } namespace { int regular_function_sum(int a, int b) { return a + b; } template auto return_n_arg_type(Args&&... args) -> typename std::tuple_element>::type { return std::get(std::forward_as_tuple(std::forward(args)...)); } struct function_object_t { int i = 0; int operator()(int a, int b) const { return a + b; } void mutate_data() { i = 0; } }; struct derived_function_object_t : function_object_t { }; } TEST_CASE("regular function") { using regular_function_t = decltype(regular_function_sum); using regular_function_ptr_t = std::add_pointer::type; // implicit conversions work static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); REQUIRE_EQ(internal::invoke(regular_function_sum, 32, 10), 42); } TEST_CASE("regular variadic function") { int i = 42; using variadic_function_t = decltype(return_n_arg_type<0, int&, float>); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); REQUIRE_EQ(std::addressof(internal::invoke(return_n_arg_type<0, int&, float>, i, 2.0f)), std::addressof(i)); } TEST_CASE("function object") { static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); REQUIRE_EQ(internal::invoke(function_object_t{}, 40, 2), 42); } TEST_CASE("lambda") { auto add = [](int a, int b) { return a + b; }; using lambda_t = decltype(add); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); REQUIRE_EQ(internal::invoke(add, 40, 2), 42); } TEST_CASE("member function - object reference") { using call_operator_t = decltype(&function_object_t::operator()); using mutate_data_t = decltype(&function_object_t::mutate_data); auto qualifiers = all_qualifiers(identity{}); static_assert(all_invocable::value, ""); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // non-const member function static_assert(internal::is_invocable::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); auto adder = function_object_t{}; REQUIRE_EQ(internal::invoke(&function_object_t::operator(), adder, 40, 2), 42); } TEST_CASE("member function - reference_wrapper") { using call_operator_t = decltype(&function_object_t::operator()); using mutate_data_t = decltype(&function_object_t::mutate_data); using ref_wrapper_t = std::reference_wrapper; using ref_wrapper_const_t = std::reference_wrapper; static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); static_assert(internal::is_invocable_r::value, ""); // non-const member function static_assert(internal::is_invocable::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); auto adder = function_object_t{}; REQUIRE_EQ(internal::invoke(&function_object_t::operator(), std::ref(adder), 40, 2), 42); REQUIRE_EQ(internal::invoke(&function_object_t::operator(), std::cref(adder), 40, 2), 42); } TEST_CASE("member function - object pointer") { using call_operator_t = decltype(&function_object_t::operator()); using mutate_data_t = decltype(&function_object_t::mutate_data); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // non-const member function static_assert(internal::is_invocable::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); auto adder = function_object_t{}; REQUIRE_EQ(internal::invoke(&function_object_t::operator(), &adder, 40, 2), 42); } TEST_CASE("member function - derived object reference") { using call_operator_t = decltype(&function_object_t::operator()); using mutate_data_t = decltype(&function_object_t::mutate_data); // should split all_qualifiers to get specific ones, right now it cannot // be used to test const objects and reference_wrapper. // Need to add make_index_sequence to do that properly. auto qualifiers = all_qualifiers(identity{}); #if !defined(FPLUS_MSVC2015_BYPASS_FAILING_TESTS) // Error C2338 under MSVC (i.e static_assert fail) static_assert(all_invocable::value, ""); #endif static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // non-const member function static_assert(internal::is_invocable::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); auto adder = derived_function_object_t{}; REQUIRE_EQ(internal::invoke(&function_object_t::operator(), adder, 40, 2), 42); } TEST_CASE("member function - reference_wrapper") { using call_operator_t = decltype(&function_object_t::operator()); using mutate_data_t = decltype(&function_object_t::mutate_data); using ref_wrapper_t = std::reference_wrapper; using ref_wrapper_const_t = std::reference_wrapper; static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // non-const member function static_assert(internal::is_invocable::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); auto adder = derived_function_object_t{}; REQUIRE_EQ(internal::invoke(&function_object_t::operator(), adder, 40, 2), 42); } TEST_CASE("member function - derived object pointer") { using call_operator_t = decltype(&function_object_t::operator()); using mutate_data_t = decltype(&function_object_t::mutate_data); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // non-const non-volatile member function static_assert(internal::is_invocable::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); auto adder = derived_function_object_t{}; REQUIRE_EQ(internal::invoke(&function_object_t::operator(), &adder, 40, 2), 42); } TEST_CASE("member data - object reference") { using member_data_t = decltype(&function_object_t::i); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // cannot convert lvalue ref to rvalue-reference static_assert(!internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable::value, ""); auto obj = function_object_t{}; obj.i = 42; REQUIRE_EQ(internal::invoke(&function_object_t::i, obj), 42); } TEST_CASE("member data - reference_wrapper") { using member_data_t = decltype(&function_object_t::i); using ref_wrapper_t = std::reference_wrapper; using ref_wrapper_const_t = std::reference_wrapper; static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // cannot convert lvalue ref to rvalue-reference static_assert(!internal::is_invocable_r::value, ""); // nor from const lvalue reference to non-const lvalue reference static_assert(!internal::is_invocable_r::value, ""); auto obj = function_object_t{}; obj.i = 42; REQUIRE_EQ(internal::invoke(&function_object_t::i, std::ref(obj)), 42); REQUIRE_EQ(internal::invoke(&function_object_t::i, std::cref(obj)), 42); } TEST_CASE("member data - object pointer") { using member_data_t = decltype(&function_object_t::i); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // cannot convert lvalue ref to rvalue-reference static_assert(!internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable::value, ""); auto obj = function_object_t{}; obj.i = 42; REQUIRE_EQ(internal::invoke(&function_object_t::i, &obj), 42); } TEST_CASE("member data - derived object reference") { using member_data_t = decltype(&function_object_t::i); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // cannot convert lvalue ref to rvalue-reference static_assert(!internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable::value, ""); auto obj = derived_function_object_t{}; obj.i = 42; REQUIRE_EQ(internal::invoke(&function_object_t::i, obj), 42); } TEST_CASE("member data - reference_wrapper") { using member_data_t = decltype(&function_object_t::i); using ref_wrapper_t = std::reference_wrapper; using ref_wrapper_const_t = std::reference_wrapper; static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // cannot convert lvalue ref to rvalue-reference static_assert(!internal::is_invocable_r::value, ""); // nor from const lvalue reference to non-const lvalue reference static_assert(!internal::is_invocable_r::value, ""); auto obj = derived_function_object_t{}; obj.i = 42; REQUIRE_EQ(internal::invoke(&function_object_t::i, std::ref(obj)), 42); REQUIRE_EQ(internal::invoke(&function_object_t::i, std::cref(obj)), 42); } TEST_CASE("member data - derived object pointer") { using member_data_t = decltype(&function_object_t::i); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // cannot convert lvalue ref to rvalue-reference static_assert(!internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable_r::value, ""); static_assert(!internal::is_invocable::value, ""); auto obj = derived_function_object_t{}; obj.i = 42; REQUIRE_EQ(internal::invoke(&derived_function_object_t::i, &obj), 42); } #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif // __GNUC__ TEST_CASE("generic lambda") { auto add = [](auto a, auto b) { return a + b; }; using lambda_t = decltype(add); static_assert(internal::is_invocable::value, ""); static_assert(internal::is_invocable_r::value, ""); // compile error, static_assert doesn't trigger though // from cppreference: // // Formally, determines whether INVOKE(declval(), // declval()...) is well formed when treated as an unevaluated // operand, where INVOKE is the operation defined in Callable. // // This is indeed well-formed in the unevaluated context... // static_assert(!internal::is_invocable::value, ""); static_assert(!internal::is_invocable_r::value, ""); REQUIRE_EQ(internal::invoke(add, 40, 2), 42); } TEST_CASE("transparent function objects") { static_assert(internal::is_invocable, int, int>::value, ""); static_assert(internal::is_invocable, int, float>::value, ""); REQUIRE_EQ(internal::invoke(std::plus<>{}, 40, 2), 42); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif // __GNUC__ libfplus-0.2.13/test/maps_test.cpp000066400000000000000000000256521376322245400171210ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { auto is_even_int = [](int x){ return x % 2 == 0; }; auto is_bigger_than_3_int = [](int x){ return x > 3; }; typedef std::vector IntVector; } TEST_CASE("maps_test, transform_map_values") { using namespace fplus; typedef std::vector> StringIntPairs; StringIntPairs stringIntPairs = {{"a", 1}, {"a", 2}, {"b", 6}, {"a", 4}}; auto stringIntPairsAsMapGrouped = pairs_to_map_grouped(stringIntPairs); auto groupNameToMedianMap = transform_map_values(median>, stringIntPairsAsMapGrouped); typedef std::map StringIntMap; REQUIRE_EQ(groupNameToMedianMap, StringIntMap({{"a", 2}, {"b", 6}})); } TEST_CASE("maps_test, choose") { using namespace fplus; REQUIRE_EQ((choose({{1, 'a'}, {2, 'b'}}, 2)), just('b')); REQUIRE_EQ((choose({{1, 'a'}, {1, 'b'}}, 1)), nothing()); REQUIRE_EQ((choose({{1, 'a'}, {2, 'b'}}, 3)), nothing()); REQUIRE_EQ((choose({}, 2)), nothing()); } TEST_CASE("maps_test, choose_by") { using namespace fplus; REQUIRE_EQ((choose_by({{is_even_int, 'a'}, {is_bigger_than_3_int, 'b'}}, 2)), just('a')); REQUIRE_EQ((choose_by({{is_even_int, 'a'}, {is_bigger_than_3_int, 'b'}}, 5)), just('b')); REQUIRE_EQ((choose_by({{is_even_int, 'a'}, {is_bigger_than_3_int, 'b'}}, 1)), nothing()); REQUIRE_EQ((choose_by({{is_even_int, 'a'}, {is_bigger_than_3_int, 'b'}}, 4)), nothing()); REQUIRE_EQ((choose_by({}, 2)), nothing()); } TEST_CASE("maps_test, choose_lazy") { using namespace fplus; typedef std::function char_stub; const char_stub a_stub = []() -> char { return 'a'; }; const char_stub b_stub = []() -> char { return 'b'; }; REQUIRE_EQ((choose_lazy({{1, a_stub}, {2, b_stub}}, 2)), just('b')); REQUIRE_EQ((choose_lazy({{1, a_stub}, {1, b_stub}}, 1)), nothing()); REQUIRE_EQ((choose_lazy({{1, a_stub}, {2, b_stub}}, 3)), nothing()); REQUIRE_EQ((choose_lazy({}, 2)), nothing()); } TEST_CASE("maps_test, choose_by_lazy") { using namespace fplus; typedef std::function char_stub; const char_stub a_stub = []() -> char { return 'a'; }; const char_stub b_stub = []() -> char { return 'b'; }; REQUIRE_EQ((choose_by_lazy({{is_even_int, a_stub}, {is_bigger_than_3_int, b_stub}}, 2)), just('a')); REQUIRE_EQ((choose_by_lazy({{is_even_int, a_stub}, {is_bigger_than_3_int, b_stub}}, 5)), just('b')); REQUIRE_EQ((choose_by_lazy({{is_even_int, a_stub}, {is_bigger_than_3_int, b_stub}}, 1)), nothing()); REQUIRE_EQ((choose_by_lazy({{is_even_int, a_stub}, {is_bigger_than_3_int, b_stub}}, 4)), nothing()); REQUIRE_EQ((choose_by_lazy({}, 2)), nothing()); } TEST_CASE("maps_test, choose_def") { using namespace fplus; REQUIRE_EQ((choose_def('c', {{1, 'a'}, {2, 'b'}}, 2)), 'b'); REQUIRE_EQ((choose_def('c', {{1, 'a'}, {1, 'b'}}, 1)), 'c'); REQUIRE_EQ((choose_def('c', {{1, 'a'}, {2, 'b'}}, 3)), 'c'); REQUIRE_EQ((choose_def('c', {}, 2)), 'c'); } TEST_CASE("maps_test, choose_by_def") { using namespace fplus; REQUIRE_EQ((choose_by_def('c', {{is_even_int, 'a'}, {is_bigger_than_3_int, 'b'}}, 2)), 'a'); REQUIRE_EQ((choose_by_def('c', {{is_even_int, 'a'}, {is_bigger_than_3_int, 'b'}}, 5)), 'b'); REQUIRE_EQ((choose_by_def('c', {{is_even_int, 'a'}, {is_bigger_than_3_int, 'b'}}, 1)), 'c'); REQUIRE_EQ((choose_by_def('c', {{is_even_int, 'a'}, {is_bigger_than_3_int, 'b'}}, 4)), 'c'); REQUIRE_EQ((choose_by_def('c', {}, 2)), 'c'); } TEST_CASE("maps_test, choose_def_lazy") { using namespace fplus; typedef std::function char_stub; const char_stub a_stub = []() -> char { return 'a'; }; const char_stub b_stub = []() -> char { return 'b'; }; const char_stub c_stub = []() -> char { return 'c'; }; REQUIRE_EQ((choose_def_lazy(c_stub, {{1, a_stub}, {2, b_stub}}, 2)), 'b'); REQUIRE_EQ((choose_def_lazy(c_stub, {{1, a_stub}, {1, b_stub}}, 1)), 'c'); REQUIRE_EQ((choose_def_lazy(c_stub, {{1, a_stub}, {2, b_stub}}, 3)), 'c'); REQUIRE_EQ((choose_def_lazy(c_stub, {}, 2)), 'c'); } TEST_CASE("maps_test, choose_by_def_lazy") { using namespace fplus; typedef std::function char_stub; const char_stub a_stub = []() -> char { return 'a'; }; const char_stub b_stub = []() -> char { return 'b'; }; const char_stub c_stub = []() -> char { return 'c'; }; REQUIRE_EQ((choose_by_def_lazy(c_stub, {{is_even_int, a_stub}, {is_bigger_than_3_int, b_stub}}, 2)), 'a'); REQUIRE_EQ((choose_by_def_lazy(c_stub, {{is_even_int, a_stub}, {is_bigger_than_3_int, b_stub}}, 5)), 'b'); REQUIRE_EQ((choose_by_def_lazy(c_stub, {{is_even_int, a_stub}, {is_bigger_than_3_int, b_stub}}, 1)), 'c'); REQUIRE_EQ((choose_by_def_lazy(c_stub, {{is_even_int, a_stub}, {is_bigger_than_3_int, b_stub}}, 4)), 'c'); REQUIRE_EQ((choose_by_def_lazy(c_stub, {}, 2)), 'c'); } TEST_CASE("maps_test, map functions") { using namespace fplus; typedef std::map IntStringMap; typedef std::map StringIntMap; IntStringMap intStringMap = {{1, "2"}, {4, "53"}, {7, "21"}}; StringIntMap stringIntMap = {{ "2", 1}, { "53", 4}, { "21", 7}}; REQUIRE_EQ(swap_keys_and_values(intStringMap), stringIntMap); typedef std::vector StringVector; REQUIRE_EQ(get_map_keys(intStringMap), IntVector({1, 4, 7})); REQUIRE_EQ(get_map_values(intStringMap), StringVector({"2", "53", "21"})); typedef std::unordered_map IntStringUnorderedMap; typedef std::unordered_map StringIntUnorderedMap; IntStringUnorderedMap intStringUnorderedMap = { { 1, "2" },{ 4, "53" },{ 7, "21" } }; StringIntUnorderedMap stringIntUnorderedMapSwapped = { { "2", 1 },{ "53", 4 },{ "21", 7 } }; REQUIRE_EQ(swap_keys_and_values(intStringUnorderedMap), stringIntUnorderedMapSwapped); REQUIRE_EQ(convert_container(intStringMap), intStringUnorderedMap); REQUIRE_EQ(convert_container(intStringUnorderedMap), intStringMap); std::vector mapInts = { 1, 4, 7 }; std::vector mapStrings = { "2", "53", "21" }; REQUIRE_EQ(create_map(mapInts, mapStrings), intStringMap); REQUIRE_EQ(create_unordered_map(mapInts, mapStrings), intStringUnorderedMap); IntStringMap intsAsStringsMap = {{1, "1"}, {4, "4"}, {7, "7"}}; REQUIRE_EQ(create_map_with(show, mapInts), intsAsStringsMap); IntStringUnorderedMap intsAsStringsUnorderedMap = {{1, "1"}, {4, "4"}, {7, "7"}}; REQUIRE_EQ(create_unordered_map_with(show, mapInts), intsAsStringsUnorderedMap); const auto is_int_string_map_key_even = [&](const IntStringMap::value_type& p) -> bool { return is_even_int(p.first); }; REQUIRE_EQ(keep_if(is_int_string_map_key_even, IntStringMap({{4, "4"}, {7, "7"}})), IntStringMap({{4, "4"}})); REQUIRE_EQ(get_from_map(intStringMap, 1), just("2")); REQUIRE_EQ(get_from_map(intStringMap, 9), nothing()); REQUIRE_EQ(get_from_map_with_def(intStringMap, std::string("n/a"), 1), "2"); REQUIRE_EQ(get_from_map_with_def(intStringMap, std::string("n/a"), 9), "n/a"); REQUIRE_EQ(map_contains(intStringMap, 1), true); REQUIRE_EQ(map_contains(intStringMap, 9), false); REQUIRE_EQ(get_first_from_map(intStringMap, IntVector({4, 1})), just("53")); REQUIRE_EQ(get_first_from_map(intStringMap, IntVector({5, 1})), just("2")); REQUIRE_EQ(get_first_from_map(intStringMap, IntVector({5, 2})), nothing()); REQUIRE_EQ(get_first_from_map_with_def(intStringMap, std::string("n/a"), IntVector({4, 1})), "53"); REQUIRE_EQ(get_first_from_map_with_def(intStringMap, std::string("n/a"), IntVector({5, 1})), "2"); REQUIRE_EQ(get_first_from_map_with_def(intStringMap, std::string("n/a"), IntVector({5, 2})), "n/a"); IntStringMap union_map_1 = {{0, "a"}, {1, "b"}}; IntStringMap union_map_2 = {{0, "c"}, {2, "d"}}; IntStringMap union_map_res = {{0, "a"}, {1, "b"}, {2, "d"}}; IntStringMap union_map_with_res = {{0, "ac"}, {1, "b"}, {2, "d"}}; REQUIRE_EQ(map_union(union_map_1, union_map_2), union_map_res); REQUIRE_EQ(map_union_with(append, union_map_1, union_map_2), union_map_with_res); IntStringUnorderedMap union_umap_1 = {{0, "a"}, {1, "b"}}; IntStringUnorderedMap union_umap_2 = {{0, "c"}, {2, "d"}}; IntStringUnorderedMap union_umap_res = {{0, "a"}, {1, "b"}, {2, "d"}}; IntStringUnorderedMap union_umap_with_res = {{0, "ac"}, {1, "b"}, {2, "d"}}; REQUIRE_EQ(map_union(union_umap_1, union_umap_2), union_umap_res); REQUIRE_EQ(map_union_with(append, union_umap_1, union_umap_2), union_umap_with_res); typedef std::map CharIntMap; typedef std::map IntCharMap; CharIntMap charIntMap = {{'a', 1}, {'b', 2}, {'A', 3}, {'C', 4}}; IntCharMap intCharMap = {{1, 'a'}, {2, 'b'}, {3, 'A'}, {4, 'C'}}; const auto is_upper = [](std::string::value_type c) -> bool { return (std::isupper(c) != 0); }; const auto is_lower = [](std::string::value_type c) -> bool { return (std::islower(c) != 0); }; REQUIRE_EQ(map_keep_if(is_upper, charIntMap), CharIntMap({{'A', 3}, {'C', 4}})); REQUIRE_EQ(map_drop_if(is_lower, charIntMap), CharIntMap({{'A', 3}, {'C', 4}})); typedef std::vector CharVector; REQUIRE_EQ(map_keep(CharVector({'b', 'F'}), charIntMap), CharIntMap({{'b', 2}})); REQUIRE_EQ(map_drop(CharVector({'a', 'A', 'C', 'F'}), charIntMap), CharIntMap({{'b', 2}})); REQUIRE_EQ(map_keep_if_value(is_upper, intCharMap), IntCharMap({{3, 'A'}, {4, 'C'}})); REQUIRE_EQ(map_drop_if_value(is_lower, intCharMap), IntCharMap({{3, 'A'}, {4, 'C'}})); REQUIRE_EQ(map_keep_values(CharVector({'b', 'F'}), intCharMap), IntCharMap({{2, 'b'}})); REQUIRE_EQ(map_drop_values(CharVector({'a', 'A', 'C', 'F'}), intCharMap), IntCharMap({{2, 'b'}})); typedef std::vector CharIntMaps; typedef std::vector> MaybeInts; REQUIRE_EQ( map_pluck('a', CharIntMaps({{{'a',1}, {'b',2}}, {{'a',3}}, {{'c',4}}})), MaybeInts({1, 3, {}})); }libfplus-0.2.13/test/maybe_test.cpp000066400000000000000000000210341376322245400172440ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { auto sqrtToMaybe = [](auto x) { return x < 0.0f ? fplus::nothing() : fplus::just(static_cast(sqrt(static_cast(x)))); }; auto sqrtToMaybeInt = [](auto x) { return x < 0 ? fplus::nothing() : fplus::just(fplus::round(sqrt(static_cast(x)))); }; float IntToFloat(const int& x) { return static_cast(x); } typedef std::vector> IntMaybes; typedef std::vector Ints; } struct foo { explicit foo(int) { msgs_.push_back("ctor"); } foo(const foo&) { msgs_.push_back("copyctor"); } foo(foo&&) noexcept { msgs_.push_back("movector"); } foo& operator = (const foo&) { msgs_.push_back("assignment"); return *this; } foo& operator = (foo&&) { msgs_.push_back("moveassignment"); return *this; } ~foo() { msgs_.push_back("dtor"); } static std::vector msgs_; }; std::vector foo::msgs_; TEST_CASE("maybe_test, construction and assignment") { using namespace fplus; foo::msgs_.clear(); REQUIRE_EQ(maybe(4), just(4)); typedef std::vector Strings; REQUIRE_EQ(foo::msgs_, Strings({})); maybe no_foo; REQUIRE_EQ(foo::msgs_, Strings({})); { foo foo_source = foo(1); maybe a_foo(foo_source); REQUIRE_EQ(foo::msgs_, Strings({"ctor", "copyctor"})); } REQUIRE_EQ(foo::msgs_, Strings({"ctor", "copyctor", "dtor", "dtor"})); foo::msgs_.clear(); } TEST_CASE("maybe_test, move semantics") { using namespace fplus; foo::msgs_.clear(); typedef std::vector Strings; maybe foo_a(foo(1)); maybe foo_b(foo(2)); maybe foo_z(std::move(foo_a)); foo_z = std::move(foo_b); REQUIRE_EQ(foo::msgs_, Strings({ "ctor", "movector", "dtor", "ctor", "movector", "dtor", "movector", "dtor", "movector"})); foo::msgs_.clear(); } TEST_CASE("maybe_test, unsafe_get_just") { using namespace fplus; fplus::maybe m(4); REQUIRE_EQ(m.unsafe_get_just(), 4); m.unsafe_get_just() += 1; REQUIRE_EQ(m.unsafe_get_just(), 5); } TEST_CASE("maybe_test, as_just_if") { using namespace fplus; REQUIRE_EQ(as_just_if(is_even, 4), just(4)); REQUIRE_EQ(as_just_if(is_even, 5), nothing()); } TEST_CASE("maybe_test, sequence") { using namespace fplus; REQUIRE_EQ(maybe_to_seq(just(4)), std::vector(1, 4)); REQUIRE_EQ(maybe_to_seq(nothing()), std::vector()); REQUIRE_EQ(singleton_seq_as_maybe(std::vector()), nothing()); REQUIRE_EQ(singleton_seq_as_maybe(std::vector(1, 4)), just(4)); REQUIRE_EQ(singleton_seq_as_maybe(std::vector(2, 4)), nothing()); } TEST_CASE("maybe_test, just_with_default") { using namespace fplus; auto x = just(2); maybe y = nothing(); auto Or42 = bind_1st_of_2(just_with_default, 42); REQUIRE_EQ(Or42(x), 2); REQUIRE_EQ(Or42(y), 42); } TEST_CASE("maybe_test, lift") { using namespace fplus; auto x = just(2); maybe y = nothing(); auto squareGeneric = [](auto n) { return n * n; }; REQUIRE_EQ(lift_maybe(square, x), just(4)); REQUIRE_EQ(lift_maybe(square, y), nothing()); REQUIRE_EQ(lift_maybe(squareGeneric, x), just(4)); REQUIRE_EQ(lift_maybe(squareGeneric, y), nothing()); auto SquareAndSquare = compose(square, square); REQUIRE_EQ(lift_maybe(SquareAndSquare, x), just(16)); REQUIRE_EQ(lift_maybe_def(3, square, x), 4); REQUIRE_EQ(lift_maybe_def(3, square, y), 3); REQUIRE_EQ(lift_maybe_def(3, squareGeneric, x), 4); REQUIRE_EQ(lift_maybe_def(3, squareGeneric, y), 3); REQUIRE_EQ(lift_maybe_2(std::plus(), x, x), just(4)); REQUIRE_EQ(lift_maybe_2(std::plus(), x, y), y); REQUIRE_EQ(lift_maybe_2(std::plus<>(), y, x), y); REQUIRE_EQ(lift_maybe_2(std::plus<>(), y, y), y); REQUIRE_EQ(lift_maybe_2_def(3, std::plus(), x, x), 4); REQUIRE_EQ(lift_maybe_2_def(3, std::plus(), x, y), 3); REQUIRE_EQ(lift_maybe_2_def(3, std::plus<>(), y, x), 3); REQUIRE_EQ(lift_maybe_2_def(3, std::plus<>(), y, y), 3); } TEST_CASE("maybe_test, join_maybe") { using namespace fplus; REQUIRE_EQ(join_maybe(just(just(2))), just(2)); REQUIRE_EQ(join_maybe(just(nothing())), nothing()); REQUIRE_EQ(join_maybe(nothing>()), nothing()); } TEST_CASE("maybe_test, and_then_maybe") { using namespace fplus; REQUIRE_EQ(and_then_maybe(sqrtToMaybeInt, just(4)), just(2)); REQUIRE_EQ(and_then_maybe(sqrtToMaybeInt, nothing()), nothing()); const auto string_to_maybe_int = [](const auto& str) { if (str == "42") return just(42); else return nothing(); }; REQUIRE_EQ(and_then_maybe(string_to_maybe_int, just("3")), nothing()); REQUIRE_EQ(and_then_maybe(string_to_maybe_int, just("42")), just(42)); REQUIRE_EQ(and_then_maybe(string_to_maybe_int, nothing()), nothing()); } TEST_CASE("maybe_test, compose") { using namespace fplus; auto sqrtAndSqrt = compose_maybe(sqrtToMaybe, sqrtToMaybe); auto sqrtIntAndSqrtIntAndSqrtInt = compose_maybe(sqrtToMaybeInt, sqrtToMaybeInt, sqrtToMaybeInt); REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtInt(256), just(2)); auto sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt = compose_maybe(sqrtToMaybeInt, sqrtToMaybeInt, sqrtToMaybeInt, sqrtToMaybeInt); REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt(65536), just(2)); auto LiftedIntToFloat = [](const maybe& m) -> maybe { return lift_maybe(IntToFloat, m); }; auto JustInt = just; auto IntToMaybeFloat = compose(JustInt, LiftedIntToFloat); auto IntToFloatAndSqrtAndSqrt = compose_maybe(IntToMaybeFloat, sqrtAndSqrt); auto squareMaybe = [](auto n) { return just(n * n); }; auto plusMaybe = [](auto m, auto n) { return just(m + n); }; REQUIRE_EQ(compose_maybe(plusMaybe, squareMaybe)(2, 3), just(25)); REQUIRE(is_in_interval(1.41f, 1.42f, unsafe_get_just (IntToFloatAndSqrtAndSqrt(4)))); } TEST_CASE("maybe_test, equality") { using namespace fplus; IntMaybes maybes = {just(1), nothing(), just(2)}; REQUIRE(justs(maybes) == Ints({ 1,2 })); REQUIRE(just(1) == just(1)); REQUIRE(just(1) != just(2)); REQUIRE(just(1) != nothing()); REQUIRE(nothing() == nothing()); } TEST_CASE("maybe_test, transform_and_keep_justs") { using namespace fplus; Ints wholeNumbers = { -3, 4, 16, -1 }; REQUIRE_EQ(transform_and_keep_justs(sqrtToMaybeInt, wholeNumbers) , Ints({2,4})); REQUIRE_EQ(transform_and_concat( bind_1st_of_2(replicate, std::size_t(3)), Ints{ 1,2 }) , Ints({ 1,1,1,2,2,2 })); } TEST_CASE("maybe_test, show_maybe") { using namespace fplus; REQUIRE_EQ(show_maybe(just(42)), std::string("Just 42")); REQUIRE_EQ(show_maybe(nothing()), std::string("Nothing")); } TEST_CASE("maybe_test, exceptions") { using namespace fplus; std::string thrown_str; try { throw_on_nothing(std::invalid_argument("raised"), nothing()); } catch (const std::exception& e) { thrown_str = e.what(); } REQUIRE_EQ(thrown_str, std::string("raised")); } TEST_CASE("maybe_test, copy") { using namespace fplus; maybe maybe_4(4); maybe maybe_4_copy(maybe_4); maybe maybe_4_copy_2; maybe_4_copy_2 = maybe_4_copy; REQUIRE_EQ(maybe_4_copy_2, just(4)); } TEST_CASE("maybe_test, flatten") { using namespace fplus; maybe maybe_int_nothing; maybe maybe_int_nothing_copy(maybe_int_nothing); maybe maybe_int_nothing_copy_2; maybe_int_nothing_copy_2 = maybe_int_nothing_copy; REQUIRE_EQ(maybe_int_nothing_copy_2, nothing()); REQUIRE_EQ(flatten_maybe(maybe>(maybe(1))), maybe(1)); REQUIRE_EQ(flatten_maybe(maybe>(maybe())), nothing()); REQUIRE_EQ(flatten_maybe(maybe>()), nothing()); } libfplus-0.2.13/test/numeric_test.cpp000066400000000000000000000473731376322245400176270ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { int mod2(int x) { return x % 2; } int mod7(int x) { return x % 7; } std::size_t string_length(const std::string& s) { return s.size(); } typedef std::vector Ints; typedef std::vector Floats; typedef std::vector Doubles; } TEST_CASE("numeric_test, is_in_interval") { REQUIRE(fplus::is_in_interval(1, 3, 1)); REQUIRE(fplus::is_in_interval(1, 3, 2)); REQUIRE_FALSE(fplus::is_in_interval(1, 3, 0)); REQUIRE_FALSE(fplus::is_in_interval(1, 3, 3)); REQUIRE(fplus::is_in_closed_interval(1, 3, 1)); REQUIRE(fplus::is_in_closed_interval(1, 3, 2)); REQUIRE(fplus::is_in_closed_interval(1, 3, 3)); REQUIRE_FALSE(fplus::is_in_open_interval(1, 3, 1)); REQUIRE(fplus::is_in_open_interval(1, 3, 2)); REQUIRE_FALSE(fplus::is_in_open_interval(1, 3, 3)); REQUIRE(fplus::is_in_interval(0.09, 0.11, fplus::abs(-0.1))); REQUIRE(fplus::is_in_interval(0.09, 0.11, fplus::abs( 0.1))); } TEST_CASE("numeric_test, is_in_interval_around") { REQUIRE_FALSE(fplus::is_in_interval_around(0.1, 2.0, 1.85)); REQUIRE(fplus::is_in_interval_around(0.1, 2.0, 1.95)); REQUIRE(fplus::is_in_interval_around(0.1, 2.0, 2.05)); REQUIRE_FALSE(fplus::is_in_interval_around(0.1, 2.0, 2.15)); REQUIRE(fplus::is_in_interval_around(1, 2, 1)); REQUIRE_FALSE(fplus::is_in_interval_around(1, 2, 3)); } TEST_CASE("numeric_test, is_in_open_interval_around") { REQUIRE_FALSE(fplus::is_in_open_interval_around(0.1, 2.0, 1.85)); REQUIRE(fplus::is_in_open_interval_around(0.1, 2.0, 1.95)); REQUIRE(fplus::is_in_open_interval_around(0.1, 2.0, 2.05)); REQUIRE_FALSE(fplus::is_in_open_interval_around(0.1, 2.0, 2.15)); REQUIRE_FALSE(fplus::is_in_open_interval_around(1, 2, 1)); REQUIRE_FALSE(fplus::is_in_open_interval_around(1, 2, 3)); } TEST_CASE("numeric_test, is_in_closed_interval_around") { REQUIRE_FALSE(fplus::is_in_closed_interval_around(0.1, 2.0, 1.85)); REQUIRE(fplus::is_in_closed_interval_around(0.1, 2.0, 1.95)); REQUIRE(fplus::is_in_closed_interval_around(0.1, 2.0, 2.05)); REQUIRE_FALSE(fplus::is_in_closed_interval_around(0.1, 2.0, 2.15)); REQUIRE(fplus::is_in_closed_interval_around(1, 2, 1)); REQUIRE(fplus::is_in_closed_interval_around(1, 2, 3)); } TEST_CASE("numeric_test, is_negative") { REQUIRE(fplus::is_negative(-0.1)); REQUIRE_FALSE(fplus::is_negative(0.1)); } TEST_CASE("numeric_test, is_positive") { REQUIRE(fplus::is_positive(0.1)); REQUIRE_FALSE(fplus::is_positive(-0.1)); } TEST_CASE("numeric_test, sign") { REQUIRE_EQ(fplus::sign(0.1), 1); REQUIRE_EQ(fplus::sign(-0.1), -1); } TEST_CASE("numeric_test, abs_diff") { REQUIRE_EQ(fplus::abs_diff(3, 5), 2); REQUIRE_EQ(fplus::abs_diff(5, 3), 2); REQUIRE_EQ(fplus::abs_diff(3, 5), 2); REQUIRE_EQ(fplus::abs_diff(5, 3), 2); } TEST_CASE("numeric_test, cyclic_value") { using namespace fplus; REQUIRE_EQ(cyclic_value(8.0)(3), 3); REQUIRE_EQ(cyclic_value(8.0)(11), 3); REQUIRE_EQ(cyclic_value(8.0)(19), 3); REQUIRE_EQ(cyclic_value(8.0)(-2), 6); REQUIRE_EQ(cyclic_value(8.0)(-5), 3); REQUIRE_EQ(cyclic_value(8.0)(-13), 3); REQUIRE_EQ(cyclic_value(8)(3), 3); REQUIRE_EQ(cyclic_value(8)(11), 3); REQUIRE_EQ(cyclic_value(8)(19), 3); REQUIRE_EQ(cyclic_value(8)(-2), 6); REQUIRE_EQ(cyclic_value(8)(-5), 3); REQUIRE_EQ(cyclic_value(8)(-13), 3); REQUIRE_EQ(cyclic_value(8)(3), 3); REQUIRE_EQ(cyclic_value(8)(11), 3); REQUIRE_EQ(cyclic_value(8)(19), 3); REQUIRE(is_in_interval(3.19, 3.21, cyclic_value(8.1)(3.2))); } TEST_CASE("numeric_test, cyclic_difference") { using namespace fplus; REQUIRE_EQ(cyclic_difference(100)(5, 2), 3); REQUIRE_EQ(cyclic_difference(100)(2, 5), 97); REQUIRE_EQ(cyclic_difference(100)(3, -2), 5); REQUIRE_EQ(cyclic_difference(100)(-2, 3), 95); REQUIRE_EQ(cyclic_difference(100)(90, 10), 80); REQUIRE_EQ(cyclic_difference(100)(10, 90), 20); REQUIRE_EQ(cyclic_difference(100)(5, 2), 3); REQUIRE_EQ(cyclic_difference(100)(2, 5), 97); REQUIRE_EQ(cyclic_difference(100)(90, 10), 80); REQUIRE_EQ(cyclic_difference(100)(10, 90), 20); } TEST_CASE("numeric_test, cyclic_shortest_difference") { using namespace fplus; REQUIRE_EQ(cyclic_shortest_difference(100)(5, 2), 3); REQUIRE_EQ(cyclic_shortest_difference(100)(2, 5), -3); REQUIRE_EQ(cyclic_shortest_difference(100)(3, -2), 5); REQUIRE_EQ(cyclic_shortest_difference(100)(-2, 3), -5); REQUIRE_EQ(cyclic_shortest_difference(100)(90, 10), -20); REQUIRE_EQ(cyclic_shortest_difference(100)(10, 90), 20); } TEST_CASE("numeric_test, cyclic_distance") { using namespace fplus; REQUIRE_EQ(cyclic_distance(100)(2, 5), 3); REQUIRE_EQ(cyclic_distance(100)(5, 2), 3); REQUIRE_EQ(cyclic_distance(100)(-2, 3), 5); REQUIRE_EQ(cyclic_distance(100)(3, -2), 5); REQUIRE_EQ(cyclic_distance(100)(10, 90), 20); REQUIRE_EQ(cyclic_distance(100)(90, 10), 20); REQUIRE_EQ(cyclic_distance(100)(2, 5), 3); REQUIRE_EQ(cyclic_distance(100)(5, 2), 3); REQUIRE_EQ(cyclic_distance(100)(10, 90), 20); REQUIRE_EQ(cyclic_distance(100)(90, 10), 20); } TEST_CASE("numeric_test, round") { // using namespace fplus; // round is also defined in tgmath.h under msvc REQUIRE_EQ(round(1.4), 1); REQUIRE_EQ(round(1.5), 2); REQUIRE_EQ(round(1.6), 2); REQUIRE_EQ((fplus::round(254.9)), 255); REQUIRE_EQ((fplus::round(300.0)), 255); REQUIRE_EQ((fplus::round(-0.0000001)), 0); REQUIRE_EQ((fplus::round(-5.0)), 0); REQUIRE_EQ(fplus::round(-1.4), -1); REQUIRE_EQ(fplus::round(-1.6), -2); } TEST_CASE("numeric_test, integral_cast_clamp") { using namespace fplus; REQUIRE_EQ(integral_cast_clamp(std::int32_t(-1)), std::uint8_t(0)); REQUIRE_EQ(integral_cast_clamp(std::int32_t(0)), std::uint8_t(0)); REQUIRE_EQ(integral_cast_clamp(std::int32_t(3)), std::uint8_t(3)); REQUIRE_EQ(integral_cast_clamp(std::int32_t(255)), std::uint8_t(255)); REQUIRE_EQ(integral_cast_clamp(std::int32_t(256)), std::uint8_t(255)); REQUIRE_EQ(integral_cast_clamp(std::int32_t(-129)), std::int8_t(-128)); REQUIRE_EQ(integral_cast_clamp(std::int32_t(-128)), std::int8_t(-128)); REQUIRE_EQ(integral_cast_clamp(std::int32_t(3)), std::int8_t(3)); REQUIRE_EQ(integral_cast_clamp(std::int32_t(127)), std::int8_t(127)); REQUIRE_EQ(integral_cast_clamp(std::int32_t(128)), std::int8_t(127)); REQUIRE_EQ(integral_cast_clamp(std::uint8_t(0)), std::int8_t(0)); REQUIRE_EQ(integral_cast_clamp(std::uint8_t(127)), std::int8_t(127)); REQUIRE_EQ(integral_cast_clamp(std::uint8_t(128)), std::int8_t(127)); REQUIRE_EQ(integral_cast_clamp(std::numeric_limits::lowest()), static_cast(std::numeric_limits::lowest())); REQUIRE_EQ(integral_cast_clamp(std::uint16_t(0)), std::uint64_t(0)); REQUIRE_EQ(integral_cast_clamp(std::numeric_limits::max()), static_cast(std::numeric_limits::max())); REQUIRE_EQ(integral_cast_clamp(std::numeric_limits::lowest()), static_cast(std::numeric_limits::lowest())); REQUIRE_EQ(integral_cast_clamp(std::int16_t(0)), std::int64_t(0)); REQUIRE_EQ(integral_cast_clamp(std::numeric_limits::max()), static_cast(std::numeric_limits::max())); } TEST_CASE("numeric_test, ceil") { using namespace fplus; REQUIRE_EQ(ceil(1.4), 2); REQUIRE_EQ(ceil(-1.4), -1); } TEST_CASE("numeric_test, floor") { using namespace fplus; REQUIRE_EQ(floor(1.4), 1); REQUIRE_EQ(floor(-1.4), -2); } TEST_CASE("numeric_test, floor_to_int_mult") { using namespace fplus; REQUIRE_EQ(floor_to_int_mult(2, -3), -4); REQUIRE_EQ(floor_to_int_mult(2, -2), -2); REQUIRE_EQ(floor_to_int_mult(2, -1), -2); REQUIRE_EQ(floor_to_int_mult(2, 0), 0); REQUIRE_EQ(floor_to_int_mult(2, 1), 0); REQUIRE_EQ(floor_to_int_mult(2, 2), 2); REQUIRE_EQ(floor_to_int_mult(2, 3), 2); REQUIRE_EQ(floor_to_int_mult(2, 0), 0); REQUIRE_EQ(floor_to_int_mult(2, 1), 0); REQUIRE_EQ(floor_to_int_mult(2, 2), 2); REQUIRE_EQ(floor_to_int_mult(2, 3), 2); REQUIRE_EQ(floor_to_int_mult(1, -1), -1); REQUIRE_EQ(floor_to_int_mult(1, 0), 0); REQUIRE_EQ(floor_to_int_mult(1, 1), 1); } TEST_CASE("numeric_test, ceil_to_int_mult") { using namespace fplus; REQUIRE_EQ(ceil_to_int_mult(2, -3), -2); REQUIRE_EQ(ceil_to_int_mult(2, -2), -2); REQUIRE_EQ(ceil_to_int_mult(2, -1), -0); REQUIRE_EQ(ceil_to_int_mult(2, 0), 0); REQUIRE_EQ(ceil_to_int_mult(2, 1), 2); REQUIRE_EQ(ceil_to_int_mult(2, 2), 2); REQUIRE_EQ(ceil_to_int_mult(2, 3), 4); REQUIRE_EQ(ceil_to_int_mult(2, 0), 0); REQUIRE_EQ(ceil_to_int_mult(2, 1), 2); REQUIRE_EQ(ceil_to_int_mult(2, 2), 2); REQUIRE_EQ(ceil_to_int_mult(2, 3), 4); REQUIRE_EQ(ceil_to_int_mult(1, -1), -1); REQUIRE_EQ(ceil_to_int_mult(1, 0), 0); REQUIRE_EQ(ceil_to_int_mult(1, 1), 1); } TEST_CASE("numeric_test, reference_interval") { using namespace fplus; REQUIRE_EQ(reference_interval(2, 6, 0, 4, 3), 5); REQUIRE_EQ(reference_interval(2, 10, 0, 4, 3), 8); REQUIRE_EQ(reference_interval(2, 6, 0, 4, -1), 1); REQUIRE_EQ(reference_interval(2, 10, 0, 4, -1), 0); } TEST_CASE("numeric_test, clamp") { using namespace fplus; REQUIRE_EQ(clamp(2, 6, 5), 5); REQUIRE_EQ(clamp(2, 6, 1), 2); REQUIRE_EQ(clamp(2, 6, 8), 6); } TEST_CASE("numeric_test, int_power") { using namespace fplus; REQUIRE_EQ(int_power(3, 0), 1); REQUIRE_EQ(int_power(3, 1), 3); REQUIRE_EQ(int_power(3, 2), 9); REQUIRE_EQ(int_power(3, 3), 27); REQUIRE_EQ(int_power(3, 4), 81); } TEST_CASE("numeric_test, min_on") { REQUIRE_EQ(fplus::min_on(mod2)(4, 4), 4); REQUIRE_EQ(fplus::min_on(mod2)(4, 3), 4); REQUIRE_EQ(fplus::min_on(mod2)(4, 3, 7), 4); REQUIRE_EQ(fplus::min_on(mod2)(5, 3, 7), 5); REQUIRE_EQ(fplus::min_on(mod2)(5, 3, 7, 9, 2), 2); REQUIRE_EQ(fplus::min_on(mod2)(5, 3, 7, 13, 19, 4), 4); REQUIRE_EQ(fplus::min_on(mod7)(4, 4), 4); REQUIRE_EQ(fplus::min_on(mod7)(4, 3), 3); REQUIRE_EQ(fplus::min_on(mod7)(4, 3, 7), 7); REQUIRE_EQ(fplus::min_on(mod7)(5, 3, 7, 9, 9), 7); REQUIRE_EQ(fplus::min_on(mod7)(5, 3, 7, 9, 9, 6, 6, 6, 6, 6, 6), 7); REQUIRE_EQ(fplus::min_on(mod7)(70, 3, 7, 9, 9, 6, 6, 6, 6, 6, 6), 70); const std::string s1("AAA"); const std::string s2("AAABB"); const std::string s3("AAABBCCC"); REQUIRE_EQ(fplus::min_on(string_length)(s1, s2), s1); REQUIRE_EQ(fplus::min_on(string_length)(s2, s3), s2); REQUIRE_EQ(fplus::min_on(string_length)(s1, s2, s3), s1); REQUIRE_EQ(fplus::min_on(string_length)(s1, s3), s1); auto l1_min_on = fplus::min_on(mod7); REQUIRE_EQ(l1_min_on(1), 1); REQUIRE_EQ(l1_min_on(1, 2), 1); REQUIRE_EQ(l1_min_on(1, 2, 3, 7), 7); REQUIRE_EQ(l1_min_on(1, 2, 3, 6, 77), 77); REQUIRE_EQ(fplus::min_on(mod7)(1), 1); REQUIRE_EQ(fplus::min_on(mod7)(1, 2), 1); REQUIRE_EQ(fplus::min_on(mod7)(1, 2, 3, 7), 7); REQUIRE_EQ(fplus::min_on(mod7)(1, 2, 3, 6, 77), 77); } TEST_CASE("numeric_test, max_on") { REQUIRE_EQ(fplus::max_on(mod2)(4, 4), 4); REQUIRE_EQ(fplus::max_on(mod2)(4, 3), 3); REQUIRE_EQ(fplus::max_on(mod2)(4, 3, 7), 3); REQUIRE_EQ(fplus::max_on(mod2)(5, 3, 7), 5); REQUIRE_EQ(fplus::max_on(mod2)(5, 3, 7, 9, 2), 5); REQUIRE_EQ(fplus::max_on(mod2)(5, 3, 7, 13, 19, 4), 5); REQUIRE_EQ(fplus::max_on(mod7)(4, 4), 4); REQUIRE_EQ(fplus::max_on(mod7)(4, 3), 4); REQUIRE_EQ(fplus::max_on(mod7)(4, 3, 7), 4); REQUIRE_EQ(fplus::max_on(mod7)(5, 3, 7, 9, 9), 5); REQUIRE_EQ(fplus::max_on(mod7)(5, 3, 7, 9, 9, 6, 6, 6, 6, 6, 6), 6); REQUIRE_EQ(fplus::max_on(mod7)(70, 3, 7, 9, 9, 6, 6, 6, 6, 6, 6), 6); const std::string s1("AAA"); const std::string s2("AAABB"); const std::string s3("AAABBCCC"); REQUIRE_EQ(fplus::max_on(string_length)(s1, s2), s2); REQUIRE_EQ(fplus::max_on(string_length)(s2, s3), s3); REQUIRE_EQ(fplus::max_on(string_length)(s1, s2, s3), s3); REQUIRE_EQ(fplus::max_on(string_length)(s1, s3), s3); auto l1_max_on = fplus::max_on(mod7); REQUIRE_EQ(l1_max_on(1), 1); REQUIRE_EQ(l1_max_on(1, 2), 2); REQUIRE_EQ(l1_max_on(1, 2, 3, 7), 3); REQUIRE_EQ(l1_max_on(1, 2, 3, 6, 77), 6); REQUIRE_EQ(fplus::max_on(mod7)(1), 1); REQUIRE_EQ(fplus::max_on(mod7)(1, 2), 2); REQUIRE_EQ(fplus::max_on(mod7)(1, 2, 3, 7), 3); REQUIRE_EQ(fplus::max_on(mod7)(1, 2, 3, 6, 77), 6); } TEST_CASE("numeric_test, mean") { using namespace fplus; Ints xs = {1,4,4}; REQUIRE_EQ(mean(xs), 3); } TEST_CASE("numeric_test, mean_obj") { using namespace fplus; struct vec_2d { double x; double y; vec_2d operator + (const vec_2d& rhs) const { return {x + rhs.x, y + rhs.y}; }; vec_2d operator / (double scalar) const { double scalar_d = static_cast(scalar); return {x / scalar_d, y / scalar_d}; }; vec_2d operator / (std::size_t scalar) const { return *this / static_cast(scalar); }; }; auto vec_2d_length_squared = [](const vec_2d& v) -> double { return v.x * v.x + v.y * v.y; }; std::vector vecs = {{1,1}, {3,3}}; auto mean_vec_div_double = mean_obj_div_double(vecs); auto mean_vec_div_size_t = mean_obj_div_size_t(vecs); double mean_vec_length_squared_dest = 2*2 + 2*2; REQUIRE(is_in_interval_around(0.001, mean_vec_length_squared_dest, vec_2d_length_squared(mean_vec_div_double))); REQUIRE(is_in_interval_around(0.001, mean_vec_length_squared_dest, vec_2d_length_squared(mean_vec_div_size_t))); } TEST_CASE("numeric_test, variadic") { using namespace fplus; REQUIRE_EQ(min(1,2,3,4,5), 1); REQUIRE_EQ(min(1.01,1.02,1.03,1.04,1.05), 1.01); REQUIRE_EQ(min(-54,2,3,54,5), -54); REQUIRE_EQ(min(-54.2,2.7,3,54,5), -54.2); REQUIRE_EQ(min(123,123,123,124), 123); REQUIRE_EQ(min(123), 123); REQUIRE_EQ(min(123,123), 123); REQUIRE_EQ(min(123,123,123), 123); REQUIRE_EQ(min(-1), -1); REQUIRE_EQ(min(-1,-2), -2); REQUIRE_EQ(min(-1,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), -2); REQUIRE_EQ(min('a','b','c'), 'a'); REQUIRE_EQ(max(1,2,3,4,5), 5); REQUIRE_EQ(max(1.01,1.02,1.03,1.04,1.05), 1.05); REQUIRE_EQ(max(-54,2,3,54,5), 54); REQUIRE_EQ(max(-54.2,2.7,3,54.85,5), 54.85); REQUIRE_EQ(max(123,123,123,124), 124); REQUIRE_EQ(max(123), 123); REQUIRE_EQ(max(123,123), 123); REQUIRE_EQ(max(123,123,123), 123); REQUIRE_EQ(max(123,123,123,123), 123); REQUIRE_EQ(max(123,123,123,123,123), 123); REQUIRE_EQ(max(123,123,123,123,123,123), 123); REQUIRE_EQ(max(-1), -1); REQUIRE_EQ(max(-1,-2), -1); REQUIRE_EQ(max(-1,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 0); REQUIRE_EQ(max('a','b','c'), 'c'); REQUIRE_EQ(max_2(2, 3), 3); REQUIRE_EQ(min_2(2, 3), 2); } TEST_CASE("numeric_test, normalize") { using namespace fplus; REQUIRE_EQ(normalize_min_max(0.0, 10.0, Doubles({1, 3, 6})), Doubles({0, 4, 10})); REQUIRE_EQ(normalize_mean_stddev(3.0, 2.0, Doubles({7, 8})), Doubles({1, 5})); REQUIRE_EQ(standardize(Doubles({2, 6})), Doubles({-1, 1})); REQUIRE_EQ(normalize_min_max(0.0f, 10.0f, Floats({1, 3, 6})), Floats({0, 4, 10})); REQUIRE_EQ(normalize_mean_stddev(3.0f, 2.0f, Floats({7, 8})), Floats({1, 5})); REQUIRE_EQ(standardize(Floats({2, 6})), Floats({-1, 1})); Floats xs1 = {1, 3, 6}; Floats xs2 = {7, 8}; Floats xs3 = {2.0, 6.0}; REQUIRE_EQ(normalize_min_max(0.0f, 10.0f, xs1), Floats({0, 4, 10})); REQUIRE_EQ(normalize_mean_stddev(3.0f, 2.0f, xs2), Floats({1, 5})); REQUIRE_EQ(standardize(xs3), Floats({-1, 1})); } TEST_CASE("numeric_test, winsorize") { using namespace fplus; REQUIRE_EQ(winsorize(0.1, Doubles()), Doubles()); REQUIRE_EQ(winsorize(0.1, Doubles({1})), Doubles({1})); REQUIRE_EQ(winsorize(0.4, Doubles({1,2})), Doubles({1,2})); REQUIRE_EQ(winsorize(0.32, Doubles({1,2,3})), Doubles({1,2,3})); REQUIRE_EQ(winsorize(0.34, Doubles({1,2,3})), Doubles({2,2,2})); REQUIRE_EQ(winsorize(0.1, Doubles({1,3,4,4,4,4,4,4,6,8})), Doubles({3,3,4,4,4,4,4,4,6,6})); REQUIRE_EQ(winsorize(-0.1, Doubles({1,3,4,4,4,4,4,4,6,8})), Doubles({1,3,4,4,4,4,4,4,6,8})); REQUIRE_EQ(winsorize(0.1, Doubles({4,4,4,3,8,4,6,4,3,4})), Doubles({3,3,4,4,4,4,4,4,6,6})); REQUIRE_EQ(winsorize(0, Doubles({1,3,4,4,4,4,4,4,6,8})), Doubles({1,3,4,4,4,4,4,4,6,8})); const auto median_result = winsorize(0.6, Doubles({1,2})); REQUIRE_EQ(median_result.size(), 2); REQUIRE(fplus::is_in_interval_around(0.001, 1.5, median_result[0])); REQUIRE(fplus::is_in_interval_around(0.001, 1.5, median_result[1])); } TEST_CASE("numeric_test, histogram") { using namespace fplus; typedef std::vector ints; typedef std::pair interval; typedef std::vector intervals; typedef std::pair bin; typedef std::vector bins; const ints xs = {0,1,4,5,6,7,8,9}; const intervals intervals1 = {{0,4}, {4,5}, {6,8}}; const bins result1 = {{{0, 4}, 2}, {{4, 5}, 1}, {{6, 8}, 2}}; REQUIRE_EQ(histogram_using_intervals(intervals1, xs), result1); } TEST_CASE("numeric_test, generate_consecutive_intervals") { using namespace fplus; typedef std::pair interval; typedef std::vector intervals; const intervals result = {{0,2}, {2,4}, {4,6}, {6,8}}; REQUIRE_EQ(generate_consecutive_intervals(0, 2, 4), result); } TEST_CASE("numeric_test, histogram_intervals") { using namespace fplus; typedef std::vector ints; typedef std::pair bin; typedef std::vector bins; const ints xs = {0,1,4,5,7,8,9}; const bins result1 = {{1, 2}, {3, 0}, {5, 2}, {7, 1}}; REQUIRE_EQ(histogram(1, 2, 4, xs), result1); } TEST_CASE("numeric_test, modulo_chain") { using namespace fplus; typedef std::vector ints; REQUIRE_EQ(modulo_chain(ints({24, 60, 60}), 23), ints({0, 0, 0, 23})); REQUIRE_EQ(modulo_chain(ints({24, 60, 60}), 7223), ints({0, 2, 0, 23})); REQUIRE_EQ( modulo_chain(ints({24, 60, 60, 1000}), 3 * 24 * 60 * 60 * 1000 + 17 * 60 * 60 * 1000 + 4 * 60 * 1000 + 31 * 1000 + 256), ints({3, 17, 4, 31, 256})); } TEST_CASE("numeric_test, line_equation") { using namespace fplus; REQUIRE(is_in_interval_around(0.001, 2.0, line_equation( std::make_pair(0.0, 0.0), std::make_pair(2.0, 1.0), 4.0))); REQUIRE(is_in_interval_around(0.001, -2.0, line_equation( std::make_pair(-1.0, 1.0), std::make_pair(-2.0, 4.0), 0.0))); } libfplus-0.2.13/test/optimize_test.cpp000066400000000000000000000035561376322245400200200ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include TEST_CASE("optimize_test, minimize_downhill") { using namespace fplus; const auto objective = [](const std::array& x) { return square(x[0] + 2) + 3; }; { const auto result = minimize_downhill<1>( objective, 0.0001, {{123}}); REQUIRE(is_in_closed_interval_around(0.0001, -2.0, result[0])); } { const auto result = minimize_downhill<1>( objective, 0.0001, {{-42}}, maybe(0.01)); REQUIRE(is_in_closed_interval_around(0.0001, -2.0, result[0])); } { const auto result = minimize_downhill<1>( objective, 0.0001, {{-2.000001}}); REQUIRE(is_in_closed_interval_around(0.0001, -2.0, result[0])); } const auto objective_2d = [](const std::array& p) { const auto x = p[0]; const auto y = p[1]; return abs(2*cube(x-3)) + 4*square(x+1) + 2*x + abs(1*cube(y-7)) + 7*square(y-4) + 6*y; }; { const auto result1 = minimize_downhill<2>( objective_2d, 0.0001, {{0,0}}); REQUIRE(is_in_closed_interval_around(0.001, 1.1946, result1[0])); REQUIRE(is_in_closed_interval_around(0.001, 4.7025, result1[1])); const auto result2 = minimize_downhill<2>( objective_2d, 0.0001, {{0,0}}, 0.123, 0.234); REQUIRE(is_in_closed_interval_around(0.001, 1.1946, result2[0])); REQUIRE(is_in_closed_interval_around(0.001, 4.7025, result2[1])); } } libfplus-0.2.13/test/pairs_test.cpp000066400000000000000000000172121376322245400172700ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include namespace { auto squareLambda = [](int x) -> int { return x*x; }; auto cubeLambda = [](int x) -> int { return x*x*x; }; typedef std::vector IntVector; typedef std::list IntList; typedef std::pair IntPair; typedef std::vector IntPairs; IntVector xs = {1,2,2,3,2}; struct dummy { int i; }; } TEST_CASE("pairs_test, zip_with") { using namespace fplus; const auto multiply = [](int x, int y){ return x * y; }; REQUIRE_EQ(zip_with(multiply, xs, xs), transform(squareLambda, xs)); const auto add = [](int x, int y){ return x + y; }; REQUIRE_EQ(zip_with(add, IntVector({1,2,3}), IntVector({1,2})), IntVector({2,4})); REQUIRE_EQ(zip_with(add, IntVector({1,2}), IntVector({1,2,3})), IntVector({2,4})); REQUIRE_EQ(zip_with(add, IntVector({1,2}), IntList({1,2,3})), IntVector({2,4})); const auto add_generic = [](auto x, int y) {return x + y;}; REQUIRE_EQ(zip_with(add_generic, IntVector({1,2,3}), IntVector({1,2})), IntVector({2,4})); REQUIRE_EQ(zip_with(std::plus<>{}, IntVector({1,2,3}), IntVector({1,2})), IntVector({2,4})); } TEST_CASE("pairs_test, zip_with_3") { using namespace fplus; const auto multiply = [](int x, int y, int z){ return x * y * z; }; const auto multiply_generic = [](auto x, auto y, auto z){ return x * y * z; }; const auto cubed = transform(cubeLambda, xs); REQUIRE_EQ(zip_with_3(multiply, xs, xs, xs), cubed); REQUIRE_EQ(zip_with_3(multiply_generic, xs, xs, xs), cubed); } TEST_CASE("pairs_test, zip_with_defaults") { using namespace fplus; const auto add = [](int x, int y){ return x + y; }; const auto add_generic = [](auto x, auto y){ return x + y; }; REQUIRE_EQ(zip_with_defaults(add, 6, 7, IntVector({1,2,3}), IntVector({1,2})), IntVector({2,4,10})); REQUIRE_EQ(zip_with_defaults(add, 6, 7, IntVector({1,2}), IntVector({1,2,3})), IntVector({2,4,9})); REQUIRE_EQ(zip_with_defaults(add_generic, 6, 7, IntVector({1,2}), IntVector({1,2,3})), IntVector({2,4,9})); } TEST_CASE("pairs_test, zip") { using namespace fplus; auto xsZippedWithXs = zip(xs, xs); REQUIRE_EQ(unzip(xsZippedWithXs).first, xs); } TEST_CASE("pairs_test, pair functions") { using namespace fplus; IntPair intPair = std::make_pair(2, 3); IntPairs intPairs = {{1,2}, {3,4}}; IntPairs intPairsSwapped = {{2,1}, {4,3}}; REQUIRE_EQ(fst(intPair), 2); REQUIRE_EQ(snd(intPair), 3); REQUIRE_EQ(swap_pair_elems(intPair), std::make_pair(3, 2)); REQUIRE_EQ(swap_pairs_elems(intPairs), intPairsSwapped); REQUIRE_EQ(transform_fst(squareLambda, intPair), std::make_pair(4, 3)); REQUIRE_EQ(transform_snd(squareLambda, intPair), std::make_pair(2, 9)); REQUIRE_EQ(transform_fst([](auto i) { return i * i; }, intPair), std::make_pair(4, 3)); REQUIRE_EQ(transform_snd([](auto i) { return i * i; }, intPair), std::make_pair(2, 9)); REQUIRE_EQ(transform_pair(squareLambda, squareLambda, intPair), std::make_pair(4, 9)); typedef std::vector> StringIntPairs; StringIntPairs stringIntPairs = {{"a", 1}, {"a", 2}, {"b", 6}, {"a", 4}}; auto stringIntPairsAsMapGrouped = pairs_to_map_grouped(stringIntPairs); auto groupNames = transform(fst, stringIntPairs); auto groupNameToMedianMap = transform_map_values(median>, stringIntPairsAsMapGrouped); auto getMedianValue = bind_1st_and_2nd_of_3(get_from_map_with_def>, groupNameToMedianMap, 0); auto groupMendianValues = transform(getMedianValue, groupNames); auto stringIntPairsSndReplacedWithGroupMedian = zip(groupNames, groupMendianValues); REQUIRE_EQ(stringIntPairsSndReplacedWithGroupMedian, StringIntPairs({{"a", 2}, {"a", 2}, {"b", 6}, {"a", 2}})); const std::function double_int = [](int x) -> int { return 2 * x; }; REQUIRE_EQ(transform_pair(double_int, double_int, IntPair({2, 3})), IntPair({4, 6})); // Thanks to invoke, such code works. // (I don't have a use case for it though) dummy dumb; dumb.i = 42; auto p = std::make_pair(dumb, dumb); auto result = transform_pair(&dummy::i, &dummy::i, p); REQUIRE_EQ(result, std::make_pair(42, 42)); } TEST_CASE("pairs_test, enumerate") { using namespace fplus; REQUIRE_EQ(enumerate(xs), (std::vector>({{0,1}, {1,2}, {2,2}, {3,3}, {4,2}}))); } TEST_CASE("pairs_test, adjacent_pairs") { using namespace fplus; REQUIRE_EQ(adjacent_pairs(xs), IntPairs({{1,2},{2,3}})); REQUIRE_EQ(adjacent_pairs(IntVector({1,2,2,3})), IntPairs({{1,2},{2,3}})); } TEST_CASE("pairs_test, overlapping_pairs") { using namespace fplus; REQUIRE_EQ(overlapping_pairs(xs), IntPairs({{1,2},{2,2},{2,3},{3,2}})); } TEST_CASE("pairs_test, overlapping_pairs_cyclic") { using namespace fplus; REQUIRE_EQ(overlapping_pairs_cyclic(xs), IntPairs({{1,2},{2,2},{2,3},{3,2},{2,1}})); } TEST_CASE("pairs_test, first_mismatch_idx_on") { using namespace fplus; REQUIRE_EQ(first_mismatch_idx_on(is_even, IntVector({1,2,3}), IntVector({3,5,3})), just(1)); REQUIRE_EQ(first_mismatch_idx_on(is_even, IntVector({1,2,3}), IntVector({1,5})), just(1)); REQUIRE_EQ(first_mismatch_idx_on(is_even, IntVector({1,2,3}), IntVector({1,6})), nothing()); REQUIRE_EQ(first_mismatch_idx_on(is_even, IntVector(), IntVector({1,2})), nothing()); } TEST_CASE("pairs_test, first_mismatch_on") { using namespace fplus; REQUIRE_EQ(first_mismatch_on(is_even, IntVector({1,2,3}), IntVector({3,5,3})), just(IntPair(2,5))); REQUIRE_EQ(first_mismatch_on(is_even, IntVector({1,2,3}), IntVector({1,5})), just(IntPair(2,5))); REQUIRE_EQ(first_mismatch_on(is_even, IntVector({1,2,3}), IntVector({1,6})), nothing()); REQUIRE_EQ(first_mismatch_on(is_even, IntVector(), IntVector({1,2})), nothing()); } TEST_CASE("pairs_test, first_mismatch_idx") { using namespace fplus; REQUIRE_EQ(first_mismatch_idx(IntVector({1,2,3}), IntVector({1,4,3})), just(1)); REQUIRE_EQ(first_mismatch_idx(IntVector({1,2,3}), IntVector({1,4})), just(1)); REQUIRE_EQ(first_mismatch_idx(IntVector({1,2,3}), IntVector({1,2})), nothing()); REQUIRE_EQ(first_mismatch_idx(IntVector(), IntVector({1,2})), nothing()); } TEST_CASE("pairs_test, first_mismatch") { using namespace fplus; REQUIRE_EQ(first_mismatch(IntVector({1,2,3}), IntVector({1,4,3})), just(IntPair(2,4))); REQUIRE_EQ(first_mismatch(IntVector({1,2,3}), IntVector({1,4})), just(IntPair(2,4))); REQUIRE_EQ(first_mismatch(IntVector({1,2,3}), IntVector({1,2})), nothing()); REQUIRE_EQ(first_mismatch(IntVector(), IntVector({1,2})), nothing()); } TEST_CASE("pairs_test, first_match_idx_on") { using namespace fplus; REQUIRE_EQ(first_match_idx_on(is_even, IntVector({1,2,3}), IntVector({2,4,3})), just(1)); REQUIRE_EQ(first_match_idx_on(is_even, IntVector(), IntVector({1,2})), nothing()); } TEST_CASE("pairs_test, first_match") { using namespace fplus; REQUIRE_EQ(first_match(IntVector({1,2,3}), IntVector({5,2,3})), just(IntPair(2,2))); REQUIRE_EQ(first_match(IntVector(), IntVector({1,2})), nothing()); } libfplus-0.2.13/test/queue_test.cpp000066400000000000000000000027331376322245400173000ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include TEST_CASE("queue_test, full") { using namespace fplus; using namespace std::chrono_literals; queue q; std::thread producer([&q] { q.push(1); q.push(2); std::this_thread::sleep_for(200ms); q.push(3); q.push(4); std::this_thread::sleep_for(200ms); q.push(5); std::this_thread::sleep_for(200ms); q.push(6); }); std::thread consumer([&q] { std::this_thread::sleep_for(100ms); REQUIRE_EQ(q.pop(), fplus::just(1)); REQUIRE_EQ(q.pop(), fplus::just(2)); REQUIRE_EQ(q.pop(), fplus::nothing()); std::this_thread::sleep_for(200ms); REQUIRE_EQ(q.pop_all(), std::vector({3, 4})); REQUIRE_EQ(q.pop_all(), std::vector({})); REQUIRE_EQ(q.wait_and_pop_all(), std::vector({5})); REQUIRE_EQ(q.wait_for_and_pop_all(100000), std::vector({})); REQUIRE_EQ(q.wait_for_and_pop_all(200000), std::vector({6})); REQUIRE_EQ(q.wait_for_and_pop_all(100000), std::vector({})); }); producer.join(); consumer.join(); } libfplus-0.2.13/test/raii_test.cpp000066400000000000000000000013551376322245400170770ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include TEST_CASE("raii_test, make_raii") { std::string log = "nothing"; const auto init = [&log]() { log = "init"; }; const auto quit = [&log]() { log = "quit"; }; REQUIRE_EQ(log, "nothing"); { REQUIRE_EQ(log, "nothing"); const auto ressource = fplus::make_raii(init, quit); REQUIRE_EQ(log, "init"); } REQUIRE_EQ(log, "quit"); } libfplus-0.2.13/test/read_test.cpp000066400000000000000000000036411376322245400170660ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include TEST_CASE("read_test, read_value") { using namespace fplus; REQUIRE_EQ(read_value("42"), just(42)); REQUIRE_EQ(read_value("foo"), just("foo")); REQUIRE_EQ(read_value("42"), just(42)); REQUIRE_EQ(read_value("42"), just(42)); REQUIRE_EQ(read_value("42"), just(42)); REQUIRE_EQ(read_value("42"), just(42)); REQUIRE_EQ(read_value("42"), just(42)); REQUIRE_EQ(read_value("-3"), just(-3)); REQUIRE_EQ(read_value("twenty"), nothing()); REQUIRE_EQ(read_value("3 thousand"), nothing()); REQUIRE_EQ(read_value_with_default(3, "42"), 42); REQUIRE_EQ(read_value_with_default(3, "foo"), 3); REQUIRE_EQ(read_value_with_default(3, ""), 3); REQUIRE_EQ(read_value_unsafe("42"), 42); REQUIRE(is_in_interval(-42.4f, -42.2f, unsafe_get_just(read_value("-42.3")))); REQUIRE(is_in_interval(-42.4 , -42.2, unsafe_get_just(read_value("-42.3")))); REQUIRE(is_in_interval(-42.4L, -42.2L, unsafe_get_just(read_value("-42.3")))); } TEST_CASE("read_test, read_value_result") { using namespace fplus; REQUIRE_EQ(read_value_result("42"), (ok(42))); REQUIRE_EQ(read_value_result("-3"), (ok(-3))); REQUIRE(is_error(read_value_result("twenty"))); REQUIRE(is_error(read_value_result("3 thousand"))); } libfplus-0.2.13/test/readme_examples_test.cpp000066400000000000000000000156551376322245400213160ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { bool is_odd_int(int x) { return x % 2 == 1; } } TEST_CASE("readme_examples_test, KeepIf") { typedef std::vector Ints; Ints values = { 24, 11, 65, 44, 80, 18, 73, 90, 69, 18 }; { // Version 1: hand written range based for loop Ints odds; for (int x : values) if (is_odd_int(x)) odds.push_back(x); } { // Version 2: STL Ints odds; std::copy_if(std::begin(values), std::end(values), std::back_inserter(odds), is_odd_int); } { // Version : FunctionalPlus auto odds = fplus::keep_if(is_odd_int, values); } } TEST_CASE("readme_examples_test, SameOldSameOld") { std::list things = {"same old", "same old"}; REQUIRE(fplus::all_the_same(things)); } TEST_CASE("readme_examples_test, eIInTeam") { std::string team = "Our team is great. I love everybody I work with."; REQUIRE_EQ(fplus::count("I", fplus::split_words(false, team)), 2); } namespace { struct Entity { Entity() : calm_(true), bright_(true) {} bool calm_; bool bright_; }; } TEST_CASE("readme_examples_test, AllIsCalmAndBright") { auto isCalm = [](const Entity& e) { return e.calm_; }; auto isBright = [](const Entity& e) { return e.bright_; }; std::vector entities(4); REQUIRE(fplus::all_by(fplus::logical_and(isCalm, isBright), entities)); } namespace { struct cat { double cuteness() const { return softness_ * temperature_ * roundness_ * fur_amount_ - size_; } std::string name_; double softness_; double temperature_; double size_; double roundness_; double fur_amount_; }; } TEST_CASE("readme_examples_test, TheCutestCat") { std::vector cats = { {"Tigger", 5, 5, 5, 5, 5}, {"Simba", 2, 9, 9, 2, 7}, {"Muffin", 9, 4, 2, 8, 6}, {"Garfield", 6, 5, 7, 9, 5}}; auto cutest_cat = fplus::maximum_on(std::mem_fn(&cat::cuteness), cats); REQUIRE_EQ(cutest_cat.name_, std::string("Muffin")); } namespace { std::list collatz_seq(int x) { std::list result; while (x > 1) { result.push_back(x); if (x % 2 == 0) x = x / 2; else x = 3 * x + 1; } result.push_back(x); return result; } } TEST_CASE("readme_examples_test, CollatzSequence") { typedef std::list Ints; // [1, 2, 3 ... 29] auto xs = fplus::numbers(1, 30); // A function that does [1, 2, 3, 4, 5] -> "[1 => 2 => 3 => 4 => 5]" auto show_ints = fplus::bind_1st_of_2(fplus::show_cont_with, " => "); // A composed function that calculates a Collatz sequence and shows it. auto show_collats_seq = fplus::compose(collatz_seq, show_ints); // Associate the numbers with the string representation of their sequences. auto collatz_dict = fplus::create_map_with(show_collats_seq, xs); // Print some of the sequences. REQUIRE_EQ(collatz_dict[13], std::string("[13 => 40 => 20 => 10 => 5 => 16 => 8 => 4 => 2 => 1]")); REQUIRE_EQ(collatz_dict[17], std::string("[17 => 52 => 26 => 13 => 40 => 20 => 10 => 5 => 16 => 8 => 4 => 2 => 1]")); } namespace { std::string gemstone_count(const std::string& input) { using namespace fplus; typedef std::set characters; const auto lines = split_lines(false, input); const auto sets = transform( convert_container, lines); const auto gem_elements = fold_left_1( set_intersection, sets); return show(size_of_cont(gem_elements)); } std::string gemstone_count_fwd_apply(const std::string& input) { using namespace fplus; typedef std::set characters; return fwd::apply( input , fwd::split_lines(false) , fwd::transform(convert_container) , fwd::fold_left_1(set_intersection) , fwd::size_of_cont() , fwd::show() ); } typedef std::set characters; const auto gemstone_count_fwd_compose = fplus::fwd::compose( fplus::fwd::split_lines(false), fplus::fwd::transform(fplus::convert_container), fplus::fwd::fold_left_1(fplus::set_intersection), fplus::fwd::size_of_cont(), fplus::fwd::show() ); } TEST_CASE("readme_examples_test, fwd_style") { const std::string input = "Lorem ipsum\ndolor sit amet,\nconsectetur,\nadipisci velit"; const auto result = gemstone_count(input); const auto result_fwd_apply = gemstone_count_fwd_apply(input); const auto result_fwd_compose = gemstone_count_fwd_compose(input); REQUIRE_EQ(result, std::string("2")); REQUIRE_EQ(result_fwd_apply, std::string("2")); REQUIRE_EQ(result_fwd_compose, std::string("2")); } namespace { std::string square_is_even(const std::vector& xs) { using namespace fplus; auto ys = fplus::transform(fplus::square, xs); auto zs = fplus::keep_if(fplus::is_even, ys); return show(size_of_cont(zs)); } std::string square_is_even_chain(const std::vector& xs) { using namespace fplus; auto zs = fplus::keep_if(fplus::is_even, fplus::transform(fplus::square, xs)); return show(size_of_cont(zs)); } std::string square_is_even_fwd_apply(const std::vector& xs) { using namespace fplus; auto zs = fwd::apply( xs , fplus::fwd::transform(fplus::square) , fplus::fwd::keep_if(fplus::is_even)); return show(size_of_cont(zs)); } const auto square_is_even_fwd_compose = fplus::fwd::compose( fplus::fwd::transform(fplus::square), fplus::fwd::keep_if(fplus::is_even), fplus::fwd::size_of_cont(), fplus::fwd::show() ); } TEST_CASE("readme_examples_test, square_is_even") { const std::vector xs = {0,1,2,3,4,5,6,7}; const auto result = square_is_even(xs); // uses l-value keep_if internally const auto result_chain = square_is_even_chain(xs); // uses r-value keep_if internally const auto result_fwd_apply = square_is_even_fwd_apply(xs); // uses r-value keep_if internally const auto result_fwd_compose = square_is_even_fwd_compose(xs); // uses r-value keep_if internally } libfplus-0.2.13/test/replace_test.cpp000066400000000000000000000034541376322245400175700ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include #include TEST_CASE("replace_test, replace_if") { auto is_even = [](int value) { return value % 2 == 0; }; std::vector v = { 1, 3, 4, 6, 7 }; auto result = fplus::replace_if(is_even, 0, v); REQUIRE_EQ(result, std::vector({1, 3, 0, 0, 7})); auto result_rvalue = fplus::replace_if(is_even, 0, std::vector({ 1, 3, 4, 6, 7 })); REQUIRE_EQ(result_rvalue, std::vector({1, 3, 0, 0, 7})); } TEST_CASE("replace_test, replace_elem_at_idx") { std::vector v = { 1, 3, 4, 4, 7 }; auto result = fplus::replace_elem_at_idx(2, 0, v); REQUIRE_EQ(result, std::vector({1, 3, 0, 4, 7})); auto result_rvalue = fplus::replace_elem_at_idx(2, 0, std::vector({ 1, 3, 4, 4, 7 })); REQUIRE_EQ(result_rvalue, std::vector({1, 3, 0, 4, 7})); } TEST_CASE("replace_test, replace_elems") { std::vector v = { 1, 3, 4, 4, 7 }; auto result = fplus::replace_elems(4, 0, v); REQUIRE_EQ(result, std::vector({1, 3, 0, 0, 7})); auto result_rvalue = fplus::replace_elems(4, 0, std::vector({1, 3, 0, 0, 7})); REQUIRE_EQ(result_rvalue, std::vector({1, 3, 0, 0, 7})); } TEST_CASE("replace_test, replace_tokens") { const std::string source = "haha"; const std::string dest = "hihi"; const std::string input = "oh, hahaha!"; auto result = fplus::replace_tokens(source, dest, input); REQUIRE_EQ(result, std::string("oh, hihiha!")); } libfplus-0.2.13/test/result_test.cpp000066400000000000000000000217751376322245400175010ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { auto sqrtToResult = [](auto x) { return x < 0.0f ? fplus::error(std::string("no sqrt of negative numbers")) : fplus::ok(static_cast(sqrt(static_cast(x)))); }; auto sqrtToResultInt = [](auto x) { return x < 0 ? fplus::error(std::string("no sqrt of negative numbers")) : fplus::ok(fplus::round(sqrt(static_cast(x)))); }; float IntToFloat(const int& x) { return static_cast(x); } typedef std::vector> IntResults; typedef std::vector Ints; typedef std::vector Strings; } class resultTestState { public: explicit resultTestState(int x) : x_(x) {} void Add(int y) { x_ += y; } int Get() const { return x_; } private: int x_; }; TEST_CASE("result_test, ok_with_default") { using namespace fplus; auto x = ok(2); auto y = error("an error"); auto Or42 = bind_1st_of_2(ok_with_default, 42); REQUIRE_EQ(Or42(x), 2); REQUIRE_EQ(Or42(y), 42); } TEST_CASE("maybe_test, join_result") { using namespace fplus; using Ok = int; using Err = std::string; using Res = result; REQUIRE_EQ(join_result(ok(ok(2))), (ok(2))); REQUIRE_EQ(join_result(ok(error("e"))), (error("e"))); REQUIRE_EQ(join_result(error("e")), (error("e"))); } TEST_CASE("result_test, and_then_result") { using namespace fplus; auto ok_4 = ok(4); auto ok_2 = ok(2); auto an_error = error("an error"); REQUIRE_EQ(and_then_result(sqrtToResultInt, ok_4), ok_2); REQUIRE_EQ(and_then_result(sqrtToResultInt, an_error), an_error); const auto string_to_result_int_string = [](const auto& str) { if (str == "42") return ok(42); else return error("not 42"); }; REQUIRE_EQ(and_then_result(string_to_result_int_string, (ok("3"))), (error("not 42"))); REQUIRE_EQ(and_then_result(string_to_result_int_string, (ok("42"))), (ok(42))); REQUIRE_EQ(and_then_result(string_to_result_int_string, (error("error"))), (error("error"))); } TEST_CASE("result_test, compose_result") { using namespace fplus; auto x = ok(2); auto y = error("an error"); auto sqrtAndSqrt = compose_result(sqrtToResult, sqrtToResult); REQUIRE_EQ(lift_result(square, x), (ok(4))); REQUIRE_EQ(lift_result(square, y), (error(std::string("an error")))); auto sqrtIntAndSqrtIntAndSqrtInt = compose_result(sqrtToResultInt, sqrtToResultInt, sqrtToResultInt); REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtInt(256), (ok(2))); auto sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt = compose_result(sqrtToResultInt, sqrtToResultInt, sqrtToResultInt, sqrtToResultInt); REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt(65536), (ok(2))); const auto LiftedIntToFloat = [](const result& r) -> result { return lift_result(IntToFloat, r); }; auto OkInt = ok; auto IntToResultFloat = compose(OkInt, LiftedIntToFloat); auto IntToFloatAndSqrtAndSqrt = compose_result(IntToResultFloat, sqrtAndSqrt); REQUIRE(is_in_interval(1.41f, 1.42f, unsafe_get_ok (IntToFloatAndSqrtAndSqrt(4)))); // first callable can take a variadic number of arguments auto sumToResult = [](auto a, auto b) { return ok(a + b); }; auto squareSumResult = compose_result(sumToResult, [](auto sum) { return ok(sum * sum); }); REQUIRE_EQ(squareSumResult(5, 5), (ok(100))); } TEST_CASE("result_test, lift") { using namespace fplus; auto x = ok(2); auto y = error("an error"); auto SquareAndSquare = compose(square, square); REQUIRE_EQ((lift_result(SquareAndSquare, x)), (ok(16))); REQUIRE_EQ((lift_result([](auto n) { return square(n) * square(n); }, x)), (ok(16))); } TEST_CASE("result_test, lift_both") { using namespace fplus; const auto x = ok(2); const auto y = error("an error"); REQUIRE_EQ(lift_result_both(square, to_upper_case, x), (ok(4))); REQUIRE_EQ(lift_result_both(square, to_upper_case, y), (error("AN ERROR"))); REQUIRE_EQ(lift_result_both([](auto z) { return z * z; }, [](auto z) { return to_upper_case(z); }, y), (error("AN ERROR"))); } TEST_CASE("result_test, unify_result") { using namespace fplus; const auto x = ok(2); const auto y = error("an error"); const auto unify = [](const result& r) -> std::string { return unify_result(show, to_upper_case, r); }; REQUIRE_EQ(unify(x), "2"); REQUIRE_EQ(unify(y), "AN ERROR"); const auto unifyGeneric = [](auto r) { return unify_result(show, to_upper_case, r); }; REQUIRE_EQ(unifyGeneric(x), "2"); REQUIRE_EQ(unifyGeneric(y), "AN ERROR"); } TEST_CASE("result_test, equality") { using namespace fplus; IntResults results = {ok(1), error(std::string("no sqrt of negative numbers")), ok(2)}; REQUIRE(oks(results) == Ints({ 1,2 })); REQUIRE(errors(results) == Strings({std::string("no sqrt of negative numbers")})); REQUIRE((ok(1)) == (ok(1))); REQUIRE((ok(1)) != (ok(2))); REQUIRE((ok(1)) != (error(std::string("fail")))); REQUIRE(error(std::string("fail")) == (error(std::string("fail")))); REQUIRE(error(std::string("fail 1")) != (error(std::string("fail 2")))); } TEST_CASE("result_test, transform_and_keep_oks") { using namespace fplus; Ints wholeNumbers = { -3, 4, 16, -1 }; REQUIRE_EQ(transform_and_keep_oks(sqrtToResultInt, wholeNumbers) , Ints({2,4})); REQUIRE_EQ(transform_and_concat( bind_1st_of_2(replicate, std::size_t(3)), Ints{ 1,2 }) , Ints({ 1,1,1,2,2,2 })); } TEST_CASE("result_test, show_result") { using namespace fplus; REQUIRE_EQ(show_result(ok(42)), std::string("Ok 42")); REQUIRE_EQ(show_result(error("fail")), std::string("Error fail")); auto x = ok(2); REQUIRE_EQ((to_maybe(x)), just(2)); REQUIRE_EQ((from_maybe(std::string("no error"), just(2))), x); } TEST_CASE("result_test, exceptions") { using namespace fplus; std::string thrown_str; try { throw_on_error(std::invalid_argument("exception string"), error("failed")); } catch (const std::exception& e) { thrown_str = e.what(); } REQUIRE_EQ(thrown_str, std::string("exception string")); thrown_str.clear(); try { throw_type_on_error(error("failed")); } catch (const std::exception& e) { thrown_str = e.what(); } REQUIRE_EQ(thrown_str, std::string("failed")); thrown_str.clear(); } TEST_CASE("result_test, copy") { using namespace fplus; result result_4 = ok(4); result result_4_copy(result_4); result result_4_copy_2 = error("dummy"); result_4_copy_2 = result_4_copy; REQUIRE_EQ(result_4_copy_2, (ok(4))); result result_int_string_error = error("error"); result result_int_string_error_copy(result_int_string_error); result result_int_string_error_copy_2 = ok(9999); result_int_string_error_copy_2 = result_int_string_error_copy; REQUIRE_EQ(result_int_string_error_copy_2, (error("error"))); } libfplus-0.2.13/test/search_test.cpp000066400000000000000000000126011376322245400174140ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace { bool is_even(int value) { return value % 2 == 0; } } TEST_CASE("search_test, find_first_by") { std::vector v = { 1, 3, 4, 6, 9 }; auto result = fplus::find_first_by(is_even, v); REQUIRE_EQ(result, fplus::just(4)); } TEST_CASE("search_test, find_first_by_nothing_found") { std::vector v = { 1, 3, 5, 7, 9 }; auto result = fplus::find_first_by(is_even, v); REQUIRE_EQ(result, fplus::nothing()); } TEST_CASE("search_test, find_last_by") { std::vector v = { 1, 3, 4, 6, 9 }; auto result = fplus::find_last_by(is_even, v); REQUIRE_EQ(result, fplus::just(6)); } TEST_CASE("search_test, find_last_by_nothing_found") { std::vector v = { 1, 3, 5, 7, 9 }; auto result = fplus::find_first_by(is_even, v); REQUIRE_EQ(result, fplus::nothing()); } TEST_CASE("search_test, find_first_idx_by") { std::vector v = { 1, 3, 4, 6, 9 }; auto result = fplus::find_first_idx_by(is_even, v); REQUIRE_EQ(result, fplus::just(2)); } TEST_CASE("search_test, find_first_idx_by_nothing_found") { std::vector v = { 1, 3, 5, 7, 9 }; auto result = fplus::find_first_idx_by(is_even, v); REQUIRE_EQ(result, fplus::nothing()); } TEST_CASE("search_test, find_last_idx_by") { std::vector v = { 1, 3, 4, 6, 9 }; auto result = fplus::find_last_idx_by(is_even, v); REQUIRE_EQ(result, fplus::just(3)); } TEST_CASE("search_test, find_last_idx_by_nothing_found") { std::vector v = { 1, 3, 5, 7, 9 }; auto result = fplus::find_last_idx_by(is_even, v); REQUIRE_EQ(result, fplus::nothing()); } TEST_CASE("search_test, find_first_idx") { std::vector v = { 1, 3, 4, 4, 9 }; auto result = fplus::find_first_idx(4, v); REQUIRE_EQ(result, fplus::just(2)); } TEST_CASE("search_test, find_first_idx_nothing_found") { std::vector v = { 1, 3, 5, 7, 9 }; auto result = fplus::find_first_idx(4, v); REQUIRE_EQ(result, fplus::nothing()); } TEST_CASE("search_test, find_last_idx") { std::vector v = { 1, 3, 4, 4, 9 }; auto result = fplus::find_last_idx(4, v); REQUIRE_EQ(result, fplus::just(3)); } TEST_CASE("search_test, find_last_idx_nothing_found") { std::vector v = { 1, 3, 5, 7, 9 }; auto result = fplus::find_last_idx(4, v); REQUIRE_EQ(result, fplus::nothing()); } TEST_CASE("search_test, find_all_idxs_by") { std::vector v = { 1, 3, 4, 6, 9 }; auto result = fplus::find_all_idxs_by(is_even, v); REQUIRE_EQ(result, std::vector({2, 3})); } TEST_CASE("search_test, find_last_idxs_by_nothing_found") { std::vector v = { 1, 3, 5, 7, 9 }; auto result = fplus::find_all_idxs_by(is_even, v); REQUIRE(result.empty()); } TEST_CASE("search_test, find_all_idxs_of") { std::vector v = { 1, 3, 4, 4, 9 }; auto result = fplus::find_all_idxs_of(4, v); REQUIRE_EQ(result, std::vector({2, 3})); } TEST_CASE("search_test, find_all_instances_of_token") { const std::string token = "haha"; const std::string input = "oh, hahaha!"; auto result = fplus::find_all_instances_of_token(token, input); REQUIRE_EQ(result, std::vector({4, 6})); } TEST_CASE("search_test, find_last_idxs_of_nothing_found") { std::vector v = { 1, 3, 5, 7, 9 }; auto result = fplus::find_all_idxs_of(4, v); REQUIRE(result.empty()); } TEST_CASE("search_test, find_all_instances_of_token_oversized_token") { const std::string token = "hahahahaha"; const std::string input = "oh, hahaha!"; auto result = fplus::find_all_instances_of_token(token, input); REQUIRE(result.empty()); } TEST_CASE("search_test, find_all_instances_of_token_non_overlapping") { const std::string token = "haha"; const std::string input = "oh, hahaha!"; auto result = fplus::find_all_instances_of_token_non_overlapping(token, input); REQUIRE_EQ(result, std::vector({4})); } TEST_CASE("search_test, find_first_instance_of_token") { const std::string token = "haha"; const std::string input = "oh, hahaha!"; auto result = fplus::find_first_instance_of_token(token, input); REQUIRE_EQ(result, fplus::just(4)); } TEST_CASE("search_test, find_first_instance_of_token_at_end") { const std::string token = "haha"; const std::string input = "oh, haha"; auto result = fplus::find_first_instance_of_token(token, input); REQUIRE_EQ(result, fplus::just(4)); } TEST_CASE("search_test, find_first_instance_of_token_nothing") { const std::string token = "hihi"; const std::string input = "oh, haha"; auto result = fplus::find_first_instance_of_token(token, input); REQUIRE_EQ(result, fplus::nothing()); } TEST_CASE("search_test, find_first_instance_of_token_oversized_token") { const std::string token = "hahahahaha"; const std::string input = "oh, hahaha!"; auto result = fplus::find_first_instance_of_token(token, input); REQUIRE_EQ(result, fplus::nothing()); } libfplus-0.2.13/test/sets_test.cpp000066400000000000000000000060621376322245400171310ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include TEST_CASE("set_test, set functions") { using namespace fplus; using IntSet = std::set; using setVector = std::vector; using IntUnordSet = std::unordered_set; using unordSetVector = std::vector; using StringUnordSet = std::unordered_set; //std::set tests REQUIRE(set_includes(IntSet({0,1,2,3}), IntSet({0,2}))); REQUIRE_FALSE(set_includes(IntSet({0,2}), IntSet({0,1,2,3}))); REQUIRE_FALSE(set_includes(IntSet({0,1,2,3}), IntSet({2,3,4,5}))); REQUIRE_EQ(set_merge(IntSet({0,1,2,3}), IntSet({2,3,4,5})), IntSet({0,1,2,3,4,5})); REQUIRE_EQ(set_merge(IntSet({0,1,2,3}), IntSet({0,2})), IntSet({0,1,2,3})); REQUIRE_EQ(set_intersection(IntSet({0,1,2,3}), IntSet({2,3,4,5})), IntSet({2,3})); REQUIRE_EQ(set_difference(IntSet({0,1,2,3}), IntSet({2,3,4,5})), IntSet({0,1})); REQUIRE_EQ(set_symmetric_difference(IntSet({0,1,2,3}), IntSet({2,3,4,5})), IntSet({0,1,4,5})); REQUIRE_EQ(set_intersection(IntSet({0,1,2,3}), IntSet({2,3,4,5})), IntSet({2,3})); REQUIRE_EQ(sets_intersection(setVector({IntSet({0,1,2,3}), IntSet({2,3,4,5}), IntSet({0,2})})), IntSet({2})); //set::unordered_set tests REQUIRE(unordered_set_includes(IntUnordSet({0,1,2,3}), IntUnordSet({0,2}))); REQUIRE_FALSE(unordered_set_includes(IntUnordSet({0,2}), IntUnordSet({0,1,2,3}))); REQUIRE_FALSE(unordered_set_includes(IntUnordSet({0,1,2,3}), IntUnordSet({2,3,4,5}))); REQUIRE_EQ(unordered_set_merge(IntUnordSet({0,1,2,3}), IntUnordSet({2,3,4,5})), IntUnordSet({0,1,2,3,4,5})); REQUIRE_EQ(unordered_set_merge(IntUnordSet({0,1,2,3}), IntUnordSet({0,2})), IntUnordSet({0,1,2,3})); REQUIRE_EQ(unordered_set_intersection(IntUnordSet({0,1,2,3}), IntUnordSet({2,3,4,5})), IntUnordSet({2,3})); REQUIRE_EQ(unordered_set_difference(IntUnordSet({0,1,2,3}), IntUnordSet({2,3,4,5})), IntUnordSet({0,1})); REQUIRE_EQ(unordered_set_symmetric_difference(IntUnordSet({0,1,2,3}), IntUnordSet({2,3,4,5})), IntUnordSet({0,1,4,5})); REQUIRE_EQ(unordered_set_intersection(IntUnordSet({0,1,2,3}), IntUnordSet({2,3,4,5})), IntUnordSet({2,3})); REQUIRE_EQ(unordered_sets_intersection(unordSetVector({IntUnordSet({0,1,2,3}), IntUnordSet({2,3,4,5}), IntUnordSet({0,2})})), IntUnordSet({2})); REQUIRE_EQ(unordered_set_merge(StringUnordSet({"yes", "no", "hello"}), StringUnordSet({"hello", "what"})), StringUnordSet({"yes", "no", "what", "hello"})); REQUIRE(set_is_disjoint(IntSet({0,1,3}), IntSet({2,4}))); REQUIRE_FALSE(set_is_disjoint(IntSet({0,1,3}), IntSet({2,1}))); REQUIRE(unordered_set_is_disjoint(IntUnordSet({0,1,3}), IntUnordSet({2,4}))); REQUIRE_FALSE(unordered_set_is_disjoint(IntUnordSet({0,1,3}), IntUnordSet({2,1}))); } libfplus-0.2.13/test/shared_ref_test.cpp000066400000000000000000000035531376322245400202570ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include namespace { std::vector logs; void log(const std::string& str) { logs.push_back(str); } struct test { int m_x; test(int x) :m_x(x) { log("test(" + fplus::show(m_x) + ")"); } test(const test& t) :m_x(t.m_x) { log("test(const test& " + fplus::show(m_x) + ")"); } test(test&& t) :m_x(std::move(t.m_x)) { log("test(test&& " + fplus::show(m_x) + ")"); } test& operator=(int x) { m_x = x; log("test::operator=(" + fplus::show(m_x) + ")"); return *this;} test& operator=(const test& t) { m_x = t.m_x; log("test::operator=(const test& " + fplus::show(m_x) + ")"); return *this;} test& operator=(test&& t) { m_x = std::move(t.m_x); log("test::operator=(test&& " + fplus::show(m_x) + ")"); return *this;} ~test() { log("~test(" + fplus::show(m_x) + ")"); } }; } TEST_CASE("shared_ref_test, full") { using namespace fplus; { auto ref = make_shared_ref(1); auto ref2 = ref; *ref2 = test(5); } { test o(2); auto ref = make_shared_ref(std::move(o)); } const std::vector logs_dest = { "test(1)", "test(5)", "test::operator=(test&& 5)", "~test(5)", "~test(5)", "test(2)", "test(test&& 2)", "~test(2)", "~test(2)" }; REQUIRE_EQ(logs, logs_dest); } libfplus-0.2.13/test/show_test.cpp000066400000000000000000000065741376322245400171430ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include namespace { typedef std::vector IntVector; IntVector xs = {1,2,2,3,2}; std::string xsShown("[1, 2, 2, 3, 2]"); typedef std::vector StringVector; StringVector stringVec = {"foo", "bar"}; std::string stringVecShown("[foo, bar]"); typedef std::set IntSet; IntSet xsSet = {1,2,3}; std::string xsSetShown("[1, 2, 3]"); typedef std::set StringSet; StringSet stringSet = {"foo", "bar"}; std::string stringSetShown("[bar, foo]"); } TEST_CASE("show_test, show") { using namespace fplus; std::map mapToShow = {{1, "one"}, {2, "two"}}; REQUIRE_EQ(show_cont(mapToShow), "[(1, one), (2, two)]"); REQUIRE_EQ(show_cont(xs), xsShown); REQUIRE_EQ(show(xs), xsShown); REQUIRE_EQ(show_cont(xsSet), xsSetShown); REQUIRE_EQ(show(xsSet), xsSetShown); REQUIRE_EQ(show_cont(stringVec), stringVecShown); REQUIRE_EQ(show(stringVec), stringVecShown); REQUIRE_EQ(show_cont(stringSet), stringSetShown); REQUIRE_EQ(show(stringSet), stringSetShown); REQUIRE_EQ(show_cont_with(", ", xs), xsShown); std::string xsShownNLs = "(1,2,\n" " 2,3,\n" " 2)"; REQUIRE_EQ(show_cont_with_frame_and_newlines(",", "(", ")", xs, 2), xsShownNLs); REQUIRE_EQ(show(1), "1"); REQUIRE_EQ(show(std::vector>({{1,2,3},{4,5,6}})), "[[1, 2, 3], [4, 5, 6]]"); } TEST_CASE("show_test, show_float") { using namespace fplus; const double pi = 3.14159; REQUIRE_EQ(show_float(0, 3, pi), "3.142"); REQUIRE_EQ(show_float(1, 3, pi), "3.142"); REQUIRE_EQ(show_float(2, 3, pi), "03.142"); REQUIRE_EQ(show_float(3, 3, pi), "003.142"); REQUIRE_EQ(show_float(1, 2, pi), "3.14"); REQUIRE_EQ(show_float(1, 4, pi), "3.1416"); REQUIRE_EQ(show_float(1, 7, pi), "3.1415900"); REQUIRE_EQ(show_float(0, 3, -pi), "-3.142"); REQUIRE_EQ(show_float(1, 3, -pi), "-3.142"); REQUIRE_EQ(show_float(2, 3, -pi), "-3.142"); REQUIRE_EQ(show_float(3, 3, -pi), "-03.142"); REQUIRE_EQ(show_float(4, 3, -pi), "-003.142"); REQUIRE_EQ(show_float(0, 3, 0.142), "0.142"); REQUIRE_EQ(show_float(1, 3, 0.142), "0.142"); REQUIRE_EQ(show_float(2, 3, 0.142), "00.142"); REQUIRE_EQ(fill_left(' ', 8, show_float(0, 3, -pi)), " -3.142"); REQUIRE_EQ(show_float_fill_left(' ', 8, 3, pi), " 3.142"); REQUIRE_EQ(show_float_fill_left(' ', 8, 6, pi), "3.141590"); REQUIRE_EQ(show_float_fill_left(' ', 8, 3, -pi), " -3.142"); REQUIRE_EQ(show_float_fill_left(' ', 2, 3, -pi), "-3.142"); REQUIRE_EQ(show_fill_left(' ', 4, 3), " 3"); REQUIRE_EQ(show_fill_left('0', 4, 3), "0003"); REQUIRE_EQ(show_fill_left(' ', 4, 12345), "12345"); REQUIRE_EQ(show_fill_right(' ', 4, 3), "3 "); REQUIRE_EQ(show_fill_right(' ', 4, 12345), "12345"); } libfplus-0.2.13/test/show_versions.cpp000066400000000000000000000017231376322245400200230ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include TEST_CASE("show_versions, print_defines") { #ifdef __cplusplus std::cout << "__cplusplus: " << __cplusplus << std::endl; #endif #ifdef _LIBCPP_VERSION std::cout << "_LIBCPP_VERSION: " << _LIBCPP_VERSION << std::endl; #endif #ifdef _LIBCPP_ABI_VERSION std::cout << "_LIBCPP_ABI_VERSION: " << _LIBCPP_ABI_VERSION << std::endl; #endif #ifdef _MSC_VER std::cout << "_MSC_VER: " << _MSC_VER << std::endl; #endif #ifdef __GLIBCPP__ std::cout << "__GLIBCPP__: " << __GLIBCPP__ << std::endl; #endif #ifdef __GLIBCXX__ std::cout << "__GLIBCXX__: " << __GLIBCXX__ << std::endl; #endif }libfplus-0.2.13/test/side_effects_test.cpp000066400000000000000000000063721376322245400206020ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include TEST_CASE("side_effects_test, execute") { using namespace fplus; std::vector buffer; auto push_one_return_true = [&]() -> bool { buffer.push_back(1); return true; }; execute_effect(push_one_return_true); REQUIRE_EQ(execute_effect(push_one_return_true), true); REQUIRE_EQ(buffer, std::vector({1,1})); } TEST_CASE("side_effects_test, execute_serially") { using namespace fplus; std::vector buffer; auto push_one_return_true = [&]() -> bool { buffer.push_back(1); return true; }; auto push_3_ones = execute_serially(replicate(3, push_one_return_true))(); REQUIRE_EQ(buffer, std::vector({1,1,1})); } TEST_CASE("side_effects_test, execute_parallelly") { using namespace fplus; auto return_one = [&](){ return 1; }; REQUIRE_EQ(execute_parallelly(replicate(4, return_one))(), std::vector({1,1,1,1})); //execute_fire_and_forget([](){std::cout << "Fired and forgotten." << std::endl;})(); } TEST_CASE("side_effects_test, execute_max_n_times_until_success") { using namespace fplus; std::vector buffer; auto push_one_return_false = [&]() -> bool { buffer.push_back(1); return false; }; execute_max_n_times_until_success(5, push_one_return_false)(); execute_max_n_times_until_success(3, push_one_return_false, 1)(); REQUIRE_EQ(buffer, std::vector({1,1,1,1,1,1,1,1})); } TEST_CASE("side_effects_test, execute_serially_until_success") { using namespace fplus; std::vector buffer; auto push_one_return_true = [&]() -> bool { buffer.push_back(1); return true; }; auto push_one_return_false = [&]() -> bool { buffer.push_back(1); return false; }; typedef std::function BoolReturningFunction; typedef std::vector BoolReturningFunctions; execute_serially_until_success(BoolReturningFunctions({push_one_return_false, push_one_return_false, push_one_return_true, push_one_return_true}))(); REQUIRE_EQ(buffer, std::vector({1,1,1})); } TEST_CASE("side_effects_test, execute_serially_until_failure") { using namespace fplus; std::vector buffer; auto push_one_return_true = [&]() -> bool { buffer.push_back(1); return true; }; auto push_one_return_false = [&]() -> bool { buffer.push_back(1); return false; }; typedef std::function BoolReturningFunction; typedef std::vector BoolReturningFunctions; execute_serially_until_failure(BoolReturningFunctions({push_one_return_true, push_one_return_false, push_one_return_false}))(); REQUIRE_EQ(buffer, std::vector({1,1})); } TEST_CASE("side_effects_test, execute_parallelly_atomic") { using namespace fplus; std::atomic atomic_int(0); auto inc_atomic_int_return_true = [&]() -> bool { ++atomic_int; return true;}; execute_parallelly(replicate(4, inc_atomic_int_return_true))(); REQUIRE_EQ(atomic_int.load(), 4); } libfplus-0.2.13/test/split_test.cpp000066400000000000000000000161651376322245400173130ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include namespace { typedef std::vector IntVector; IntVector xs = {1,2,2,3,2}; typedef std::vector IntVectors; typedef std::vector IdxVector; typedef std::list IntList; typedef std::vector IntLists; auto is_even_int = [](int x){ return x % 2 == 0; }; } TEST_CASE("split_test, split functions") { using namespace fplus; REQUIRE_EQ(split(0, true, IntVector{1,0,1}), IntVectors({{1},{1}})); REQUIRE_EQ(split(0, true, IntVector{0,1,0}), IntVectors({{},{1},{}})); REQUIRE_EQ(split(0, true, IntVector{0,1}), IntVectors({{},{1}})); REQUIRE_EQ(split(0, true, IntVector{1,0}), IntVectors({{1},{}})); REQUIRE_EQ(split(0, true, IntVector{0}), IntVectors({{},{}})); REQUIRE_EQ(split(0, true, IntVector{}), IntVectors{{}}); REQUIRE_EQ(split(0, false, IntVector{1,0,1}), IntVectors({{1},{1}})); REQUIRE_EQ(split(0, false, IntVector{0,1,0}), IntVectors{{1}}); REQUIRE_EQ(split(0, false, IntVector{0,1}), IntVectors{{1}}); REQUIRE_EQ(split(0, false, IntVector{1,0}), IntVectors{{1}}); REQUIRE_EQ(split(0, false, IntVector{0}), IntVectors({})); REQUIRE_EQ(split(0, false, IntVector{}), IntVectors({})); REQUIRE_EQ(split(0, true, IntVector{1,0,1}), IntVectors({{1},{1}})); REQUIRE_EQ(split(0, true, IntVector{0,1,0}), IntVectors({{},{1},{}})); REQUIRE_EQ(split(0, true, IntVector{0,1}), IntVectors({{},{1}})); REQUIRE_EQ(split(0, true, IntVector{1,0}), IntVectors({{1},{}})); REQUIRE_EQ(split(0, true, IntVector{0}), IntVectors({{},{}})); REQUIRE_EQ(split(0, true, IntVector{}), IntVectors{{}}); REQUIRE_EQ(split(0, false, IntVector{1,0,1}), IntVectors({{1},{1}})); REQUIRE_EQ(split(0, false, IntVector{0,1,0}), IntVectors{{1}}); REQUIRE_EQ(split(0, false, IntVector{0,1}), IntVectors{{1}}); REQUIRE_EQ(split(0, false, IntVector{1,0}), IntVectors{{1}}); REQUIRE_EQ(split(0, false, IntVector{0}), IntVectors({})); REQUIRE_EQ(split(0, false, IntVector{}), IntVectors({})); REQUIRE_EQ(split_by_token(IntVector({0}), true, IntVector{1,0,1}), IntVectors({{1},{1}})); REQUIRE_EQ(split_by_token(IntVector({0}), true, IntVector{0,1,0}), IntVectors({{},{1},{}})); REQUIRE_EQ(split_by_token(IntVector({0}), true, IntVector{0,1}), IntVectors({{},{1}})); REQUIRE_EQ(split_by_token(IntVector({0}), true, IntVector{1,0}), IntVectors({{1},{}})); REQUIRE_EQ(split_by_token(IntVector({0}), true, IntVector{0}), IntVectors({{},{}})); REQUIRE_EQ(split_by_token(IntVector({0}), true, IntVector{}), IntVectors{{}}); REQUIRE_EQ(split_by_token(IntVector({0}), false, IntVector{1,0,1}), IntVectors({{1},{1}})); REQUIRE_EQ(split_by_token(IntVector({0}), false, IntVector{0,1,0}), IntVectors{{1}}); REQUIRE_EQ(split_by_token(IntVector({0}), false, IntVector{0,1}), IntVectors{{1}}); REQUIRE_EQ(split_by_token(IntVector({0}), false, IntVector{1,0}), IntVectors{{1}}); REQUIRE_EQ(split_by_token(IntVector({0}), false, IntVector{0}), IntVectors({})); REQUIRE_EQ(split_by_token(IntVector({0}), false, IntVector{}), IntVectors({})); REQUIRE_EQ(split_by_token(IntVector({0,9}), true, IntVector{1,0,9,1}), IntVectors({{1},{1}})); REQUIRE_EQ(split_by_token(IntVector({0,9}), true, IntVector{0,9,1,0,9}), IntVectors({{},{1},{}})); REQUIRE_EQ(split_by_token(IntVector({0,9}), true, IntVector{0,9,1}), IntVectors({{},{1}})); REQUIRE_EQ(split_by_token(IntVector({0,9}), true, IntVector{1,0,9}), IntVectors({{1},{}})); REQUIRE_EQ(split_by_token(IntVector({0,9}), true, IntVector{0,9}), IntVectors({{},{}})); REQUIRE_EQ(split_by_token(IntVector({0,9}), true, IntVector{}), IntVectors{{}}); REQUIRE_EQ(split_by_token(IntVector({0,9}), false, IntVector{1,0,9,1}), IntVectors({{1},{1}})); REQUIRE_EQ(split_by_token(IntVector({0,9}), false, IntVector{0,9,1,0,9}), IntVectors{{1}}); REQUIRE_EQ(split_by_token(IntVector({0,9}), false, IntVector{0,9,1}), IntVectors{{1}}); REQUIRE_EQ(split_by_token(IntVector({0,9}), false, IntVector{1,0,9}), IntVectors{{1}}); REQUIRE_EQ(split_by_token(IntVector({0,9}), false, IntVector{0,9}), IntVectors({})); REQUIRE_EQ(split_by_token(IntVector({0,9}), false, IntVector{}), IntVectors({})); REQUIRE_EQ(split_by_token(IntVector({}), true, IntVector{}), IntVectors({{},{}})); REQUIRE_EQ(split_by_token(IntVector({}), true, IntVector{1}), IntVectors({{},{1},{}})); REQUIRE_EQ(split_by_token(IntVector({}), true, IntVector{1,2}), IntVectors({{},{1},{2},{}})); REQUIRE_EQ(split_by_token(IntVector({}), false, IntVector{}), IntVectors({})); REQUIRE_EQ(split_by_token(IntVector({}), false, IntVector{1}), IntVectors{{1}}); REQUIRE_EQ(split_by_token(IntVector({}), false, IntVector{1,2}), IntVectors({{1},{2}})); REQUIRE_EQ(split_one_of(IntVector({0,3}), true, IntVector{1,3,2,0,0,6,0,7,5}), IntVectors({{1},{2},{},{6},{7,5}})); REQUIRE_EQ(split_one_of(std::string(" ,.?"), false, std::string("Hi, how are you?")), std::vector({"Hi", "how", "are", "you"})); REQUIRE_EQ(split_at_idx(2, xs), std::make_pair(IntVector({1,2}), IntVector({2,3,2}))); REQUIRE_EQ(partition(is_even_int, xs), std::make_pair(IntVector({2,2,2}), IntVector({1,3}))); REQUIRE_EQ(split_every(2, xs), IntVectors({{1,2}, {2, 3}, {2}})); auto splittedAt1And3 = split_at_idxs(IdxVector({1,3}), xs); IntVectors splittedAt1And3Dest = {IntVector({1}), IntVector({2,2}), IntVector({3,2})}; REQUIRE_EQ(splittedAt1And3, splittedAt1And3Dest); auto splittedAt1And3And3 = split_at_idxs(IdxVector({1,3,3}), xs); IntVectors splittedAt1And3And3Dest = {IntVector({1}), IntVector({2,2}), IntVector({}), IntVector({3,2})}; REQUIRE_EQ(splittedAt1And3And3, splittedAt1And3And3Dest); REQUIRE_EQ(split(3, true, xs), IntVectors({IntVector({1, 2, 2}), IntVector({2})})); REQUIRE_EQ(split(1, true, IntVector{0,1,2}), IntVectors({{0},{2}})); REQUIRE_EQ(split(2, true, IntVector{5,2,0,3}), IntVectors({{5},{0,3}})); REQUIRE_EQ(split(2, true, IntVector{1,2}), IntVectors({{1},{}})); REQUIRE_EQ(split(2, true, IntVector{2}), IntVectors({{},{}})); REQUIRE_EQ(split(2, true, IntVector{}), IntVectors{{}}); REQUIRE_EQ(split_by(is_even_int, true, IntList({1,3,2,2,5,5,3,6,7,9})), IntLists({{1,3},{},{5,5,3},{7,9}})); REQUIRE_EQ(split_by_keep_separators(is_even_int, IntList({})), IntLists()); REQUIRE_EQ(split_by_keep_separators(is_even_int, IntList({2})), IntLists({IntList({2})})); REQUIRE_EQ(split_by_keep_separators(is_even_int, IntList({2,2,3})), IntLists({{2},{2,3}})); REQUIRE_EQ(split_by_keep_separators(is_even_int, IntList({1,3,2})), IntLists({{1,3},{2}})); REQUIRE_EQ(split_by_keep_separators(is_even_int, IntList({1,3,2,2,5,5,3,6,7,9})), IntLists({{1,3},{2},{2,5,5,3},{6,7,9}})); REQUIRE_EQ(split_keep_separators(2, IntList({1,3,2,2,5,5,3,2,7,9})), IntLists({{1,3},{2},{2,5,5,3},{2,7,9}})); }libfplus-0.2.13/test/stopwatch_test.cpp000066400000000000000000000026311376322245400201650ustar00rootroot00000000000000// (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include using namespace std::chrono_literals; template auto invoke_n_times(int n, Function f) { for (int i = 0; i < n; i++) { f(); } } TEST_CASE("Timer, test_accuracy") { #ifdef NDEBUG // only on release builds using namespace fplus; using namespace std::chrono_literals; auto measure_delta = []() { fplus::stopwatch t; std::this_thread::sleep_for(0.05s); auto duration = t.elapsed(); auto delta = duration - 0.05; return fabs(delta); }; // we ask for a sleep of 0.05s, but we will have a duration that can be higher // (up to 15 ms higher, since the cpu scheduler might switch to another process during this sleep) // to account for the cpu/task scheduler double max_acceptable_delta__task_scheduler = 0.02; // One run { auto delta = measure_delta(); REQUIRE_LT(delta, max_acceptable_delta__task_scheduler); } // 10 consecutive runs (total duration = 0.5 seconds) { std::vector deltas; invoke_n_times(10, [&]() { deltas.push_back(measure_delta()); }); auto mean_dev = fplus::mean_stddev(deltas); REQUIRE_LT(mean_dev.first, 0.03); REQUIRE_LT(mean_dev.second, 0.01); } #endif } libfplus-0.2.13/test/stringtools_cp1251_test.cpp000066400000000000000000000022301376322245400215260ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include #include "get_locale.hpp" TEST_CASE("stringtools_test, to_lower/upper_case, cp1251") { using namespace fplus; const std::locale loc = get_locale( #ifdef WIN32 "ru-RU" #else "ru_RU.cp1251" #endif ); auto lower = fwd::to_lower_case_loc(loc); auto upper = fwd::to_upper_case_loc(loc); REQUIRE_EQ(lower(std::string("cYrIlLiC 123&? ")), std::string("cyrillic 123&? ")); REQUIRE_EQ(lower(std::string("Ũ")), std::string("")); REQUIRE_EQ(upper(std::string("cYrIlLiC 123&? ")), std::string("CYRILLIC 123&? ")); REQUIRE_EQ(upper(std::string("")), std::string("Ũ")); } libfplus-0.2.13/test/stringtools_cp1253_test.cpp000066400000000000000000000022311376322245400215310ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include #include "get_locale.hpp" TEST_CASE("stringtools_test, to_lower/upper_case, cp1253") { using namespace fplus; const std::locale loc = get_locale( #if defined(WIN32) "el-GR" #elif defined(__APPLE__) "el_GR.ISO8859-7" #else "el_GR.cp1253" #endif ); auto lower = fwd::to_lower_case_loc(loc); auto upper = fwd::to_upper_case_loc(loc); REQUIRE_EQ(lower(std::string("GrEeCe 123&? ")), std::string("greece 123&? ")); REQUIRE_EQ(lower(std::string("")), std::string("")); REQUIRE_EQ(upper(std::string("GrEeCe 123&? ")), std::string("GREECE 123&? ")); REQUIRE_EQ(upper(std::string("")), std::string("")); } libfplus-0.2.13/test/stringtools_test.cpp000066400000000000000000000053171376322245400205440ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include TEST_CASE("stringtools_test, trim") { using namespace fplus; std::string untrimmed = " \n \t foo "; REQUIRE_EQ(trim_whitespace_left(untrimmed), "foo "); REQUIRE_EQ(trim_whitespace_right(untrimmed), " \n \t foo"); REQUIRE_EQ(trim_whitespace(untrimmed), "foo"); } TEST_CASE("stringtools_test, split") { using namespace fplus; std::string text = "Hi,\nI am a\r\n***strange***\n\rstring."; std::vector textAsLinesWithEmty = { std::string("Hi,"), std::string("I am a"), std::string("***strange***"), std::string(""), std::string("string.") }; std::vector textAsLinesWithoutEmpty = { std::string("Hi,"), std::string("I am a"), std::string("***strange***"), std::string("string.") }; std::vector textAsWords = { std::string("Hi"), std::string("I"), std::string("am"), std::string("a"), std::string("strange"), std::string("string") }; std::vector textSplitBySpaceOnly = { std::string("Hi,\nI"), std::string("am"), std::string("a\r\n***strange***\n\rstring.") }; std::vector textSplitBySpaceAndCommaAndLine = { std::string("Hi"), std::string("I"), std::string("am"), std::string("a"), std::string("***strange***"), std::string("string.") }; REQUIRE_EQ(split_lines(true, text), textAsLinesWithEmty); REQUIRE_EQ(split_lines(false, text), textAsLinesWithoutEmpty); REQUIRE_EQ(split_words(false, text), textAsWords); REQUIRE_EQ(split(' ', false, text), textSplitBySpaceOnly); REQUIRE_EQ(split_one_of(std::string{ " ,\r\n" }, false, text), textSplitBySpaceAndCommaAndLine); } TEST_CASE("stringtools_test, to_string_filled") { using namespace fplus; REQUIRE_EQ(to_string_fill_left('0', 5, 42), std::string("00042") ); REQUIRE_EQ(to_string_fill_right(' ', 5, 42), std::string("42 ") ); } TEST_CASE("stringtools_test, to_lower_case") { using namespace fplus; REQUIRE_EQ(to_lower_case(std::string("ChaRacTer&WorDs23")), std::string("character&words23")); } TEST_CASE("stringtools_test, to_upper_case") { using namespace fplus; REQUIRE_EQ(to_upper_case(std::string("ChaRacTer&WorDs34")), std::string("CHARACTER&WORDS34")); } libfplus-0.2.13/test/stringtools_utf8_test.cpp000066400000000000000000000040571376322245400215120ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include #include "get_locale.hpp" //TODO: google says there is no UTF8 locale on Windows, is it so? TEST_CASE("stringtools_test, to_lower/upper_case, utf8") { #ifndef _MSC_VER // FATAL ERROR: Couldn't acquire locale: bad locale name. Is 'ru_RU.utf8' supported on your system? using namespace fplus; const std::locale loc = get_locale( #ifndef __APPLE__ "ru_RU.utf8" #else "ru_RU.UTF-8" #endif ); auto lower = fwd::to_lower_case_loc(loc); auto upper = fwd::to_upper_case_loc(loc); REQUIRE_EQ(lower(std::wstring(L"cYrIlLiC 123&? КиРиЛлИцА")), std::wstring(L"cyrillic 123&? кириллица")); REQUIRE_EQ(lower(std::wstring(L"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ")), std::wstring(L"абвгдеёжзийклмнопрстуфхцчшщъыьэюя")); REQUIRE_EQ(upper(std::wstring(L"cYrIlLiC 123&? КиРиЛлИцА")), std::wstring(L"CYRILLIC 123&? КИРИЛЛИЦА")); REQUIRE_EQ(upper(std::wstring(L"абвгдеёжзийклмнопрстуфхцчшщъыьэюя")), std::wstring(L"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ")); REQUIRE_EQ(lower(std::wstring(L"GrEeCe 123&? ΕλΛαΔα")), std::wstring(L"greece 123&? ελλαδα")); REQUIRE_EQ(lower(std::wstring(L"ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ")), std::wstring(L"αβγδεζηθικλμνξοπρστυφχψω")); REQUIRE_EQ(upper(std::wstring(L"GrEeCe 123&? ΕλΛαΔα")), std::wstring(L"GREECE 123&? ΕΛΛΑΔΑ")); REQUIRE_EQ(upper(std::wstring(L"αβγδεζηθικλμνξοπρστυφχψω")), std::wstring(L"ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ")); #endif } libfplus-0.2.13/test/timed_test.cpp000066400000000000000000000117721376322245400172610ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include #include #include // Utility functions namespace { void require_are_execution_times_close(double t1, double t2) { // up to 30 ms difference, since the cpu scheduler might switch to another process during a sleep double max_acceptable_delta__task_scheduler = 0.03; #if defined(__APPLE__) max_acceptable_delta__task_scheduler = 0.1; // travis osx builders are slow... #endif REQUIRE(fabs(t1 - t2) < max_acceptable_delta__task_scheduler); } template void require_are_timed_equal(const fplus::timed & a, const fplus::timed & b) { REQUIRE(a.get() == b.get()); require_are_execution_times_close(a.time_in_s(), b.time_in_s()); } void sleep_seconds(double sleep_seconds) { long long sleep_ns = static_cast(sleep_seconds * 1E9); std::this_thread::sleep_for(std::chrono::nanoseconds(sleep_ns)); } int add(int a, int b) // a simple function that will be decorated { sleep_seconds(0.002); return a + b; } void void_function() { sleep_seconds(0.002); } } // Test timed class TEST_CASE("timed, ctor") { using namespace fplus; { // default constructor fplus::timed v; REQUIRE(v.get() == doctest::Approx(0.)); REQUIRE(v.time_in_s() == doctest::Approx(0.)); } { // T + time constructor fplus::timed v(1., 3.); REQUIRE(v.get() == doctest::Approx(1.)); REQUIRE(v.time_in_s() == doctest::Approx(3.)); } { // timed constructor fplus::timed v1(2.); auto v2(v1); REQUIRE(v1.time_in_s() == doctest::Approx(v2.time_in_s())); REQUIRE(v1.get() == doctest::Approx(v2.get())); } } TEST_CASE("timed, operator=") { { auto v1 = fplus::timed(1.); auto v2 = fplus::timed(2.); REQUIRE(v1.get() != doctest::Approx(v2.get())); v2 = v1; REQUIRE(v1.get() == doctest::Approx(v2.get())); } } TEST_CASE("timed, show_timed") { { fplus::timed v(42, 1); auto s = show_timed(v); REQUIRE_EQ(s, "42 (1000ms)"); } } TEST_CASE("timed, duration_in_s") { { fplus::timed v(42, 1.2345); auto d = v.duration_in_s(); double seconds = d.count(); REQUIRE( seconds == doctest::Approx(1.2345) ); } } // Test make_timed_function TEST_CASE("make_timed_function") { using namespace fplus; { // Test decorated function auto add_timed = make_timed_function(add); auto result = add_timed(39, 3); auto expected = timed(42, 0.02); require_are_timed_equal(result, expected); } { // Test void function auto void_function_timed = fplus::make_timed_void_function(void_function); auto execution_time = void_function_timed(); require_are_execution_times_close(execution_time, 0.02); } { // Test decorated lambda auto sub = [](int a, int b) -> int { sleep_seconds(0.03); return a - b; }; auto sub_timed = make_timed_function(sub); auto result = sub_timed(45, 3); auto expected = timed(42, 0.03); require_are_timed_equal(result, expected); } { // Test decorated void lambda auto fn = []() { sleep_seconds(0.03); }; auto fn_timed = make_timed_void_function(fn); auto execution_time = fn_timed(); require_are_execution_times_close(execution_time, 0.03); } { // Test std::function auto sub_lambda = [](int a, int b) -> int { sleep_seconds(0.03); return a - b; }; std::function sub = sub_lambda; auto sub_timed = make_timed_function(sub); auto result = sub_timed(45, 3); auto expected = timed(42, 0.03); require_are_timed_equal(result, expected); } { using Ints = std::vector; Ints ascending_numbers = fplus::numbers(0, 1000); Ints shuffled_numbers = fplus::shuffle(std::mt19937::default_seed, ascending_numbers); auto sort_func = [](const Ints& values) { return fplus::sort(values); }; auto sort_bench = fplus::make_timed_function(sort_func); auto sorted_numbers = sort_bench(shuffled_numbers); REQUIRE_EQ(sorted_numbers.get(), ascending_numbers); // sorting 1000 numbers should require less than 0.1 seconds (in practice it requires about 0.2ms) REQUIRE_LT(sorted_numbers.time_in_s(), 0.1); } } libfplus-0.2.13/test/transform_test.cpp000066400000000000000000000170341376322245400201670ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include namespace { typedef std::vector IntVector; typedef std::vector IntVectors; IntVector xs = {1,2,2,3,2}; auto squareLambda = [](auto x) { return x*x; }; typedef std::array IntArray5; IntArray5 xs_array = {{1,2,2,3,2}}; typedef std::list IntList; IntList intList = { 1,2,2,3,2 }; bool is_even(int value) { return ( value % 2 == 0 ); } } TEST_CASE("transform_test, replicate_elems") { using namespace fplus; REQUIRE_EQ(replicate_elems(2, xs), IntVector({1,1,2,2,2,2,3,3,2,2})); } TEST_CASE("transform_test, interleave") { using namespace fplus; REQUIRE_EQ(interleave(IntVectors()), IntVector()); REQUIRE_EQ(interleave(IntVectors({})), IntVector()); REQUIRE_EQ(interleave(IntVectors({IntVector({})})), IntVector()); REQUIRE_EQ(interleave(IntVectors({{},{}})), IntVector()); REQUIRE_EQ(interleave(IntVectors({{1},{}})), IntVector({1})); REQUIRE_EQ(interleave(IntVectors({{1,2,3},{4,5},{6,7,8}})), IntVector({1,4,6,2,5,7,3,8})); REQUIRE_EQ(interleave(IntVectors({{1,2,3},{4,5},{6,7,8}})), IntVector({1,4,6,2,5,7,3,8})); } TEST_CASE("transform_test, transpose") { using namespace fplus; REQUIRE_EQ(transpose(IntVectors()), IntVectors()); REQUIRE_EQ(transpose(IntVectors({})), IntVectors()); REQUIRE_EQ(transpose(IntVectors({},{})), IntVectors()); REQUIRE_EQ(transpose(IntVectors({ { 1 }, { 2 } })), IntVectors({ { 1, 2 } })); REQUIRE_EQ(transpose(IntVectors({ { 1, 2 } })), IntVectors({ { 1 }, { 2 } })); REQUIRE_EQ(transpose(IntVectors({ { 1, 2 }, { 3, 4 } })), IntVectors({ { 1, 3 }, { 2, 4 } })); REQUIRE_EQ(transpose(IntVectors({ { 1, 2, 3 }, { 4, 5, 6 } })), IntVectors({ { 1, 4 }, { 2, 5 }, { 3, 6 } })); REQUIRE_EQ(transpose(IntVectors({{1,2,3},{4,5,6},{7,8,9}})), IntVectors({{1,4,7},{2,5,8},{3,6,9}})); REQUIRE_EQ(transpose(IntVectors({{1,2,3},{4,5},{7,8,9}})), IntVectors({{1,4,7},{2,5,8},{3,9}})); } TEST_CASE("transform_test, shuffle") { using namespace fplus; const auto shuffled1 = shuffle(std::mt19937::default_seed, xs); REQUIRE(is_permutation_of(shuffled1, xs)); const auto shuffled1_rvalue = shuffle(std::mt19937::default_seed, IntVector({1,2,2,3,2})); REQUIRE(is_permutation_of(shuffled1_rvalue, xs)); const auto shuffled2 = shuffle(std::random_device()(), xs); REQUIRE(is_permutation_of(shuffled2, xs)); } TEST_CASE("transform_test, random_element") { using namespace fplus; const auto elem1 = random_element(std::mt19937::default_seed, xs); REQUIRE(is_elem_of(elem1, xs)); const auto elem2 = random_element(std::random_device()(), xs); REQUIRE(is_elem_of(elem2, xs)); } TEST_CASE("transform_test, random_elements") { using namespace fplus; const auto check_is_elem_of_xs = bind_2nd_of_2(is_elem_of, xs); const auto sampled1 = random_elements(std::mt19937::default_seed, 23, xs); REQUIRE_EQ(sampled1.size(), 23); REQUIRE(all_by(check_is_elem_of_xs, sampled1)); const auto sampled2 = random_elements(std::random_device()(), 23, xs); REQUIRE_EQ(sampled2.size(), 23); REQUIRE(all_by(check_is_elem_of_xs, sampled2)); } TEST_CASE("transform_test, sample") { using namespace fplus; const auto check_is_elem_of_xs = bind_2nd_of_2(is_elem_of, xs); const auto sampled1 = sample(std::mt19937::default_seed, 3, xs); REQUIRE_EQ(sampled1.size(), 3); REQUIRE(all_by(check_is_elem_of_xs, sampled1)); const auto sampled2 = sample(std::random_device()(), 3, xs); REQUIRE_EQ(sampled2.size(), 3); REQUIRE(all_by(check_is_elem_of_xs, sampled2)); } TEST_CASE("transform_test, transform") { using namespace fplus; auto intTimes2 = [](int x) -> int { return x*2; }; auto intTimes3 = [](int x) -> int { return x*3; }; REQUIRE_EQ(transform([](auto x) { return x*x; }, xs), IntVector({1,4,4,9,4})); std::initializer_list initListInts = { 1,2,2,3,2 }; REQUIRE_EQ(transform(squareLambda, std::vector(initListInts)), IntVector({1,4,4,9,4})); REQUIRE_EQ(transform_convert>(squareLambda, initListInts), IntVector({1,4,4,9,4})); REQUIRE_EQ(transform_convert>(squareLambda, xs_array), IntVector({1,4,4,9,4})); REQUIRE_EQ(transform(squareLambda, xs), IntVector({1,4,4,9,4})); REQUIRE_EQ(transform(squareLambda, intList), IntList({ 1,4,4,9,4 })); REQUIRE_EQ(transform_parallelly(squareLambda, intList), IntList({ 1,4,4,9,4 })); REQUIRE_EQ(transform_parallelly_n_threads(3, squareLambda, intList), IntList({ 1,4,4,9,4 })); REQUIRE_EQ(transform_convert(squareLambda, xs), IntList({ 1,4,4,9,4 })); REQUIRE_EQ(transform(squareLambda, std::set({1,2,3,-3})), std::set({1,4,9})); REQUIRE_EQ(transform_inner(intTimes2, IntVectors({{1,3,4},{1,2}})), IntVectors({{2,6,8},{2,4}})); typedef std::array IntArray2; typedef std::array IntArrays2_3; IntArrays2_3 int_arrays = {{IntArray2({{1,2}}),IntArray2({{2,3}}),IntArray2({{3,4}})}}; IntArrays2_3 int_arrays_times_2 = {{IntArray2({{2,4}}),IntArray2({{4,6}}),IntArray2({{6,8}})}}; REQUIRE_EQ(transform_inner(intTimes2, int_arrays), int_arrays_times_2); auto add_size_t_and_int = [](std::size_t a, int b) -> int { return static_cast(a) + b; }; REQUIRE_EQ(transform_with_idx(add_size_t_and_int, xs), IntVector({1+0,2+1,2+2,3+3,2+4})); std::vector> multiplyFunctions = {intTimes2, intTimes3}; REQUIRE_EQ(apply_functions(multiplyFunctions, 4), IntVector({8, 12})); auto showInt = [](auto x) { return fplus::show(x); }; std::vector showIntFuncs = {showInt, showInt}; REQUIRE_EQ(apply_functions(showIntFuncs, 4), std::vector({"4", "4"})); REQUIRE_EQ(apply_function_n_times(squareLambda, 3, 2), 256); REQUIRE_EQ(transform(squareLambda, xs_array), IntArray5({{1,4,4,9,4}})); } TEST_CASE("transform_test, reduce") { using namespace fplus; REQUIRE_EQ(reduce_parallelly(std::plus(), 100, xs), 110); REQUIRE_EQ(reduce_1_parallelly(std::plus(), xs), 10); } TEST_CASE("transform_test, keep_if_parallelly") { const std::vector v = {1, 2, 3, 2, 4, 5}; auto result = fplus::keep_if_parallelly(is_even, v); REQUIRE_EQ(result, std::vector({2, 2, 4})); } TEST_CASE("transform_test, transform_reduce") { const std::vector v = {1, 2, 3, 4, 5}; auto result = fplus::transform_reduce( fplus::square, std::plus(), 0, v); REQUIRE_EQ(result, 55); } TEST_CASE("transform_test, transform_reduce_1") { const std::vector v = {1, 2, 3, 4, 5}; auto result = fplus::transform_reduce_1( fplus::square, std::plus(), v); REQUIRE_EQ(result, 55); } TEST_CASE("transform_test, transform_reduce_parallelly") { const std::vector v = {1, 2, 3, 4, 5}; auto result = fplus::transform_reduce_parallelly( fplus::square, std::plus(), 0, v); REQUIRE_EQ(result, 55); } TEST_CASE("transform_test, transform_reduce_1_parallelly") { const std::vector v = {1, 2, 3, 4, 5}; auto result = fplus::transform_reduce_1_parallelly( fplus::square, std::plus(), v); REQUIRE_EQ(result, 55); } libfplus-0.2.13/test/tree_test.cpp000066400000000000000000000106001376322245400171030ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include namespace { typedef std::pair IntPair; typedef std::vector IntVector; typedef std::vector IntPairVector; typedef fplus::tree IntPairTree; typedef fplus::tree IntTree; typedef std::vector IntPairTreeVector; } TEST_CASE("tree_test, are_trees_equal") { using namespace fplus; const IntPairTree a = {{0, 8}, { {{0, 4}, {}}, {{5, 7}, {}}}}; const IntPairTree b = {{0, 8}, { {{5, 7}, {}}, {{0, 4}, {}}}}; REQUIRE(are_trees_equal(a, b)); } TEST_CASE("tree_test, sequence_to_tree small") { using namespace fplus; IntPairVector elems = { {0, 4}, {0, 8}, {5, 7} }; const auto is_child_of = [](const IntPair& a, const IntPair& b) -> bool { return a.first >= b.first && a.second <= b.second; }; const auto result_1 = trees_from_sequence(is_child_of, elems); const auto result_2 = trees_from_sequence(is_child_of, fplus::shuffle(0, elems)); const IntPairTreeVector result = { {{0, 8}, { {{0, 4}, {}}, {{5, 7}, {}}}} }; REQUIRE_EQ(result_1.size(), result_2.size()); REQUIRE(all(zip_with(are_trees_equal, result_1, result_2))); REQUIRE_EQ(result_1.size(), result.size()); REQUIRE(all(zip_with(are_trees_equal, result_1, result))); } TEST_CASE("tree_test, sequence_to_tree medium") { using namespace fplus; IntPairVector elems = { {0, 4}, {0, 8}, {5, 7}, {9, 10}, {12, 13}, {9, 17}, {11, 15}, {13, 14}, {8, 20} }; const auto is_child_of = [](const IntPair& a, const IntPair& b) -> bool { return a.first >= b.first && a.second <= b.second; }; const auto result_1 = trees_from_sequence(is_child_of, elems); const auto result_2 = trees_from_sequence(is_child_of, fplus::shuffle(0, elems)); const IntPairTreeVector result = { {{0, 8}, { {{0, 4}, {}}, {{5, 7}, {}} }}, {{8, 20}, { {{9, 17}, { {{9, 10}, {}}, {{11, 15}, { {{12, 13}, {}}, {{13, 14}, {}} }} }}, }}, }; REQUIRE_EQ(result_1.size(), result_2.size()); REQUIRE(all(zip_with(are_trees_equal, result_1, result_2))); REQUIRE_EQ(result_1.size(), result.size()); REQUIRE(all(zip_with(are_trees_equal, result_1, result))); } TEST_CASE("tree_test, tree_depth") { const IntTree t = {{0}, { {{1}, { {{2}, { }}, {{3}, { {{4}, { }}, {{5}, { }} }}, {{6}, { }} }}, {{7}, { }}, {{8}, { }} }}; REQUIRE_EQ(tree_depth(t), 4); } TEST_CASE("tree_test, flatten_tree_depth_first") { const IntTree t = {{0}, { {{1}, { {{2}, { }}, {{3}, { {{4}, { }}, {{5}, { }} }}, {{6}, { }} }}, {{7}, { }}, {{8}, { }} }}; REQUIRE_EQ(flatten_tree_depth_first(t), IntVector({0,1,2,3,4,5,6,7,8})); } TEST_CASE("tree_test, flatten_tree_breadth_first") { const IntTree t = {{0}, { {{1}, { {{4}, { }}, {{5}, { {{7}, { }}, {{8}, { }} }}, {{6}, { }} }}, {{2}, { }}, {{3}, { }} }}; REQUIRE_EQ(flatten_tree_breadth_first(t), IntVector({0,1,2,3,4,5,6,7,8})); } libfplus-0.2.13/test/udemy_course_test.cpp000066400000000000000000000342331376322245400206570ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // tests for exercise solutions of the Udemy course // "Functional Programming using C++" // https://www.udemy.com/functional-programming-using-cpp/ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include #include namespace Correctness_follows_from_expressiveness { bool is_even(int x) { return x % 2 == 0; } template Cont keep_if(Pred pred, const Cont& xs) { Cont ys; for (const auto x : xs) { if (pred(x)) { ys.push_back(x); } } return ys; } } TEST_CASE("udemy_course_test, Correctness_follows_from_expressiveness") { using namespace Correctness_follows_from_expressiveness; std::vector xs = {0,1,2,3,4}; const auto ys = keep_if(is_even, xs); REQUIRE_EQ(ys, std::vector({0,2,4})); } namespace Programming_challenge_parse_and_product { double str_to_double(const std::string& str) { double result; std::istringstream(str) >> result; return result; } } TEST_CASE("udemy_course_test, Programming_challenge_parse_and_product") { using namespace Programming_challenge_parse_and_product; const std::string input = "1,5,4,7,2,2,3.34"; const auto parts = fplus::split(',', false, input); const auto nums = fplus::transform(str_to_double, parts); const auto result = fplus::reduce(std::plus(), 1, nums); REQUIRE(fplus::is_in_interval_around(0.001, 25.34, result)); } namespace Programming_challenge_longest_edge_of_polygon { typedef std::pair point; float point_distance(const point& p1, const point& p2) { const float dx = p2.first - p1.first; const float dy = p2.second - p1.second; return std::sqrt(dx * dx + dy * dy); } } TEST_CASE("udemy_course_test, Programming_challenge_longest_edge_of_polygon") { using namespace std; using namespace Programming_challenge_longest_edge_of_polygon; vector polygon = { {1.f,2.f}, {7.f,3.f}, {6.f,5.f}, {4.f,4.f}, {2.f,9.f} }; const auto edges = fplus::overlapping_pairs_cyclic(polygon); const auto result = fplus::maximum_on( [](const std::pair& edge) -> float { return point_distance(edge.first, edge.second); }, edges); REQUIRE_EQ(fplus::show(result), std::string("((2, 9), (1, 2))")); } namespace The_problem_with_comments { int str_to_int(const std::string& str) { int result; std::istringstream(str) >> result; return result; } template typename Cont::value_type product(const Cont& xs) { return fplus::reduce(std::multiplies(), 1, xs); } template typename Cont::value_type sum(const Cont& xs) { return fplus::reduce(std::plus(), 1, xs); } } TEST_CASE("udemy_course_test, The_problem_with_comments") { using namespace The_problem_with_comments; const std::string input = "1,5,4,7,2,2,3"; const auto parts = fplus::split(',', false, input); const auto nums = fplus::transform(str_to_int, parts); const auto result = product(nums); // sum(nums) REQUIRE_EQ(result, 1680); } namespace High_level_expressiveness_and_concise_code { typedef std::pair point; typedef std::pair edge; typedef std::vector points; float point_distance(const point& p1, const point& p2) { const float dx = p2.first - p1.first; const float dy = p2.second - p1.second; return std::sqrt(dx * dx + dy * dy); } float edge_length(const edge& e) { return point_distance(e.first, e.second); } std::vector get_edges(const points& polygon) { return fplus::overlapping_pairs_cyclic(polygon); } } TEST_CASE("udemy_course_test, High_level_expressiveness_and_concise_code") { using namespace std; using namespace High_level_expressiveness_and_concise_code; vector polygon = { {1.f,2.f}, {7.f,3.f}, {6.f,5.f}, {4.f,4.f}, {2.f,9.f} }; const auto result = fplus::maximum_on( edge_length, get_edges(polygon)); REQUIRE_EQ(fplus::show(result), std::string("((2, 9), (1, 2))")); } namespace Currying_and_partial_function_application { } TEST_CASE("udemy_course_test, Currying_and_partial_function_application") { using namespace Currying_and_partial_function_application; std::vector> xss = {{0,1,2}, {3,4,5}}; // 1 fplus::transform(fplus::fwd::transform(fplus::square), xss); // 2 const auto add_four_curried = [](int a) { return [a](int b) { return [a, b](int c) { return [a, b, c](int d) { return a + b + c + d; }; }; }; }; REQUIRE_EQ(add_four_curried(1)(2)(3)(4), 1+2+3+4); } namespace Forward_application { typedef std::pair point; typedef std::pair edge; typedef std::vector points; float point_distance(const point& p1, const point& p2) { const float dx = p2.first - p1.first; const float dy = p2.second - p1.second; return std::sqrt(dx * dx + dy * dy); } float edge_length(const edge& e) { return point_distance(e.first, e.second); } std::vector get_edges(const points& polygon) { return fplus::overlapping_pairs_cyclic(polygon); } } TEST_CASE("udemy_course_test, Forward_application") { using namespace std; using namespace Forward_application; vector polygon = { {1.f,2.f}, {7.f,3.f}, {6.f,5.f}, {4.f,4.f}, {2.f,9.f} }; // 1: const auto result = fplus::fwd::apply(polygon , get_edges , fplus::fwd::maximum_on(edge_length)); REQUIRE_EQ(fplus::show(result), std::string("((2, 9), (1, 2))")); // 2: int a = 3; // intermediate values int b = fplus::square(a); int c = fplus::min_2(2, b); int d = fplus::abs_diff(7, c); int e = fplus::clamp(1, 4, d); int f = fplus::max_2(6, e); REQUIRE_EQ(f, 6); // nested function calls int f_nested = fplus::max_2(6, fplus::clamp(1, 4, fplus::abs_diff(7, fplus::min_2(2, fplus::square(a))))); REQUIRE_EQ(f_nested, 6); // foward-application style int f_fwd = fplus::fwd::apply(a , fplus::fwd::square() , fplus::fwd::min_2(2) , fplus::fwd::abs_diff(7) , fplus::fwd::clamp(1, 4) , fplus::fwd::max_2(6)); REQUIRE_EQ(f_fwd, 6); } namespace Programming_challenge_Interacting_with_the_command_line { // cmd_line_interact : (String -> String) -> () template void cmd_line_interact(F) { // no side effects in unit tests } } TEST_CASE("udemy_course_test, Programming_challenge_Interacting_with_the_command_line") { using namespace fplus; using namespace Programming_challenge_Interacting_with_the_command_line; // 1: cmd_line_interact(fwd::to_upper_case()); // 2: cmd_line_interact( fwd::compose( fwd::split_lines(false), fwd::sort(), fwd::join(std::string("\n")))); } namespace Function_composition { double str_to_double(const std::string& str) { double result; std::istringstream(str) >> result; return result; } const auto parse_and_product = fplus::fwd::compose( fplus::fwd::split(',', false), fplus::fwd::transform(str_to_double), fplus::fwd::product()); } TEST_CASE("udemy_course_test, Function_composition") { using namespace Function_composition; const std::string input = "1,5,4,7,2,2,3.34"; const auto result = parse_and_product(input); REQUIRE(fplus::is_in_interval_around(0.001, 1870.4, result)); } namespace Programming_challenge_an_SQL_analogy { struct user { std::string name; std::string country; std::size_t visits; }; std::string get_country(const user& u) { return u.country; } std::size_t get_visits(const user& u) { return u.visits; } } TEST_CASE("udemy_course_test, Programming_challenge_an_SQL_analogy") { using namespace Programming_challenge_an_SQL_analogy; const std::vector users = { {"Nicole", "GER", 2}, {"Justin", "USA", 1}, {"Rachel", "USA", 5}, {"Robert", "USA", 6}, {"Stefan", "GER", 4} }; const auto visit_sum = [](const std::vector& xs) -> std::size_t { return fplus::fwd::apply(xs , fplus::fwd::transform(get_visits) , fplus::fwd::sum()); }; // n^2 const auto result = fplus::fwd::apply(users , fplus::fwd::group_globally_on_labeled(get_country) , fplus::fwd::transform(fplus::fwd::transform_snd(visit_sum)) ); REQUIRE_EQ(fplus::show_cont(result), std::string("[(GER, 6), (USA, 12)]")); // n * log(n) const auto result_n_log_n = fplus::fwd::apply(users , fplus::fwd::sort_on(get_country) , fplus::fwd::group_on_labeled(get_country) , fplus::fwd::transform(fplus::fwd::transform_snd(visit_sum)) ); REQUIRE_EQ(fplus::show_cont(result_n_log_n), std::string("[(GER, 6), (USA, 12)]")); } namespace Functors { template std::map lift_dict(F f, const std::map& dict) { std::map result; for (const auto& key_and_val : dict) { result[key_and_val.first] = f(key_and_val.second); } return result; } } TEST_CASE("udemy_course_test, Functors") { using namespace Functors; using namespace fplus; std::map dict = {{2, 1.41}, {3, 1.73}, {4, 2.0}}; auto dict_squared = lift_dict(square, dict); auto dict_shown = lift_dict(show, dict); REQUIRE_EQ(show_cont(dict_squared), "[(2, 1.9881), (3, 2.9929), (4, 4)]"); REQUIRE_EQ(show_cont(dict_shown), "[(2, 1.41), (3, 1.73), (4, 2)]"); } namespace Monads { using namespace std; using namespace fplus; typedef vector Ints; typedef vector Strings; result get_input_filepath(const Strings& args) { assert(args.size() > 0); if (args.size() != 2) return error( "Usage: " + args[0] + " FILEPATH"); else return ok(args[1]); } result read_file(const string&) { // dummy, no side effects in tests return ok("1,1,1,4"); } result parse_content(const string& content) { const auto maybe_values = fwd::apply(content , fwd::split(',', false) , fwd::transform(read_value)); if (all_by(is_just, maybe_values)) return ok(justs(maybe_values)); else return error("Can not parse file."); } result calc_median(const vector& xs) { if (is_not_empty(xs)) return ok(median(xs)); return error("Need at least one value."); } string show_median(int value) { return "The median is " + show(value); } string show_error(const string& error) { return "ERROR: " + error; } template result my_and_then_result( F f, const result& r) { if (is_ok(r)) return f(unsafe_get_ok(r)); else return error(r.unsafe_get_error()); } } TEST_CASE("udemy_course_test, Monads") { using namespace fplus; using namespace std; using namespace Monads; const Strings arguments = {"executable", "input.txt"}; const string error_msg = "An error occured."; const auto input_filepath = get_input_filepath(arguments); const auto file_content = my_and_then_result(read_file, input_filepath); const auto values = my_and_then_result(parse_content, file_content); const auto res = my_and_then_result(calc_median, values); const auto output = unify_result(show_median, show_error, res); REQUIRE_EQ(output, std::string("The median is 1")); } namespace Multithreading { struct Image {}; // dummy struct FaceImage {}; // dummy std::vector images; FaceImage extract_face(Image) { return {}; } // dummy FaceImage empty_face_image; // dummy FaceImage add_face_images(FaceImage, FaceImage) { return {}; } // dummy FaceImage divide_values(FaceImage, std::size_t) { return {}; } // dummy } TEST_CASE("udemy_course_test, Multithreading") { using namespace Multithreading; fplus::transform_reduce_parallelly( extract_face, add_face_images, empty_face_image, images); } namespace OOP_Design_patterns_vanishing { int square(int x) { return x * x; } // decorate_with_logging : (String, (Int -> Int)) -> (Int -> Int) template std::function decorate_with_logging(const std::string& str, F f) { return [str, f](int x) -> int { int result = f(x); // no side effects in tests //std::cout << str << ": " << x << " => " << result << std::endl; return result; }; } } TEST_CASE("udemy_course_test, OOP_Design_patterns_vanishing") { using namespace OOP_Design_patterns_vanishing; const auto logging_square = decorate_with_logging("Square", square); int a = logging_square(4); int b = logging_square(5); REQUIRE_EQ(a, 16); REQUIRE_EQ(b, 25); }libfplus-0.2.13/test/variant_test.cpp000066400000000000000000000070761376322245400176250ustar00rootroot00000000000000// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. // https://github.com/Dobiasd/FunctionalPlus // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include namespace { std::string print_output; bool print_int(int x) { print_output = "int " + std::to_string(x); return true; } bool print_double(double x) { print_output = "double " + std::to_string(fplus::round(x)); return true; } bool print_string(const std::string& str) { print_output = "string " + str; return true; } std::string show_int(int x) { return fplus::show(x); } std::string show_string(const std::string& str) { return fplus::show(str); } } TEST_CASE("variant_test, visit_one") { using namespace fplus; fplus::variant int_or_double(3); int_or_double.visit_one(print_int); REQUIRE_EQ(print_output, "int 3"); print_output.clear(); int_or_double.visit_one(print_double); REQUIRE_EQ(print_output, ""); print_output.clear(); int_or_double = 23.0; int_or_double.visit_one(print_int); REQUIRE_EQ(print_output, ""); print_output.clear(); int_or_double.visit_one(print_double); REQUIRE_EQ(print_output, "double 23"); print_output.clear(); using float_or_double = fplus::variant; using nested = fplus::variant; nested int_or_float_double_variant(fplus::variant(3.0)); const auto print_nested_double = [&](const float_or_double& f_o_d) -> int { f_o_d.visit_one(print_double); return 0; }; int_or_float_double_variant.visit_one(print_nested_double); REQUIRE_EQ(print_output, "double 3"); print_output.clear(); } TEST_CASE("variant_test, equality_test") { using namespace fplus; fplus::variant int_or_string_i(3); fplus::variant int_or_string_s(std::string("hi")); REQUIRE(int_or_string_i == int_or_string_i); REQUIRE_FALSE(int_or_string_i == int_or_string_s); } TEST_CASE("variant_test, inequality_test") { using namespace fplus; fplus::variant int_or_string_i(3); fplus::variant int_or_string_s(std::string("hi")); REQUIRE_FALSE(int_or_string_i != int_or_string_i); REQUIRE(int_or_string_i != int_or_string_s); } TEST_CASE("variant_test, visit") { using namespace fplus; // should not compile //int_or_double.visit_one(print_string); fplus::variant int_or_string(3); REQUIRE(int_or_string.is()); REQUIRE_FALSE(int_or_string.is()); int_or_string.visit(print_int, print_string); REQUIRE_EQ(print_output, "int 3"); print_output.clear(); const auto transform_result = int_or_string.transform(show_int, show_string); transform_result.visit_one(print_string); REQUIRE_EQ(print_output, "string 3"); print_output.clear(); // should not compile //std::cout << int_or_string.visit(show_int, show_float) << std::endl; // should not compile //std::cout << int_or_string.visit(show_int, show_int) << std::endl; // should not compile //std::cout << int_or_string.visit(show_int, show_string, show_double) << std::endl; // should not compile //std::cout << int_or_string.visit(show_int) << std::endl; } libfplus-0.2.13/test_package/000077500000000000000000000000001376322245400160575ustar00rootroot00000000000000libfplus-0.2.13/test_package/CMakeLists.txt000066400000000000000000000004331376322245400206170ustar00rootroot00000000000000project(test_package) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup() add_executable(test_package test_package.cpp) target_link_libraries(test_package ${CONAN_LIBS})libfplus-0.2.13/test_package/conanfile.py000066400000000000000000000005521376322245400203710ustar00rootroot00000000000000from conans import ConanFile, CMake import os class TestPackageConan(ConanFile): settings = "os", "compiler", "build_type", "arch" generators = "cmake" def build(self): cmake = CMake(self) cmake.configure() cmake.build() def test(self): bin_path = os.path.join("bin", "test_package") self.run(bin_path)libfplus-0.2.13/test_package/test_package.cpp000066400000000000000000000003361376322245400212170ustar00rootroot00000000000000#include #include int main() { std::list things = {"same old", "same old"}; if (fplus::all_the_same(things)) std::cout << "All things being equal." << std::endl; }