#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 internal::invoke_result_t, 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 internal::invoke_result_t, 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 internal::invoke_result_t, 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 internal::invoke_result_t, 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 class Alloc, class NewT, int SizeOffset> struct same_cont_new_t>, NewT, SizeOffset> { typedef typename std::vector> type; }; template class Alloc, class NewT, int SizeOffset> struct same_cont_new_t>, NewT, SizeOffset> { typedef typename std::deque> type; }; template class Alloc, class NewT, int SizeOffset> struct same_cont_new_t>, NewT, SizeOffset> { typedef typename std::forward_list> type; }; template class Alloc, class NewT, int SizeOffset> struct same_cont_new_t>, NewT, SizeOffset> { typedef typename std::list> type; }; template class Alloc, class NewT, int SizeOffset> 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; }; // For aligned allocators. template class Alloc, class NewT, int SizeOffset, std::size_t N> struct same_cont_new_t>, NewT, SizeOffset> { typedef typename std::vector> type; }; template class Alloc, class NewT, int SizeOffset, std::size_t N> struct same_cont_new_t>, NewT, SizeOffset> { typedef typename std::deque> 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); } // Avoid self-assignment. template void assign(T& x, T&& y) { if (&x != &y) x = std::move(y); } 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); assign((*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)); } // GCC 4.9 does not support std::rbegin, std::rend and std::make_reverse_iterator template std::reverse_iterator make_reverse_iterator(Iterator it) { return std::reverse_iterator(it); } } // namespace internal // 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 % 2 != 0; } // 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: take_last_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_last_while(is_even, [0,2,7,5,6,4,8]) == [6,4,8] template Container take_last_while(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); const auto r_begin = internal::make_reverse_iterator(std::end(xs)); const auto r_end = internal::make_reverse_iterator(std::begin(xs)); const auto itFirstReverse = std::find_if(r_begin, r_end, logical_not(pred)); if (itFirstReverse == r_begin) return Container(); if (itFirstReverse == r_end) return xs; return Container(itFirstReverse.base(), std::end(xs)); } // 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: drop_last_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_last_while(is_even, [0,2,7,5,6,4,8]) == [0,2,7,5] template Container drop_last_while(UnaryPredicate pred, const Container& xs) { internal::check_unary_predicate_for_container(); const auto r_begin = internal::make_reverse_iterator(std::end(xs)); const auto r_end = internal::make_reverse_iterator(std::begin(xs)); const auto itFirstNotReverse = std::find_if_not(r_begin, r_end, pred); if (itFirstNotReverse == r_begin) return xs; if (itFirstNotReverse == r_end) return Container(); return Container(std::begin(xs), itFirstNotReverse.base()); } // 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 (is_even(size_of_cont(xs))) 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++)) assign(*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 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 // // 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: zip_repeat : ([a], [b]) -> [(a, b)] // fwd bind count: 1 // Similar to zip but repeats the shorter sequence // to align with the longer sequence. // zip([1, 2, 3, 4], [5, 6]) == [(1, 5), (2, 6), (3, 5), (4, 6)] template auto zip_repeat(const ContainerIn1& xs, const ContainerIn2& ys) { auto nx = xs.size(); auto ny = ys.size(); auto qx = ny/nx + (ny % nx ? 1 : 0); auto qy = nx/ny + (nx % ny ? 1 : 0); return zip(qx > 1 ? repeat(qx, xs) : xs, qy > 1 ? repeat(qy, ys) : 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); } 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 // // 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 (is_odd(size_of_cont(xsSorted))) { 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: pairs_to_unordered_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_unordered_map_grouped([("a", 1), ("a", 2), ("b", 6), ("a", 4)]) // -> {"a": [1, 2, 4], "b": [6]} template >> MapOut pairs_to_unordered_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 collisions. // 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: map_grouped_to_pairs : Map key [val] -> [(key, val)] // fwd bind count: 0 // Convert a dictionary with sequence values to a list of key-value pairs. // Inverse operation of pairs_to_map_grouped. // map_grouped_to_pairs({"a": [1, 2, 4], "b": [6]}) // -> [("a", 1), ("a", 2), ("a", 4), ("b", 6)] template auto map_grouped_to_pairs(const MapType &dict) { using Key = typename MapType::key_type; using Group = typename MapType::mapped_type; auto fn = [](const auto &pair) { const auto f = zip_repeat, Group>; return apply_to_pair(f, transform_fst(singleton_seq < Key > , pair)); }; return concat(transform(fn, map_to_pairs(dict))); } // 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_map_grouped : ((val -> key), [val]) -> Map key val // fwd bind count: 1 // Take a list of values and create a grouped dictionary // generating the keys by applying f to each key. // create_map_grouped(length, ["one", "three", "two"]) == {3: ["one", "two"], 5: ["three"]} // Also known as group_by template auto create_map_grouped(F f, const ContainerIn& values) { return pairs_to_map_grouped(zip(transform(f, values), values)); } // 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: create_unordered_map_grouped : ((val -> key), [val]) -> Map key val // fwd bind count: 1 // Take a list of values and create a grouped dictionary // generating the keys by applying f to each key. // create_unordered_map_grouped(length, ["one", "three", "two"]) == {3: ["one", "two"], 5: ["three"]} // Also known as group_by template auto create_unordered_map_grouped(F f, const ContainerIn& values) { return pairs_to_unordered_map_grouped(zip(transform(f, values), values)); } // 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) == 1 || size_of_cont(xs) == 0) { 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 (is_odd(size_of_cont(xs))) { 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 (is_odd(size_of_cont(xs))) { 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 (is_odd(size_of_cont(xs))) { 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 (is_odd(size_of_cont(xs))) { 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 #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)); } // Based on https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat // Case N, recursive template struct TupleStreamer { static void stream(const Tuple& t, std::list& sl) { TupleStreamer::stream(t,sl); std::stringstream ss; ss << std::get(t); sl.emplace_back(ss.str()); } }; // Based on https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat // Case N=1 template struct TupleStreamer { static void stream(const Tuple& t, std::list& sl) { std::stringstream ss; ss << std::get<0>(t); sl.emplace_back(ss.str()); } }; // Based on https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat // Case N=0 template = 0> void stream(const std::tuple& , std::list& ) { return; } // Based on https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat // Example: // std::tuple t1(10, "Test", 3.14); // std::list lt1 = stream(t1); // std::cout << fplus::show_cont(lt1); template = 0> std::list stream(const std::tuple& t) { std::list sl; TupleStreamer::stream(t,sl); return sl; } } // 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: for_each : (Io a, [a]) -> Io () // fwd bind count: 1 // Runs the function `f` on all the container elements. // The function will perform its side effects, and nothing is returned. template void for_each(F f, const Container& xs) { using IdxType = typename Container::value_type; auto f_dummy_return = [&f](const IdxType& v) { f(v); return true; }; fplus::transform(f_dummy_return, xs); } // API search type: parallel_for_each : (Io a, [a]) -> Io () // fwd bind count: 1 // Runs the function `f` in parallel on all the container elements. // The function will perform its side effects, and nothing is returned. template void parallel_for_each(F f, const Container& xs) { using IdxType = typename Container::value_type; auto f_dummy_return = [&f](const IdxType& v) { f(v); return true; }; fplus::transform_parallelly(f_dummy_return, xs); } // API search type: parallel_for_each_n_threads : (Int, Io a, [a]) -> Io () // fwd bind count: 2 // Runs the function `f` in parallel on all the container elements, using `n_threads` threads. // The function will perform its side effects, and nothing is returned. template void parallel_for_each_n_threads(size_t n_threads, F f, const Container& xs) { using IdxType = typename Container::value_type; auto f_dummy_return = [&f](const IdxType& v) { f(v); return true; }; fplus::transform_parallelly_n_threads(n_threads, f_dummy_return, xs); } // 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(std::forward(args)...); } private: template auto _timed_result(Args&&... args) { fplus::stopwatch timer; auto r = _fn(std::forward(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(std::forward(args)...); } private: template auto _timed_result(Args&&... args) { fplus::stopwatch timer; _fn(std::forward(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(std::forward(args)...); } private: template auto _bench_result(Args&&... args) { fplus::stopwatch timer; auto r = fn_(std::forward(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(std::forward(args)...); } private: template auto _bench_result(Args&&... args) { fplus::stopwatch timer; fn_(std::forward(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_even) fplus_curry_define_fn_0(is_odd) 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(take_last_while) fplus_curry_define_fn_1(drop_while) fplus_curry_define_fn_1(drop_last_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(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(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_1(zip_repeat) 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(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(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(pairs_to_unordered_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(map_grouped_to_pairs) 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_map_grouped) fplus_curry_define_fn_1(create_unordered_map) fplus_curry_define_fn_1(create_unordered_map_with) fplus_curry_define_fn_1(create_unordered_map_grouped) 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_1(for_each) fplus_curry_define_fn_1(parallel_for_each) fplus_curry_define_fn_2(parallel_for_each_n_threads) 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_even) fplus_fwd_define_fn_0(is_odd) 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(take_last_while) fplus_fwd_define_fn_1(drop_while) fplus_fwd_define_fn_1(drop_last_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(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(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_1(zip_repeat) 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(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(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(pairs_to_unordered_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(map_grouped_to_pairs) 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_map_grouped) fplus_fwd_define_fn_1(create_unordered_map) fplus_fwd_define_fn_1(create_unordered_map_with) fplus_fwd_define_fn_1(create_unordered_map_grouped) 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_1(for_each) fplus_fwd_define_fn_1(parallel_for_each) fplus_fwd_define_fn_2(parallel_for_each_n_threads) 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(take_last_while) fplus_fwd_flip_define_fn_1(drop_while) fplus_fwd_flip_define_fn_1(drop_last_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(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(apply_to_pair) fplus_fwd_flip_define_fn_1(zip) fplus_fwd_flip_define_fn_1(zip_repeat) 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(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_map_grouped) 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(create_unordered_map_grouped) 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) fplus_fwd_flip_define_fn_1(for_each) fplus_fwd_flip_define_fn_1(parallel_for_each) } // namespace fwd } // namespace fplus