You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
16344 lines
530 KiB
C++
16344 lines
530 KiB
C++
#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)
|
|
|
|
/**
|
|
|
|
``<utils/traits.hpp>`` --- 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 <cstdlib>
|
|
#include <tuple>
|
|
#include <functional>
|
|
#include <type_traits>
|
|
|
|
|
|
//
|
|
// 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 <type_traits>
|
|
|
|
namespace fplus
|
|
{
|
|
namespace internal
|
|
{
|
|
// C++14 compatible void_t (http://en.cppreference.com/w/cpp/types/void_t)
|
|
template <typename... Ts>
|
|
struct make_void
|
|
{
|
|
using type = void;
|
|
};
|
|
|
|
template <typename... Ts>
|
|
using void_t = typename make_void<Ts...>::type;
|
|
|
|
// Sometimes you don't want to use std::decay_t, and the temptation of short
|
|
// writing can be huge...
|
|
template <typename T>
|
|
using uncvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
|
|
|
// disjunction/conjunction/negation, useful to short circuit SFINAE checks
|
|
// Use with parsimony, MSVC 2015 can have ICEs quite easily
|
|
template <typename...>
|
|
struct disjunction : std::false_type
|
|
{
|
|
};
|
|
|
|
template <typename B1>
|
|
struct disjunction<B1> : B1
|
|
{
|
|
};
|
|
|
|
template <typename B1, typename... Bn>
|
|
struct disjunction<B1, Bn...>
|
|
: std::conditional<bool(B1::value), B1, disjunction<Bn...>>::type
|
|
{
|
|
};
|
|
|
|
template <typename...>
|
|
struct conjunction : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename B1>
|
|
struct conjunction<B1> : B1
|
|
{
|
|
};
|
|
|
|
template <typename B1, typename... Bn>
|
|
struct conjunction<B1, Bn...>
|
|
: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type
|
|
{
|
|
};
|
|
|
|
template <typename B>
|
|
struct negation : std::integral_constant<bool, !bool(B::value)>
|
|
{
|
|
};
|
|
|
|
// non short-circuiting meta functions
|
|
// source: https://stackoverflow.com/a/27221517/4116453
|
|
template <bool...>
|
|
struct bool_pack;
|
|
|
|
template <bool... Values>
|
|
struct all_of
|
|
: std::is_same<bool_pack<Values..., true>, bool_pack<true, Values...>>
|
|
{
|
|
};
|
|
|
|
// 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<class... Any>
|
|
using is_function = std::is_function<Any...>;
|
|
#else //PROVIDE_IS_FUNCTION_POLYFILL
|
|
// primary template
|
|
template<class>
|
|
struct is_function : std::false_type { };
|
|
|
|
// specialization for regular functions
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...)> : std::true_type {};
|
|
|
|
// specialization for variadic functions such as std::printf
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...)> : std::true_type {};
|
|
|
|
// specialization for function types that have cv-qualifiers
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) const> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) volatile> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) const volatile> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) const> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) volatile> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) const volatile> : std::true_type {};
|
|
|
|
// specialization for function types that have ref-qualifiers
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) &> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) const &> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) volatile &> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) const volatile &> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) &> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) const &> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) volatile &> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) const volatile &> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) &&> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) const &&> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) volatile &&> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...) const volatile &&> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) &&> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) const &&> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) volatile &&> : std::true_type {};
|
|
template<class Ret, class... Args>
|
|
struct is_function<Ret(Args...,...) const volatile &&> : std::true_type {};
|
|
#endif //PROVIDE_IS_FUNCTION_POLYFILL
|
|
|
|
template <typename>
|
|
struct reverse_integer_sequence_impl;
|
|
|
|
template <typename T>
|
|
struct reverse_integer_sequence_impl<std::integer_sequence<T>>
|
|
: std::integer_sequence<T>
|
|
{
|
|
};
|
|
|
|
template <typename T, T... Ints>
|
|
struct reverse_integer_sequence_impl<std::integer_sequence<T, Ints...>>
|
|
: std::integer_sequence<T, sizeof...(Ints) - 1 - Ints...>
|
|
{
|
|
};
|
|
|
|
template <typename Seq>
|
|
using reverse_integer_sequence = reverse_integer_sequence_impl<Seq>;
|
|
|
|
template <typename T, T N>
|
|
using make_reverse_integer_sequence =
|
|
reverse_integer_sequence<std::make_integer_sequence<T, N>>;
|
|
|
|
template <std::size_t... Idx>
|
|
using reverse_index_sequence =
|
|
reverse_integer_sequence<std::index_sequence<Idx...>>;
|
|
|
|
template <std::size_t N>
|
|
using make_reverse_index_sequence =
|
|
make_reverse_integer_sequence<std::size_t, N>;
|
|
}
|
|
}
|
|
|
|
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<int> >::value);
|
|
// ^ prints '1' (true)
|
|
printf("%d\n", has_result_type< double(*)() >::value);
|
|
// ^ prints '0' (false)
|
|
*/
|
|
#define DECLARE_HAS_TYPE_MEMBER(member_name) \
|
|
template <typename, typename = void> \
|
|
struct has_##member_name \
|
|
{ enum { value = false }; }; \
|
|
template <typename T> \
|
|
struct has_##member_name<T, typename std::enable_if<sizeof(typename T::member_name)||true>::type> \
|
|
{ enum { value = true }; };
|
|
|
|
/**
|
|
.. type:: struct utils::function_traits<F>
|
|
|
|
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<F>``
|
|
* 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 <typename T>
|
|
struct function_traits
|
|
: public function_traits<decltype(&T::operator())>
|
|
{};
|
|
|
|
namespace xx_impl
|
|
{
|
|
template <typename C, typename R, typename... A>
|
|
struct memfn_type
|
|
{
|
|
typedef typename std::conditional<
|
|
std::is_const<C>::value,
|
|
typename std::conditional<
|
|
std::is_volatile<C>::value,
|
|
R (C::*)(A...) const volatile,
|
|
R (C::*)(A...) const
|
|
>::type,
|
|
typename std::conditional<
|
|
std::is_volatile<C>::value,
|
|
R (C::*)(A...) volatile,
|
|
R (C::*)(A...)
|
|
>::type
|
|
>::type type;
|
|
};
|
|
}
|
|
|
|
template <typename ReturnType, typename... Args>
|
|
struct function_traits<ReturnType(Args...)>
|
|
{
|
|
/**
|
|
.. 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<OwnerType>
|
|
|
|
The member function type for an *OwnerType* (``R(OwnerType::*)(T...)``).
|
|
*/
|
|
template <typename OwnerType>
|
|
using member_function_type = typename xx_impl::memfn_type<
|
|
typename std::remove_pointer<typename std::remove_reference<OwnerType>::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<n>::type
|
|
|
|
The type of the *n*-th argument.
|
|
*/
|
|
template <size_t i>
|
|
struct arg
|
|
{
|
|
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
|
|
};
|
|
};
|
|
|
|
template <typename ReturnType, typename... Args>
|
|
struct function_traits<ReturnType(*)(Args...)>
|
|
: public function_traits<ReturnType(Args...)>
|
|
{};
|
|
|
|
template <typename ClassType, typename ReturnType, typename... Args>
|
|
struct function_traits<ReturnType(ClassType::*)(Args...)>
|
|
: public function_traits<ReturnType(Args...)>
|
|
{
|
|
typedef ClassType& owner_type;
|
|
};
|
|
|
|
template <typename ClassType, typename ReturnType, typename... Args>
|
|
struct function_traits<ReturnType(ClassType::*)(Args...) const>
|
|
: public function_traits<ReturnType(Args...)>
|
|
{
|
|
typedef const ClassType& owner_type;
|
|
};
|
|
|
|
template <typename ClassType, typename ReturnType, typename... Args>
|
|
struct function_traits<ReturnType(ClassType::*)(Args...) volatile>
|
|
: public function_traits<ReturnType(Args...)>
|
|
{
|
|
typedef volatile ClassType& owner_type;
|
|
};
|
|
|
|
template <typename ClassType, typename ReturnType, typename... Args>
|
|
struct function_traits<ReturnType(ClassType::*)(Args...) const volatile>
|
|
: public function_traits<ReturnType(Args...)>
|
|
{
|
|
typedef const volatile ClassType& owner_type;
|
|
};
|
|
|
|
template <typename FunctionType>
|
|
struct function_traits<std::function<FunctionType>>
|
|
: public function_traits<FunctionType>
|
|
{};
|
|
|
|
#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 <typename R, typename C>
|
|
struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R C::*>>
|
|
: public function_traits<R(C*)>
|
|
{};
|
|
template <typename R, typename C, typename... A>
|
|
struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R(C::*)(A...)>>
|
|
: public function_traits<R(C*, A...)>
|
|
{};
|
|
template <typename R, typename C, typename... A>
|
|
struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R(C::*)(A...) const>>
|
|
: public function_traits<R(const C*, A...)>
|
|
{};
|
|
template <typename R, typename C, typename... A>
|
|
struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R(C::*)(A...) volatile>>
|
|
: public function_traits<R(volatile C*, A...)>
|
|
{};
|
|
template <typename R, typename C, typename... A>
|
|
struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R(C::*)(A...) const volatile>>
|
|
: public function_traits<R(const volatile C*, A...)>
|
|
{};
|
|
|
|
#undef MEM_FN_SYMBOL_XX0SL7G4Z0J
|
|
#endif
|
|
|
|
template <typename T>
|
|
struct function_traits<T&> : public function_traits<T> {};
|
|
template <typename T>
|
|
struct function_traits<const T&> : public function_traits<T> {};
|
|
template <typename T>
|
|
struct function_traits<volatile T&> : public function_traits<T> {};
|
|
template <typename T>
|
|
struct function_traits<const volatile T&> : public function_traits<T> {};
|
|
template <typename T>
|
|
struct function_traits<T&&> : public function_traits<T> {};
|
|
template <typename T>
|
|
struct function_traits<const T&&> : public function_traits<T> {};
|
|
template <typename T>
|
|
struct function_traits<volatile T&&> : public function_traits<T> {};
|
|
template <typename T>
|
|
struct function_traits<const volatile T&&> : public function_traits<T> {};
|
|
|
|
|
|
#define FORWARD_RES_8QR485JMSBT \
|
|
typename std::conditional< \
|
|
std::is_lvalue_reference<R>::value, \
|
|
T&, \
|
|
typename std::remove_reference<T>::type&& \
|
|
>::type
|
|
|
|
/**
|
|
.. function:: auto utils::forward_like<Like, T>(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 <typename R, typename T>
|
|
FORWARD_RES_8QR485JMSBT forward_like(T&& input) noexcept
|
|
{
|
|
return static_cast<FORWARD_RES_8QR485JMSBT>(input);
|
|
}
|
|
|
|
#undef FORWARD_RES_8QR485JMSBT
|
|
|
|
/**
|
|
.. type:: struct utils::copy_cv<From, To>
|
|
|
|
Copy the CV qualifier between the two types. For example,
|
|
``utils::copy_cv<const int, double>::type`` will become ``const double``.
|
|
*/
|
|
template <typename From, typename To>
|
|
struct copy_cv
|
|
{
|
|
private:
|
|
typedef typename std::remove_cv<To>::type raw_To;
|
|
typedef typename std::conditional<std::is_const<From>::value,
|
|
const raw_To, raw_To>::type const_raw_To;
|
|
public:
|
|
/**
|
|
.. type:: type type
|
|
|
|
Result of cv-copying.
|
|
*/
|
|
typedef typename std::conditional<std::is_volatile<From>::value,
|
|
volatile const_raw_To, const_raw_To>::type type;
|
|
};
|
|
|
|
/**
|
|
.. type:: struct utils::pointee<T>
|
|
|
|
Returns the type by derefering an instance of *T*. This is a generalization
|
|
of ``std::remove_pointer``, that it also works with iterators.
|
|
*/
|
|
template <typename T>
|
|
struct pointee
|
|
{
|
|
/**
|
|
.. type:: type type
|
|
|
|
Result of dereferencing.
|
|
*/
|
|
typedef typename std::remove_reference<decltype(*std::declval<T>())>::type type;
|
|
};
|
|
|
|
/**
|
|
.. function:: std::add_rvalue_reference<T>::type utils::rt_val<T>() 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 T>
|
|
typename std::add_rvalue_reference<T>::type rt_val() noexcept
|
|
{
|
|
return std::move(*static_cast<T*>(nullptr));
|
|
}
|
|
|
|
#ifdef __GNUC__
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
namespace fplus
|
|
{
|
|
namespace internal
|
|
{
|
|
template <typename>
|
|
struct is_std_function : std::false_type
|
|
{
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_std_function<std::function<T>> : std::true_type
|
|
{
|
|
};
|
|
|
|
// Those traits are needed to not perform arity checks on a generic-lambd
|
|
// or a templated/overloaded operator()
|
|
template <typename T, typename = void>
|
|
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 <typename T>
|
|
struct has_function_traits<T,
|
|
std::enable_if_t<!is_std_function<T>::value,
|
|
void_t<decltype(&T::operator())>>>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename... Args>
|
|
struct has_function_traits<ReturnType(Args...)> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename... Args>
|
|
struct has_function_traits<ReturnType (*)(Args...)> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...)> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) const>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) volatile>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) const volatile>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...)&> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) const &>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) volatile&>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) const volatile&>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) &&>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) const &&>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) volatile&&>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename ClassType, typename... Args>
|
|
struct has_function_traits<ReturnType (ClassType::*)(Args...) const volatile&&>
|
|
: std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename FunctionType>
|
|
struct has_function_traits<std::function<FunctionType>> : 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 <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
|
|
//
|
|
// 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 <functional>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
|
|
// 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 <typename U>
|
|
static std::true_type is_refwrap_test(const std::reference_wrapper<U>&);
|
|
|
|
template <typename U>
|
|
static std::false_type is_refwrap_test(const U&);
|
|
|
|
template <typename T>
|
|
struct is_reference_wrapper : decltype(is_refwrap_test(std::declval<T>()))
|
|
{
|
|
};
|
|
|
|
template <typename T, typename U = typename std::decay<T>::type>
|
|
struct unwrap_reference_wrapper
|
|
{
|
|
using type = T;
|
|
};
|
|
|
|
template <typename T, typename U>
|
|
struct unwrap_reference_wrapper<T, std::reference_wrapper<U>>
|
|
{
|
|
using type = U&;
|
|
};
|
|
|
|
template <typename T>
|
|
using unwrap_reference_wrapper_t = typename unwrap_reference_wrapper<T>::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 <typename T>
|
|
struct is_const_member_function : std::false_type
|
|
{
|
|
};
|
|
|
|
// decay doesn't add pointer to abominable functions, don't bother writing them
|
|
template <typename R, typename... Args>
|
|
struct is_const_member_function<R(Args...) const> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_const_member_function<R(Args...) const&> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_const_member_function<R(Args...) const&&> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_const_member_function<R(Args...) const volatile> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_const_member_function<R(Args...) const volatile&> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_const_member_function<R(Args...) const volatile&&> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_volatile_member_function : std::false_type
|
|
{
|
|
};
|
|
|
|
// decay doesn't add pointer to abominable functions, don't bother writing them
|
|
template <typename R, typename... Args>
|
|
struct is_volatile_member_function<R(Args...) volatile> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_volatile_member_function<R(Args...) volatile&> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_volatile_member_function<R(Args...) volatile&&> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_volatile_member_function<R(Args...) const volatile> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_volatile_member_function<R(Args...) const volatile&> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct is_volatile_member_function<R(Args...) const volatile&&> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <typename Object, typename Signature>
|
|
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<typename std::remove_cv<Object>::type, Object>::value ||
|
|
((is_volatile_member_function<Signature>::value ==
|
|
std::is_volatile<Object>::value) &&
|
|
(is_const_member_function<Signature>::value ==
|
|
std::is_const<Object>::value));
|
|
};
|
|
|
|
// pointer to member function - reference to object
|
|
template <
|
|
typename Base,
|
|
typename T,
|
|
typename Derived,
|
|
typename... Args,
|
|
typename Unwrapped = unwrap_reference_wrapper_t<Derived>,
|
|
typename std::enable_if<
|
|
is_function<T>::value &&
|
|
has_correct_cv<typename std::remove_reference<Unwrapped>::type, T>::value &&
|
|
std::is_base_of<Base, typename std::decay<Unwrapped>::type>::value,
|
|
int>::type = 0>
|
|
inline auto invoke_impl(T Base::*pmf, Derived&& ref, Args&&... args)
|
|
FPLUS_INVOKE_RETURN((std::forward<Unwrapped>(ref).*
|
|
pmf)(std::forward<Args>(args)...))
|
|
|
|
// pointer to member function - pointer to object
|
|
template <
|
|
typename Base,
|
|
typename T,
|
|
typename Pointer,
|
|
typename... Args,
|
|
typename std::enable_if<
|
|
is_function<T>::value &&
|
|
has_correct_cv<typename std::remove_pointer<
|
|
typename std::decay<Pointer>::type>::type,
|
|
T>::value &&
|
|
!std::is_base_of<Base, typename std::decay<Pointer>::type>::value,
|
|
int>::type = 0>
|
|
inline auto invoke_impl(T Base::*pmf, Pointer&& ptr, Args&&... args)
|
|
FPLUS_INVOKE_RETURN(((*std::forward<Pointer>(ptr)).*
|
|
pmf)(std::forward<Args>(args)...))
|
|
|
|
// pointer to non-static data member - reference to object
|
|
template <
|
|
typename Base,
|
|
typename T,
|
|
typename Derived,
|
|
typename Unwrapped = unwrap_reference_wrapper_t<Derived>,
|
|
typename std::enable_if<
|
|
!is_function<T>::value &&
|
|
std::is_base_of<Base, typename std::decay<Unwrapped>::type>::value,
|
|
int>::type = 0>
|
|
inline auto invoke_impl(T Base::*pmd, Derived&& ref)
|
|
FPLUS_INVOKE_RETURN((std::forward<Unwrapped>(ref).*pmd))
|
|
|
|
// pointer to non-static data member - pointer to object
|
|
template <
|
|
typename Base,
|
|
typename T,
|
|
typename Pointer,
|
|
typename std::enable_if<
|
|
!is_function<T>::value &&
|
|
!std::is_base_of<Base, typename std::decay<Pointer>::type>::value,
|
|
int>::type = 0>
|
|
inline auto invoke_impl(T Base::*pmd, Pointer&& ptr)
|
|
FPLUS_INVOKE_RETURN((*std::forward<Pointer>(ptr)).*pmd)
|
|
|
|
// normal case - functions, lambdas, function objects
|
|
template <typename F,
|
|
typename... Args,
|
|
typename std::enable_if<
|
|
!std::is_member_pointer<typename std::decay<F>::type>::value,
|
|
int>::type = 0>
|
|
inline auto invoke_impl(F&& f, Args&&... args)
|
|
FPLUS_INVOKE_RETURN((std::forward<F>(f)(std::forward<Args>(args)...)))
|
|
|
|
template <typename AlwaysVoid, typename, typename...>
|
|
struct invoke_result_impl
|
|
{
|
|
};
|
|
|
|
template <typename F, typename... Args>
|
|
struct invoke_result_impl<decltype(void(invoke_impl(std::declval<F>(),
|
|
std::declval<Args>()...))),
|
|
F,
|
|
Args...>
|
|
{
|
|
using type =
|
|
decltype(invoke_impl(std::declval<F>(), std::declval<Args>()...));
|
|
};
|
|
|
|
template <typename F, typename... ArgTypes>
|
|
struct invoke_result : invoke_result_impl<void, F, ArgTypes...>
|
|
{
|
|
};
|
|
|
|
template <typename F, typename... Args>
|
|
using invoke_result_t = typename invoke_result<F, Args...>::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 <typename F, typename... ArgTypes>
|
|
invoke_result_t<F, ArgTypes...> invoke(F&& f, ArgTypes&&... args)
|
|
{
|
|
return invoke_impl(std::forward<F>(f), std::forward<ArgTypes>(args)...);
|
|
}
|
|
|
|
// Invoke useful traits (libstdc++ 7.1.0's implementation, ugly-case removed)
|
|
template <typename Result, typename ReturnType, typename = void>
|
|
struct is_invocable_impl : std::false_type
|
|
{
|
|
};
|
|
|
|
template <typename Result, typename ReturnType>
|
|
struct is_invocable_impl<Result, ReturnType, void_t<typename Result::type>>
|
|
: disjunction<std::is_void<ReturnType>,
|
|
std::is_convertible<typename Result::type, ReturnType>>::type
|
|
{
|
|
};
|
|
|
|
template <typename F, typename... ArgTypes>
|
|
struct is_invocable
|
|
: is_invocable_impl<invoke_result<F, ArgTypes...>, void>::type
|
|
{
|
|
};
|
|
|
|
template <typename ReturnType, typename F, typename... ArgTypes>
|
|
struct is_invocable_r
|
|
: is_invocable_impl<invoke_result<F, ArgTypes...>, ReturnType>::type
|
|
{
|
|
};
|
|
}
|
|
}
|
|
|
|
#undef FPLUS_INVOKE_RETURN
|
|
|
|
namespace fplus
|
|
{
|
|
namespace internal
|
|
{
|
|
// C++17 std::apply (http://en.cppreference.com/w/cpp/utility/apply)
|
|
template <typename F, typename Tuple, std::size_t... I>
|
|
constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>)
|
|
{
|
|
return internal::invoke(std::forward<F>(f),
|
|
std::get<I>(std::forward<Tuple>(t))...);
|
|
}
|
|
|
|
template <typename F, typename Tuple>
|
|
constexpr decltype(auto) apply(F&& f, Tuple&& t)
|
|
{
|
|
return internal::apply_impl(
|
|
std::forward<F>(f),
|
|
std::forward<Tuple>(t),
|
|
std::make_index_sequence<
|
|
std::tuple_size<std::decay_t<Tuple>>::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 <typename T, typename...>
|
|
struct function_traits_asserts;
|
|
|
|
template <
|
|
typename,
|
|
typename F,
|
|
typename... Args,
|
|
typename std::enable_if<is_invocable<F, Args...>::value, int>::type = 0>
|
|
constexpr void trigger_static_asserts()
|
|
{
|
|
}
|
|
|
|
// Marks a variable as unused. Prevents the compiler warning
|
|
// for set but unused variables.
|
|
template<class T>
|
|
inline void unused(T&&) { }
|
|
|
|
template <typename Tag,
|
|
typename F,
|
|
typename... Args,
|
|
typename std::enable_if<has_function_traits<F>::value &&
|
|
!is_invocable<F, Args...>::value,
|
|
int>::type = 0>
|
|
constexpr void trigger_static_asserts()
|
|
{
|
|
// don't perform checks if function_traits<F> doesn't exist
|
|
unused(function_traits_asserts<Tag, F, Args...>{});
|
|
}
|
|
|
|
template <typename,
|
|
typename F,
|
|
typename... Args,
|
|
typename std::enable_if<!has_function_traits<F>::value &&
|
|
!is_invocable<F, Args...>::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 <typename F>
|
|
struct function_traits_asserts<nullary_function_tag, F>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 0,
|
|
"Function must take no parameters.");
|
|
};
|
|
|
|
template <typename F, typename X>
|
|
struct function_traits_asserts<unary_function_tag, F, X>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 1,
|
|
"Function must take one parameter.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Invalid argument type for function");
|
|
};
|
|
|
|
template <typename F>
|
|
struct function_traits_asserts<binary_function_tag, F>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 2,
|
|
"Function must take two parameters.");
|
|
};
|
|
|
|
template <typename F, typename X, typename Y>
|
|
struct function_traits_asserts<binary_function_tag, F, X ,Y>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 2,
|
|
"Function must take two parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Invalid first argument type for function");
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Invalid second argument type for function");
|
|
};
|
|
|
|
template <typename F>
|
|
struct function_traits_asserts<binary_predicate_tag, F>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 2,
|
|
"Function must take two parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
static_assert(std::is_same<FIn0, FIn1>::value,
|
|
"Both parameters must have the same type.");
|
|
static_assert(std::is_same<std::decay_t<internal::invoke_result_t<F, FIn0, FIn1>>, bool>::value,
|
|
"Predicate must return bool.");
|
|
};
|
|
|
|
template <typename F, typename... Args>
|
|
struct function_traits_asserts<check_arity_tag, F, Args...>
|
|
{
|
|
static_assert(utils::function_traits<F>::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 <typename F, typename X, typename Y>
|
|
struct function_traits_asserts<bind_1st_of_2_tag, F, X, Y>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 2,
|
|
"Function must take two parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function can not take bound parameter type");
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Function can not take provided parameter type");
|
|
};
|
|
|
|
template <typename F, typename X, typename Y>
|
|
struct function_traits_asserts<bind_2nd_of_2_tag, F, X, Y>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 2,
|
|
"Function must take two parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function can not take provided parameter type");
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Function can not take bound parameter type");
|
|
};
|
|
|
|
template <typename F, typename X, typename Y, typename Z>
|
|
struct function_traits_asserts<bind_1st_of_3_tag, F, X, Y, Z>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 3,
|
|
"Function must take three parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
typedef typename utils::function_traits<F>::template arg<2>::type FIn2;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function can not take bound parameter type");
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Function can not take provided first parameter type");
|
|
static_assert(std::is_convertible<Z, FIn2>::value,
|
|
"Function can not take provided second parameter type");
|
|
};
|
|
|
|
template <typename F, typename X, typename Y, typename Z>
|
|
struct function_traits_asserts<bind_1st_and_2nd_of_3_tag, F, X, Y, Z>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 3,
|
|
"Function must take three parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
typedef typename utils::function_traits<F>::template arg<2>::type FIn2;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function can not take first bound parameter type");
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Function can not take second bound parameter type");
|
|
static_assert(std::is_convertible<Z, FIn2>::value,
|
|
"Function can not take provided parameter type");
|
|
};
|
|
|
|
template <typename F, typename X, typename Y, typename Z>
|
|
struct function_traits_asserts<bind_2nd_and_3rd_of_3_tag, F, X, Y, Z>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 3,
|
|
"Function must take three parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
typedef typename utils::function_traits<F>::template arg<2>::type FIn2;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function can not take provided parameter type");
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Function can not take second bound parameter type");
|
|
static_assert(std::is_convertible<Z, FIn2>::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 <tuple>
|
|
#include <utility>
|
|
|
|
|
|
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 <typename... Fs>
|
|
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>(fs)...)
|
|
{
|
|
}
|
|
|
|
template <typename... Ts>
|
|
auto operator()(Ts&&... ts) const
|
|
{
|
|
return _apply(std::integral_constant<std::size_t, 0>{},
|
|
std::forward<Ts>(ts)...);
|
|
}
|
|
|
|
private:
|
|
template <std::size_t N, typename... Ts>
|
|
auto _apply(std::integral_constant<std::size_t, N>, Ts&&... ts) const
|
|
{
|
|
return _apply(std::integral_constant<std::size_t, N + 1>{},
|
|
std::get<N>(_functionTuple)(std::forward<Ts>(ts)...));
|
|
}
|
|
|
|
template <typename... Ts>
|
|
auto _apply(std::integral_constant<std::size_t, size - 1>, Ts&&... ts) const
|
|
{
|
|
return internal::invoke(std::get<size - 1>(_functionTuple),
|
|
std::forward<Ts>(ts)...);
|
|
}
|
|
|
|
std::tuple<Fs...> _functionTuple;
|
|
};
|
|
|
|
// Is BinaryLift really correct?
|
|
template <typename Tuple, typename BinaryLift>
|
|
auto compose_binary_lift_impl(std::integral_constant<std::size_t, 1>,
|
|
const Tuple& tup,
|
|
const BinaryLift& lifter)
|
|
{
|
|
return lifter(std::get<0>(tup), std::get<1>(tup));
|
|
}
|
|
|
|
template <std::size_t N, typename Tuple, typename BinaryLift>
|
|
auto compose_binary_lift_impl(std::integral_constant<std::size_t, N>,
|
|
const Tuple& tup,
|
|
const BinaryLift& lifter)
|
|
{
|
|
return lifter(
|
|
compose_binary_lift_impl(
|
|
std::integral_constant<std::size_t, N - 1>{}, tup, lifter),
|
|
std::get<N>(tup));
|
|
}
|
|
|
|
template <typename BinaryLift, typename... Callables>
|
|
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<Callables>(args)...);
|
|
return compose_binary_lift_impl(
|
|
std::integral_constant<std::size_t, sizeof...(Callables) - 1>{},
|
|
tup,
|
|
lifter);
|
|
}
|
|
|
|
// concentrate asserts in this method. Lambda is provided by the library.
|
|
template <typename Lambda, typename F, typename G>
|
|
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::unary_function_tag,
|
|
F,
|
|
decltype(x)>();
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
G,
|
|
decltype(x)>();
|
|
using FRes = std::decay_t<internal::invoke_result_t<F, decltype(x)>>;
|
|
using GRes = std::decay_t<internal::invoke_result_t<G, decltype(x)>>;
|
|
static_assert(std::is_same<FRes, bool>::value, "Must return bool.");
|
|
static_assert(std::is_same<GRes, bool>::value, "Must return bool.");
|
|
|
|
return op(f, g, x);
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
// API search type: bind_1st_of_2 : (((a, b) -> c), a) -> (b -> c)
|
|
// Bind first parameter of binary function.
|
|
template <typename F, typename T>
|
|
auto bind_1st_of_2(F f, T x)
|
|
{
|
|
return [f, x](auto&& y) {
|
|
internal::trigger_static_asserts<internal::bind_1st_of_2_tag,
|
|
F,
|
|
T,
|
|
decltype(y)>();
|
|
return internal::invoke(f, x, std::forward<decltype(y)>(y));
|
|
};
|
|
}
|
|
|
|
// API search type: bind_2nd_of_2 : (((a, b) -> c), b) -> (a -> c)
|
|
// Bind second parameter of binary function.
|
|
template <typename F, typename T>
|
|
auto bind_2nd_of_2(F f, T y)
|
|
{
|
|
return [f, y](auto&& x) {
|
|
internal::trigger_static_asserts<internal::bind_2nd_of_2_tag,
|
|
F,
|
|
decltype(x),
|
|
T>();
|
|
return internal::invoke(f, std::forward<decltype(x)>(x), y);
|
|
};
|
|
}
|
|
|
|
// API search type: bind_1st_of_3 : (((a, b, c) -> d), a) -> ((b, c) -> d)
|
|
// Bind first parameter of ternary function.
|
|
template <typename F, typename X>
|
|
auto bind_1st_of_3(F f, X x)
|
|
{
|
|
return [f, x](auto&& y, auto&& z) {
|
|
internal::trigger_static_asserts<internal::bind_1st_of_3_tag,
|
|
F,
|
|
X,
|
|
decltype(y),
|
|
decltype(z)>();
|
|
return internal::invoke(
|
|
f, x, std::forward<decltype(y)>(y), std::forward<decltype(z)>(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 <typename F, typename X, typename Y>
|
|
auto bind_1st_and_2nd_of_3(F f, X x, Y y)
|
|
{
|
|
return [f, x, y](auto&& z) {
|
|
internal::trigger_static_asserts<internal::bind_1st_and_2nd_of_3_tag,
|
|
F,
|
|
X,
|
|
Y,
|
|
decltype(z)>();
|
|
return internal::invoke(f, x, y, std::forward<decltype(z)>(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 <typename F, typename Y, typename Z>
|
|
auto bind_2nd_and_3rd_of_3(F f, Y y, Z z)
|
|
{
|
|
return [f, y, z](auto&& x) {
|
|
internal::trigger_static_asserts<internal::bind_2nd_and_3rd_of_3_tag,
|
|
F,
|
|
decltype(x),
|
|
Y,
|
|
Z>();
|
|
return internal::invoke(f, std::forward<decltype(x)>(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 <typename F>
|
|
auto flip(F f)
|
|
{
|
|
return [f](auto&&... args) {
|
|
return internal::apply_impl(
|
|
f,
|
|
std::forward_as_tuple(std::forward<decltype(args)>(args)...),
|
|
internal::make_reverse_index_sequence<sizeof...(args)>{});
|
|
};
|
|
}
|
|
|
|
// API search type: forward_apply : (a, (a -> b)) -> b
|
|
// Forward application.
|
|
// Returns the result of applying the function f to the value x.
|
|
template <typename X, typename F>
|
|
auto forward_apply(X&& x, F f)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, X>();
|
|
return internal::invoke(f, std::forward<X>(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<typename F, typename... Args>
|
|
auto lazy(F f, Args ... args)
|
|
{
|
|
return [f, args...] {
|
|
internal::trigger_static_asserts<internal::check_arity_tag, F, Args...>();
|
|
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<typename T>
|
|
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 <typename... Fs>
|
|
auto compose(Fs&&... fs)
|
|
{
|
|
return internal::compose_impl<Fs...>(std::forward<Fs>(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 <typename Predicate>
|
|
auto logical_not(Predicate f)
|
|
{
|
|
return [f](auto&&... args) {
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
Predicate,
|
|
decltype(args)...>();
|
|
using Res =
|
|
std::decay_t<internal::invoke_result_t<Predicate, decltype(args)...>>;
|
|
static_assert(std::is_same<Res, bool>::value, "Function must return bool.");
|
|
|
|
return !internal::invoke(f, std::forward<decltype(args)>(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 <typename UnaryPredicateF, typename UnaryPredicateG>
|
|
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 <typename UnaryPredicateF, typename UnaryPredicateG>
|
|
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 <typename UnaryPredicateF, typename UnaryPredicateG>
|
|
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 <typename F,
|
|
typename FIn = typename utils::function_traits<F>::template arg<0>::type,
|
|
typename FOut = typename internal::invoke_result_t<F, FIn>,
|
|
typename MemoMap = std::unordered_map<
|
|
typename std::remove_reference<typename std::remove_const<FIn>::type>::type,
|
|
FOut>>
|
|
std::function<FOut(FIn)> memoize(F f)
|
|
{
|
|
static_assert(utils::function_traits<F>::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 <typename F, typename Cache,
|
|
typename FIn1 = typename utils::function_traits<F>::template arg<0>::type,
|
|
typename FIn2 = typename utils::function_traits<F>::template arg<1>::type,
|
|
typename FOut = typename internal::invoke_result_t<F, FIn1, FIn2>,
|
|
typename ResultF = std::function<FOut(FIn2)>>
|
|
ResultF memoize_recursive_helper(const F f, std::shared_ptr<Cache> 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<uint64_t(uint64_t)>& 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 <typename F,
|
|
typename FIn1 = typename utils::function_traits<F>::template arg<0>::type,
|
|
typename FIn2 = typename utils::function_traits<F>::template arg<1>::type,
|
|
typename FOut = typename internal::invoke_result_t<F, FIn1, FIn2>,
|
|
typename MemoMap = std::unordered_map<
|
|
typename std::remove_reference<typename std::remove_const<FIn2>::type>::type,
|
|
FOut>>
|
|
std::function<FOut(FIn2)> memoize_recursive(F f)
|
|
{
|
|
std::shared_ptr<MemoMap> storage = std::make_shared<MemoMap>();
|
|
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 <typename F,
|
|
typename FIn1 = typename utils::function_traits<F>::template arg<0>::type,
|
|
typename FIn2 = typename utils::function_traits<F>::template arg<1>::type,
|
|
typename FOut = typename internal::invoke_result_t<F, FIn1, FIn2>,
|
|
typename ParamPair = std::pair<
|
|
typename std::remove_reference<typename std::remove_const<FIn1>::type>::type,
|
|
typename std::remove_reference<typename std::remove_const<FIn2>::type>::type>,
|
|
typename MemoMap = std::unordered_map<ParamPair, FOut>>
|
|
std::function<FOut(FIn1, FIn2)> 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<decltype(unary_f),
|
|
ParamPair, FOut, std::map<ParamPair, FOut>>(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<foo, int, int>;
|
|
// create_foo(1,2) == foo(1, 2);
|
|
template <typename T, class ... Types>
|
|
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 <type_traits>
|
|
|
|
|
|
namespace fplus
|
|
{
|
|
namespace internal
|
|
{
|
|
template <typename Compare>
|
|
auto ord_to_impl(Compare comp)
|
|
{
|
|
return [comp](auto x, auto y)
|
|
{
|
|
static_assert(std::is_same<decltype(x), decltype(y)>::value,
|
|
"Argument types must be the same");
|
|
using In = decltype(x);
|
|
internal::trigger_static_asserts<internal::binary_predicate_tag, Compare, In, In>();
|
|
|
|
using CompareOut = std::decay_t<internal::invoke_result_t<Compare, In, In>>;
|
|
static_assert(std::is_same<CompareOut, bool>::value,
|
|
"Function must return bool.");
|
|
return std::make_pair(internal::invoke(comp, x, y),
|
|
internal::invoke(comp, y, x));
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
namespace internal
|
|
{
|
|
template <typename UnaryPredicate, typename T>
|
|
void check_unary_predicate_for_type()
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, UnaryPredicate, T>();
|
|
static_assert(std::is_convertible<
|
|
internal::invoke_result_t<UnaryPredicate, T>, bool>::value,
|
|
"Predicate must return bool.");
|
|
}
|
|
template <typename F, typename G, typename X, typename Y>
|
|
void check_compare_preprocessors_for_types()
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, X>();
|
|
internal::trigger_static_asserts<internal::unary_function_tag, G, Y>();
|
|
static_assert(std::is_same<
|
|
std::decay_t<internal::invoke_result_t<F, X>>,
|
|
std::decay_t<internal::invoke_result_t<G, Y>>>::value,
|
|
"Both functions must return the same type.");
|
|
}
|
|
} // namespace internal
|
|
|
|
// API search type: identity : a -> a
|
|
// fwd bind count: 0
|
|
// identity(x) == x
|
|
template <typename T>
|
|
T identity(const T& x)
|
|
{
|
|
return x;
|
|
}
|
|
|
|
// API search type: is_equal : (a, a) -> Bool
|
|
// fwd bind count: 1
|
|
// x == y
|
|
// Equality check.
|
|
template <typename T>
|
|
bool is_equal(const T& x, const T& y)
|
|
{
|
|
return x == y;
|
|
}
|
|
|
|
// API search type: always : a -> (b -> a)
|
|
// always(x)(y) == x
|
|
template <typename X>
|
|
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 <typename X, typename Y>
|
|
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 <typename X, typename Y>
|
|
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 <typename F, typename G>
|
|
auto is_equal_by_and_by(F f, G g)
|
|
{
|
|
return [f, g](const auto& x, const auto& y) {
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
F,
|
|
decltype(x)>();
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
G,
|
|
decltype(y)>();
|
|
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 <typename F>
|
|
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 <typename F, typename X>
|
|
auto is_equal_by_to(F f, const X& x)
|
|
{
|
|
return [f, x](const auto& y)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
F,
|
|
decltype(y)>();
|
|
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 <typename X>
|
|
auto is_equal_to(const X& x)
|
|
{
|
|
return is_equal_by_to(identity<X>, x);
|
|
}
|
|
|
|
// API search type: is_not_equal : (a, a) -> Bool
|
|
// fwd bind count: 1
|
|
// x != y
|
|
// Unequally check.
|
|
template <typename T>
|
|
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 <typename F, typename G>
|
|
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::unary_function_tag,
|
|
F,
|
|
decltype(x)>();
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
G,
|
|
decltype(y)>();
|
|
using FOut = std::decay_t<internal::invoke_result_t<F, decltype(x)>>;
|
|
using GOut = std::decay_t<internal::invoke_result_t<G, decltype(y)>>;
|
|
static_assert(std::is_same<FOut, GOut>::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 <typename F>
|
|
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 <typename F, typename X>
|
|
auto is_not_equal_by_to(F f, const X& x)
|
|
{
|
|
return [f, x](const auto& y) {
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
F,
|
|
decltype(y)>();
|
|
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 <typename X>
|
|
auto is_not_equal_to(const X& x)
|
|
{
|
|
return is_not_equal_by_to(identity<X>, x);
|
|
}
|
|
|
|
// API search type: is_less : (a, a) -> Bool
|
|
// fwd bind count: 1
|
|
// x < y
|
|
// Less check.
|
|
template <typename T>
|
|
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 <typename F, typename G>
|
|
auto is_less_by_and_by(F f, G g)
|
|
{
|
|
return [f, g](const auto& x, const auto& y)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
F,
|
|
decltype(x)>();
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
G,
|
|
decltype(y)>();
|
|
using FOut = std::decay_t<internal::invoke_result_t<F, decltype(x)>>;
|
|
using GOut = std::decay_t<internal::invoke_result_t<G, decltype(y)>>;
|
|
static_assert(std::is_same<FOut, GOut>::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 <typename F>
|
|
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 <typename F, typename X>
|
|
auto is_less_by_than(F f, const X& x)
|
|
{
|
|
return [f, x](const auto& y)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
F,
|
|
decltype(y)>();
|
|
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 <typename X>
|
|
auto is_less_than(const X& x)
|
|
{
|
|
return is_less_by_than(identity<X>, x);
|
|
}
|
|
|
|
// API search type: is_less_or_equal : (a, a) -> Bool
|
|
// fwd bind count: 1
|
|
// x <= y
|
|
// Less-or-equal check.
|
|
template <typename T>
|
|
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 <typename F, typename G>
|
|
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<F, G, FIn, GIn>();
|
|
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 <typename F>
|
|
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 <typename F, typename X>
|
|
auto is_less_or_equal_by_than(F f, const X& x)
|
|
{
|
|
return [f, x](const auto& y)
|
|
{
|
|
internal::
|
|
trigger_static_asserts<internal::unary_function_tag, F, decltype(y)>();
|
|
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 <typename X>
|
|
auto is_less_or_equal_than(const X& x)
|
|
{
|
|
return is_less_or_equal_by_than(identity<X>, x);
|
|
}
|
|
|
|
// API search type: is_greater : a -> a -> Bool
|
|
// fwd bind count: 1
|
|
// x > y
|
|
// Greater check.
|
|
template <typename T>
|
|
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 <typename F, typename G>
|
|
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<F, G, FIn, GIn>();
|
|
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 <typename F>
|
|
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 <typename F, typename X>
|
|
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 <typename X>
|
|
auto is_greater_than(const X& x)
|
|
{
|
|
return is_greater_by_than(identity<X>, x);
|
|
}
|
|
|
|
// API search type: is_greater_or_equal : (a, a) -> Bool
|
|
// fwd bind count: 1
|
|
// x >= y
|
|
// Greater-or-equal check.
|
|
template <typename T>
|
|
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 <typename F, typename G>
|
|
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<F, G, FIn, GIn>();
|
|
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 <typename F>
|
|
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 <typename F, typename X>
|
|
auto is_greater_or_equal_by_than(F f, const X& x)
|
|
{
|
|
return [f, x](const auto& y)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, decltype(y)>();
|
|
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 <typename X>
|
|
auto is_greater_or_equal_than(const X& x)
|
|
{
|
|
return is_greater_or_equal_by_than(identity<X>, x);
|
|
}
|
|
|
|
// API search type: xor_bools : (Bool, Bool) -> Bool
|
|
// fwd bind count: 1
|
|
// Exclusive or.
|
|
template <typename T>
|
|
bool xor_bools(const T& x, const T& y)
|
|
{
|
|
static_assert(std::is_convertible<T, bool>::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 <typename Compare>
|
|
auto ord_to_eq(Compare comp)
|
|
{
|
|
return [comp](auto x, auto y)
|
|
{
|
|
static_assert(std::is_same<decltype(x), decltype(y)>::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 <typename Compare>
|
|
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 <typename Compare>
|
|
auto ord_eq_to_eq(Compare comp)
|
|
{
|
|
return [comp](auto x, auto y)
|
|
{
|
|
static_assert(std::is_same<decltype(x), decltype(y)>::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 <typename Compare>
|
|
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 <array>
|
|
#include <deque>
|
|
#include <forward_list>
|
|
#include <list>
|
|
#include <map>
|
|
#include <limits>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <queue>
|
|
#include <set>
|
|
#include <stack>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
namespace internal
|
|
{
|
|
|
|
#ifdef __GNUC__
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Weffc++"
|
|
#endif
|
|
|
|
template<class T> struct has_order : public std::false_type {};
|
|
template<class T, std::size_t N> struct has_order<std::array<T, N>> : public std::true_type {};
|
|
template<class T, class Alloc> struct has_order<std::vector<T, Alloc>> : public std::true_type {};
|
|
template<class T, class Alloc> struct has_order<std::deque<T, Alloc>> : public std::true_type {};
|
|
template<class T, class Alloc> struct has_order<std::forward_list<T, Alloc>> : public std::true_type {};
|
|
template<class T, class Alloc> struct has_order<std::list<T, Alloc>> : public std::true_type {};
|
|
template<class T, class Alloc> struct has_order<std::set<T, Alloc>> : public std::false_type {};
|
|
template<class T, class Container> struct has_order<std::stack<T, Container>> : public std::true_type {};
|
|
template<class T, class Container> struct has_order<std::queue<T, Container>> : public std::true_type {};
|
|
template<class T, class Container, class Compare> struct has_order<std::priority_queue<T, Container, Compare>> : public std::false_type {};
|
|
template<class CharT, class Traits, class Alloc> struct has_order<std::basic_string<CharT, Traits, Alloc>> : public std::true_type {};
|
|
|
|
// http://stackoverflow.com/a/33828321/1866775
|
|
template<class Cont, class NewT, int SizeOffset = std::numeric_limits<int>::lowest()> struct same_cont_new_t : public std::false_type{};
|
|
template<class T, std::size_t N, class NewT, int SizeOffset> struct same_cont_new_t<std::array<T, N>, NewT, SizeOffset>
|
|
{
|
|
static_assert(SizeOffset != std::numeric_limits<int>::lowest(), "Size of std::array must be known at compile-time.");
|
|
typedef typename std::array<NewT, static_cast<std::size_t>(static_cast<int>(N) + SizeOffset)> type;
|
|
};
|
|
template<class T, template<class> class Alloc, class NewT, int SizeOffset> struct same_cont_new_t<std::vector<T, Alloc<T>>, NewT, SizeOffset> { typedef typename std::vector<NewT, Alloc<NewT>> type; };
|
|
template<class T, template<class> class Alloc, class NewT, int SizeOffset> struct same_cont_new_t<std::deque<T, Alloc<T>>, NewT, SizeOffset> { typedef typename std::deque<NewT, Alloc<NewT>> type; };
|
|
template<class T, template<class> class Alloc, class NewT, int SizeOffset> struct same_cont_new_t<std::forward_list<T, Alloc<T>>, NewT, SizeOffset> { typedef typename std::forward_list<NewT, Alloc<NewT>> type; };
|
|
template<class T, template<class> class Alloc, class NewT, int SizeOffset> struct same_cont_new_t<std::list<T, Alloc<T>>, NewT, SizeOffset> { typedef typename std::list<NewT, Alloc<NewT>> type; };
|
|
template<class T, template<class> class Alloc, class NewT, int SizeOffset> struct same_cont_new_t<std::set<T, Alloc<T>>, NewT, SizeOffset> { typedef typename std::set<NewT, Alloc<NewT>> type; };
|
|
template<class T, class Container, class NewT, int SizeOffset> struct same_cont_new_t<std::stack<T, Container>, NewT, SizeOffset> { typedef typename std::stack<NewT, Container> type; };
|
|
template<class T, class Container, class NewT, int SizeOffset> struct same_cont_new_t<std::queue<T, Container>, NewT, SizeOffset> { typedef typename std::queue<NewT, Container> type; };
|
|
template<class T, class Container, class Compare, class NewT, int SizeOffset> struct same_cont_new_t<std::priority_queue<T, Container, Compare>, NewT, SizeOffset> { typedef typename std::priority_queue<NewT, Container, Compare> type; };
|
|
template<class CharT, class Traits, class Alloc, class NewT, int SizeOffset> struct same_cont_new_t<std::basic_string<CharT, Traits, Alloc>, NewT, SizeOffset> { typedef typename std::basic_string<NewT, Traits, Alloc> type; };
|
|
|
|
// For aligned allocators.
|
|
template<class T, template<class, std::size_t> class Alloc, class NewT, int SizeOffset, std::size_t N> struct same_cont_new_t<std::vector<T, Alloc<T, N>>, NewT, SizeOffset> { typedef typename std::vector<NewT, Alloc<NewT, N>> type; };
|
|
template<class T, template<class, std::size_t> class Alloc, class NewT, int SizeOffset, std::size_t N> struct same_cont_new_t<std::deque<T, Alloc<T, N>>, NewT, SizeOffset> { typedef typename std::deque<NewT, Alloc<NewT, N>> type; };
|
|
|
|
template<class Cont, class NewKey, class NewVal> struct SameMapTypeNewTypes : public std::false_type {};
|
|
template<class Key, class T, class Compare, class Alloc, class NewKey, class NewVal> struct SameMapTypeNewTypes<std::map<Key, T, Compare, Alloc>, NewKey, NewVal> { typedef typename std::map<NewKey, NewVal> type; };
|
|
template<class Key, class T, class Compare, class Alloc, class NewKey, class NewVal> struct SameMapTypeNewTypes<std::unordered_map<Key, T, Compare, Alloc>, NewKey, NewVal> { typedef typename std::unordered_map<NewKey, NewVal> type; };
|
|
|
|
#ifdef __GNUC__
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
template<
|
|
typename ContIn,
|
|
typename F,
|
|
int SizeOffset = std::numeric_limits<int>::lowest(),
|
|
typename T = typename ContIn::value_type,
|
|
typename ContOut = typename same_cont_new_t<ContIn, std::decay_t<internal::invoke_result_t<F, 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<int>::lowest(),
|
|
typename ContOut = typename same_cont_new_t<ContIn, std::decay_t<internal::invoke_result_t<F, T1, T2>>, SizeOffset>::type>
|
|
struct same_cont_new_t_from_binary_f
|
|
{
|
|
typedef ContOut type;
|
|
};
|
|
|
|
|
|
|
|
// https://stackoverflow.com/a/44549820/1866775
|
|
|
|
template<class T>
|
|
struct can_self_assign {
|
|
using type = std::is_assignable<T&, T>;
|
|
};
|
|
|
|
template<typename T>
|
|
using can_self_assign_t = typename can_self_assign<T>::type;
|
|
|
|
template<typename T0, typename T1>
|
|
struct can_self_assign<std::pair<T0, T1>>
|
|
{
|
|
enum { t0 = can_self_assign_t<T0>::value, t1 = can_self_assign_t<T1>::value, x = t0&&t1 };
|
|
using type = std::integral_constant<bool, x>;
|
|
};
|
|
|
|
template<>
|
|
struct can_self_assign<std::tuple<>>
|
|
{
|
|
using type = std::integral_constant<bool, true>;
|
|
};
|
|
template<typename T0, typename...Ts>
|
|
struct can_self_assign<std::tuple<T0, Ts...>>
|
|
{
|
|
using type = std::integral_constant<bool, can_self_assign_t<T0>::value && can_self_assign_t<std::tuple<Ts...>>::value >;
|
|
};
|
|
|
|
template<class T, T v>
|
|
struct reuse_container_bool_t {
|
|
};
|
|
using create_new_container_t = reuse_container_bool_t<bool, false>;
|
|
using reuse_container_t = reuse_container_bool_t<bool, true>;
|
|
|
|
template <typename Container>
|
|
struct can_reuse
|
|
{
|
|
using dContainer = typename std::decay<Container>::type;
|
|
using can_assign = can_self_assign_t<typename dContainer::value_type>;
|
|
using cannot_reuse = std::is_lvalue_reference<Container>;
|
|
using value = reuse_container_bool_t<bool, can_assign::value && !cannot_reuse::value>;
|
|
};
|
|
|
|
template<typename Container>
|
|
using can_reuse_v = typename can_reuse<Container>::value;
|
|
|
|
template <typename T>
|
|
struct remove_const_and_ref
|
|
{
|
|
using type = typename std::remove_const<typename std::remove_reference<T>::type>::type;
|
|
};
|
|
|
|
template<typename T>
|
|
using remove_const_and_ref_t = typename remove_const_and_ref<T>::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 <cassert>
|
|
#include <exception>
|
|
#include <functional>
|
|
#include <memory>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
// Can hold a value of type T or nothing.
|
|
template <typename T>
|
|
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<const T*>(&value_);
|
|
}
|
|
T& unsafe_get_just()
|
|
{
|
|
assert(is_just());
|
|
return *reinterpret_cast<T*>(&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<T>& other) : is_present_(other.is_just()), value_()
|
|
{
|
|
if (is_present_)
|
|
{
|
|
new (&value_) T(other.unsafe_get_just());
|
|
}
|
|
}
|
|
maybe(maybe<T>&& other) : is_present_(std::move(other.is_present_)), value_()
|
|
{
|
|
if (is_present_)
|
|
{
|
|
new (&value_) T(std::move(other.unsafe_get_just()));
|
|
}
|
|
}
|
|
maybe<T>& 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<T>& operator = (const maybe<T>& other)
|
|
{
|
|
destruct_content();
|
|
if (other.is_just())
|
|
{
|
|
is_present_ = true;
|
|
new (&value_) T(other.unsafe_get_just());
|
|
}
|
|
return *this;
|
|
}
|
|
maybe& operator = (maybe<T>&& 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<const T*>(&value_)).~T();
|
|
}
|
|
}
|
|
bool is_present_;
|
|
typename std::aligned_storage<sizeof(T), alignof(T)>::type value_;
|
|
};
|
|
|
|
namespace internal
|
|
{
|
|
template <typename>
|
|
struct is_maybe : std::false_type
|
|
{
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_maybe<maybe<T>> : std::true_type
|
|
{
|
|
};
|
|
}
|
|
|
|
// API search type: is_just : Maybe a -> Bool
|
|
// fwd bind count: 0
|
|
// Is not nothing?
|
|
template <typename T>
|
|
bool is_just(const maybe<T>& maybe)
|
|
{
|
|
return maybe.is_just();
|
|
}
|
|
|
|
// API search type: is_nothing : Maybe a -> Bool
|
|
// fwd bind count: 0
|
|
// Has no value?
|
|
template <typename T>
|
|
bool is_nothing(const maybe<T>& maybe)
|
|
{
|
|
return !is_just(maybe);
|
|
}
|
|
|
|
// API search type: unsafe_get_just : Maybe a -> a
|
|
// fwd bind count: 0
|
|
// Crashes if maybe is nothing!
|
|
template <typename T>
|
|
T unsafe_get_just(const maybe<T>& 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 <typename T>
|
|
T just_with_default(const T& defaultValue, const maybe<T>& 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 <typename E, typename T>
|
|
T throw_on_nothing(const E& e, const maybe<T>& 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 <typename T>
|
|
maybe<T> 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 <typename Pred, typename T>
|
|
maybe<T> as_just_if(Pred pred, const T& val)
|
|
{
|
|
internal::check_unary_predicate_for_type<Pred, T>();
|
|
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 <typename T, typename ContainerOut = std::vector<T>>
|
|
ContainerOut maybe_to_seq(const maybe<T>& 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 <typename Container>
|
|
maybe<typename Container::value_type>
|
|
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 <typename T>
|
|
maybe<T> nothing()
|
|
{
|
|
return {};
|
|
}
|
|
|
|
// True if just values are the same or if both are nothing.
|
|
template <typename T>
|
|
bool operator == (const maybe<T>& x, const maybe<T>& 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 <typename T>
|
|
bool operator != (const maybe<T>& x, const maybe<T>& 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<int> into a Maybe<string>.
|
|
// A nothing remains a nothing, regardless of the conversion.
|
|
template <typename F, typename A>
|
|
auto lift_maybe(F f, const maybe<A>& m)
|
|
{
|
|
internal::trigger_static_asserts<internal::check_arity_tag, F, A>();
|
|
|
|
using B = std::decay_t<internal::invoke_result_t<F, A>>;
|
|
if (is_just(m))
|
|
return just<B>(internal::invoke(f, unsafe_get_just(m)));
|
|
return nothing<B>();
|
|
}
|
|
|
|
// 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 <typename F, typename A, typename Default>
|
|
auto lift_maybe_def(const Default& def, F f, const maybe<A>& m)
|
|
{
|
|
internal::trigger_static_asserts<internal::check_arity_tag, F, A>();
|
|
|
|
using B = std::decay_t<internal::invoke_result_t<F, A>>;
|
|
static_assert(
|
|
std::is_convertible<Default, B>::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 <typename F, typename A, typename B>
|
|
auto lift_maybe_2(F f, const maybe<A>& m_a, const maybe<B>& m_b)
|
|
{
|
|
internal::trigger_static_asserts<internal::check_arity_tag, F, A, B>();
|
|
|
|
using FOut = std::decay_t<internal::invoke_result_t<F, A, B>>;
|
|
if (is_just(m_a) && is_just(m_b))
|
|
{
|
|
return just<FOut>(
|
|
internal::invoke(f, unsafe_get_just(m_a), unsafe_get_just(m_b)));
|
|
}
|
|
return nothing<FOut>();
|
|
}
|
|
|
|
// 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 <typename F, typename A, typename B, typename Default>
|
|
auto lift_maybe_2_def(const Default& def,
|
|
F f,
|
|
const maybe<A>& m_a,
|
|
const maybe<B>& m_b)
|
|
{
|
|
internal::trigger_static_asserts<internal::check_arity_tag, F, A, B>();
|
|
|
|
using C = std::decay_t<internal::invoke_result_t<F, A, B>>;
|
|
static_assert(
|
|
std::is_convertible<Default, C>::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 <typename A>
|
|
maybe<A> join_maybe(const maybe<maybe<A>>& m)
|
|
{
|
|
if (is_just(m))
|
|
return unsafe_get_just(m);
|
|
else
|
|
return nothing<A>();
|
|
}
|
|
|
|
// 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 <typename T, typename F>
|
|
auto and_then_maybe(F f, const maybe<T>& m)
|
|
{
|
|
internal::trigger_static_asserts<internal::check_arity_tag, F, T>();
|
|
using FOut = std::decay_t<internal::invoke_result_t<F, T>>;
|
|
static_assert(internal::is_maybe<FOut>::value,
|
|
"Function must return a maybe<> type");
|
|
if (is_just(m))
|
|
return internal::invoke(f, unsafe_get_just(m));
|
|
else
|
|
return nothing<typename FOut::type>();
|
|
}
|
|
|
|
// 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 <typename... Callables>
|
|
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<decltype(f), decltype(args)...>>;
|
|
static_assert(internal::is_maybe<FOut>::value,
|
|
"Functions must return a maybe<> type");
|
|
using GOut = std::decay_t<
|
|
internal::invoke_result_t<decltype(g), typename FOut::type>>;
|
|
static_assert(internal::is_maybe<GOut>::value,
|
|
"Functions must return a maybe<> type");
|
|
|
|
auto maybeB =
|
|
internal::invoke(f, std::forward<decltype(args)>(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>(callables)...);
|
|
}
|
|
|
|
// API search type: flatten_maybe : (Maybe (Maybe a)) -> Maybe a
|
|
// fwd bind count: 0
|
|
// Also known as join.
|
|
template <typename T>
|
|
maybe<T> flatten_maybe(const maybe<maybe<T>>& maybe_maybe)
|
|
{
|
|
if (is_nothing(maybe_maybe))
|
|
return nothing<T>();
|
|
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 <numeric>
|
|
#include <type_traits>
|
|
|
|
|
|
namespace fplus
|
|
{
|
|
namespace internal
|
|
{
|
|
|
|
template<class InputIt, class T>
|
|
T accumulate(InputIt first, InputIt last, T init)
|
|
{
|
|
for (; first != last; ++first) {
|
|
init = std::move(init) + *first;
|
|
}
|
|
return init;
|
|
}
|
|
|
|
template<class InputIt, class T, class BinaryOperation>
|
|
T accumulate(InputIt first, InputIt last, T init,
|
|
BinaryOperation op)
|
|
{
|
|
for (; first != last; ++first) {
|
|
init = op(std::move(init), *first);
|
|
}
|
|
return init;
|
|
}
|
|
|
|
template <typename F,
|
|
typename Acc,
|
|
typename InputIterator,
|
|
typename OutputIterator>
|
|
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 <algorithm>
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <iterator>
|
|
#include <numeric>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
namespace internal
|
|
{
|
|
template <typename UnaryPredicate, typename Container>
|
|
void check_unary_predicate_for_container()
|
|
{
|
|
internal::check_unary_predicate_for_type<UnaryPredicate,
|
|
typename Container::value_type>();
|
|
}
|
|
|
|
template <typename F, typename Container>
|
|
void check_index_with_type_predicate_for_container()
|
|
{
|
|
typedef typename Container::value_type T;
|
|
internal::trigger_static_asserts<internal::binary_function_tag, F, std::size_t, T>();
|
|
static_assert(std::is_convertible<
|
|
internal::invoke_result_t<F, std::size_t, T>, bool>::value,
|
|
"Function must return bool.");
|
|
}
|
|
|
|
template <typename Compare, typename Container>
|
|
void check_compare_for_container()
|
|
{
|
|
typedef typename Container::value_type T;
|
|
internal::trigger_static_asserts<internal::binary_predicate_tag, Compare, T, T>();
|
|
}
|
|
|
|
template <typename BinaryPredicate, typename Container>
|
|
void check_binary_predicate_for_container()
|
|
{
|
|
typedef typename Container::value_type T;
|
|
internal::trigger_static_asserts<internal::binary_predicate_tag, BinaryPredicate, T, T>();
|
|
}
|
|
|
|
// PrepareContainer and BackInserter are overloaded
|
|
// to increase performance on std::vector and std::string
|
|
// by using std::vector<T>::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 <typename C>
|
|
void prepare_container(const std::basic_string<C, std::char_traits<C>,
|
|
std::allocator<C>>& ys, std::size_t size)
|
|
{
|
|
ys.reserve(size);
|
|
}
|
|
|
|
template <typename Y>
|
|
void prepare_container(std::vector<Y>& ys, std::size_t size)
|
|
{
|
|
ys.reserve(size);
|
|
}
|
|
|
|
template <typename T, std::size_t N>
|
|
void prepare_container(std::array<T, N>&, std::size_t size)
|
|
{
|
|
assert(size == N);
|
|
unused(size);
|
|
}
|
|
|
|
template <typename Y>
|
|
void prepare_container(std::unordered_set<Y>& ys, std::size_t size)
|
|
{
|
|
ys.reserve(size);
|
|
}
|
|
|
|
template <typename Key, typename T>
|
|
void prepare_container(std::unordered_map<Key, T>& ys, std::size_t size)
|
|
{
|
|
ys.reserve(size);
|
|
}
|
|
|
|
template <typename Y>
|
|
void prepare_container(std::unordered_multiset<Y>& ys, std::size_t size)
|
|
{
|
|
ys.reserve(size);
|
|
}
|
|
|
|
template <typename Key, typename T>
|
|
void prepare_container(std::unordered_multimap<Key, T>& ys, std::size_t size)
|
|
{
|
|
ys.reserve(size);
|
|
}
|
|
|
|
template <typename Container>
|
|
void prepare_container(Container&, std::size_t)
|
|
{
|
|
}
|
|
|
|
template <typename Container>
|
|
std::back_insert_iterator<Container> get_back_inserter(std::string& ys)
|
|
{
|
|
return std::back_inserter(ys);
|
|
}
|
|
|
|
template <typename Container, typename Y>
|
|
std::back_insert_iterator<Container> get_back_inserter(std::vector<Y>& ys)
|
|
{
|
|
return std::back_inserter(ys);
|
|
}
|
|
|
|
template <typename Container, typename Y>
|
|
std::back_insert_iterator<Container> get_back_inserter(std::list<Y>& ys)
|
|
{
|
|
return std::back_inserter(ys);
|
|
}
|
|
|
|
template <typename Container, typename Y>
|
|
std::back_insert_iterator<Container> get_back_inserter(std::deque<Y>& ys)
|
|
{
|
|
return std::back_inserter(ys);
|
|
}
|
|
|
|
// Avoid self-assignment.
|
|
template <typename T>
|
|
void assign(T& x, T&& y) {
|
|
if (&x != &y)
|
|
x = std::move(y);
|
|
}
|
|
|
|
template <typename T, std::size_t N>
|
|
struct array_back_insert_iterator : public std::back_insert_iterator<std::array<T, N>>
|
|
{
|
|
typedef std::back_insert_iterator<std::array<T, N>> base_type;
|
|
explicit array_back_insert_iterator(std::array<T, N>& arr) :
|
|
base_type(arr), arr_ptr_(&arr), pos_(0) {}
|
|
array_back_insert_iterator(const array_back_insert_iterator<T, N>& other) :
|
|
base_type(*other.arr_ptr_), arr_ptr_(other.arr_ptr_), pos_(other.pos_) {}
|
|
array_back_insert_iterator<T, N>& operator=(const array_back_insert_iterator<T, N>& other)
|
|
{
|
|
arr_ptr_ = other.arr_ptr_;
|
|
pos_ = other.pos_;
|
|
return *this;
|
|
}
|
|
~array_back_insert_iterator()
|
|
{
|
|
assert(pos_ == 0 || pos_ == N);
|
|
}
|
|
array_back_insert_iterator<T, N>& operator=(const T& x)
|
|
{
|
|
assert(pos_ < N);
|
|
(*arr_ptr_)[pos_] = x;
|
|
++pos_;
|
|
return *this;
|
|
}
|
|
array_back_insert_iterator<T, N>& operator=(T&& x)
|
|
{
|
|
assert(pos_ < N);
|
|
assign((*arr_ptr_)[pos_], std::move(x));
|
|
++pos_;
|
|
return *this;
|
|
}
|
|
array_back_insert_iterator<T, N>& operator*() { return *this; }
|
|
array_back_insert_iterator<T, N>& operator++() { return *this; }
|
|
array_back_insert_iterator<T, N> operator++(int) { return *this; }
|
|
private:
|
|
std::array<T, N>* arr_ptr_;
|
|
std::size_t pos_;
|
|
};
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
|
template <typename T, std::size_t N>
|
|
struct std::_Is_checked_helper<array_back_insert_iterator<T, N>>
|
|
: public true_type
|
|
{ // mark array_back_insert_iterator as checked
|
|
};
|
|
#endif
|
|
|
|
template <typename Container, typename Y, std::size_t N>
|
|
array_back_insert_iterator<Y, N> get_back_inserter(std::array<Y, N>& ys)
|
|
{
|
|
return array_back_insert_iterator<Y, N>(ys);
|
|
}
|
|
|
|
template <typename Container>
|
|
std::insert_iterator<Container> get_back_inserter(Container& ys)
|
|
{
|
|
return std::inserter(ys, std::end(ys));
|
|
}
|
|
|
|
template <typename Iterator>
|
|
void advance_iterator(Iterator& it, std::size_t distance)
|
|
{
|
|
std::advance(it,
|
|
static_cast<typename Iterator::difference_type>(distance));
|
|
}
|
|
|
|
template <typename T>
|
|
void advance_iterator(T*& it, std::size_t distance)
|
|
{
|
|
it += static_cast<std::ptrdiff_t>(distance);
|
|
}
|
|
|
|
template <typename Iterator>
|
|
Iterator add_to_iterator(Iterator it, std::size_t distance = 1)
|
|
{
|
|
return std::next(it,
|
|
static_cast<typename Iterator::difference_type>(distance));
|
|
}
|
|
|
|
// GCC 4.9 does not support std::rbegin, std::rend and std::make_reverse_iterator
|
|
template <typename Iterator>
|
|
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it)
|
|
{
|
|
return std::reverse_iterator<Iterator>(it);
|
|
}
|
|
} // namespace internal
|
|
|
|
// API search type: is_even : Int -> Bool
|
|
// fwd bind count: 0
|
|
// Checks if x is even.
|
|
template <typename X>
|
|
bool is_even(X x)
|
|
{
|
|
static_assert(std::is_integral<X>::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 <typename X>
|
|
bool is_odd(X x)
|
|
{
|
|
static_assert(std::is_integral<X>::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 <typename Container>
|
|
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 <typename Container>
|
|
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 <typename Container>
|
|
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 <typename Dest, typename Source>
|
|
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<NewT>([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)]
|
|
template <typename NewT, typename ContainerIn,
|
|
typename ContainerOut = typename internal::same_cont_new_t<ContainerIn, NewT, 0>::type>
|
|
ContainerOut convert_elems(const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_constructible<NewT,
|
|
typename ContainerIn::value_type>::value,
|
|
"Elements not convertible.");
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, size_of_cont(xs));
|
|
auto it = internal::get_back_inserter<ContainerOut>(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<NewT>(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 <typename ContainerOut, typename ContainerIn>
|
|
ContainerOut convert_container(const ContainerIn& xs)
|
|
{
|
|
typedef typename ContainerIn::value_type SourceElem;
|
|
typedef typename ContainerOut::value_type DestElem;
|
|
static_assert(std::is_same<DestElem, SourceElem>::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<ContainerOut>(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 <typename ContainerOut, typename ContainerIn>
|
|
ContainerOut convert_container_and_elems(const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_convertible<typename ContainerIn::value_type,
|
|
typename ContainerOut::value_type>::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<ContainerOut>(ys);
|
|
for (const auto& x : xs)
|
|
{
|
|
*it = convert<DestElem>(x);
|
|
}
|
|
return ys;
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Container>
|
|
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<Container>(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<Container>(xs);
|
|
}
|
|
|
|
template <typename Container>
|
|
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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut get_segment
|
|
(std::size_t idx_begin, std::size_t idx_end, Container&& xs)
|
|
{
|
|
return internal::get_segment(internal::can_reuse_v<Container>{},
|
|
idx_begin, idx_end, std::forward<Container>(xs));
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename ContainerToken, typename Container>
|
|
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<Container>(xs);
|
|
}
|
|
|
|
template <typename ContainerToken, typename Container>
|
|
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 <typename ContainerToken, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut set_segment
|
|
(std::size_t idx_begin, const ContainerToken& token, Container&& xs)
|
|
{
|
|
return internal::set_segment(internal::can_reuse_v<Container>{},
|
|
idx_begin, token, std::forward<Container>(xs));
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Container>
|
|
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<Container>(xs);
|
|
}
|
|
|
|
template <typename Container>
|
|
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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut remove_segment(
|
|
std::size_t idx_begin, std::size_t idx_end, Container&& xs)
|
|
{
|
|
return internal::remove_segment(internal::can_reuse_v<Container>{},
|
|
idx_begin, idx_end, std::forward<Container>(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 <typename Container>
|
|
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 <typename Container>
|
|
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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
maybe<T> 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 <typename Container,
|
|
typename ContainerIdxs,
|
|
typename T = typename Container::value_type,
|
|
typename ContainerOut = std::vector<T>>
|
|
std::vector<T> elems_at_idxs(const ContainerIdxs& idxs, const Container& xs)
|
|
{
|
|
static_assert(std::is_same<typename ContainerIdxs::value_type, std::size_t>::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 <typename Container, typename F>
|
|
Container transform(internal::reuse_container_t, F f, Container&& xs)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
F,
|
|
decltype(*std::begin(xs))>();
|
|
std::transform(std::begin(xs), std::end(xs), std::begin(xs), f);
|
|
return std::forward<Container>(xs);
|
|
}
|
|
|
|
template <typename ContainerOut, typename F, typename ContainerIn>
|
|
ContainerOut transform(internal::create_new_container_t, F f,
|
|
const ContainerIn& xs)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
F,
|
|
decltype(*std::begin(xs))>();
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, size_of_cont(xs));
|
|
auto it = internal::get_back_inserter<ContainerOut>(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 <typename F, typename ContainerIn,
|
|
typename ContainerOut = typename internal::same_cont_new_t_from_unary_f<
|
|
internal::remove_const_and_ref_t<ContainerIn>, F, 0>::type>
|
|
ContainerOut transform(F f, ContainerIn&& xs)
|
|
{
|
|
using reuse_t = typename std::conditional<
|
|
std::is_same<
|
|
internal::can_reuse_v<ContainerIn>,
|
|
internal::reuse_container_t>::value &&
|
|
std::is_base_of<
|
|
std::true_type,
|
|
internal::has_order<ContainerIn>>::value &&
|
|
std::is_same<
|
|
internal::remove_const_and_ref_t<ContainerIn>,
|
|
ContainerOut>::value,
|
|
internal::reuse_container_t,
|
|
internal::create_new_container_t>::type;
|
|
return internal::transform<ContainerOut>(
|
|
reuse_t{}, f, std::forward<ContainerIn>(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 <typename ContainerOut, typename F, typename ContainerIn>
|
|
ContainerOut transform_convert(F f, const ContainerIn& xs)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type>();
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, size_of_cont(xs));
|
|
auto it = internal::get_back_inserter<ContainerOut>(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 <typename F, typename ContainerIn,
|
|
typename ContainerOut =
|
|
typename internal::same_cont_new_t<
|
|
ContainerIn,
|
|
typename internal::same_cont_new_t_from_unary_f<
|
|
typename ContainerIn::value_type, F, 0
|
|
>::type, 0
|
|
>::type>
|
|
ContainerOut transform_inner(F f, const ContainerIn& xs)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type::value_type>();
|
|
return fplus::transform(
|
|
fplus::bind_1st_of_2(
|
|
fplus::transform<F, const typename ContainerIn::value_type&>, f),
|
|
xs);
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Container>
|
|
Container reverse(internal::reuse_container_t, Container&& xs)
|
|
{
|
|
static_assert(internal::has_order<Container>::value,
|
|
"Reverse: Container has no order.");
|
|
std::reverse(std::begin(xs), std::end(xs));
|
|
return std::forward<Container>(xs);
|
|
}
|
|
|
|
template <typename Container>
|
|
Container reverse(internal::create_new_container_t, const Container& xs)
|
|
{
|
|
static_assert(internal::has_order<Container>::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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut reverse(Container&& xs)
|
|
{
|
|
return internal::reverse(internal::can_reuse_v<Container>{},
|
|
std::forward<Container>(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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut take(std::size_t amount, Container&& xs)
|
|
{
|
|
if (amount >= size_of_cont(xs))
|
|
return xs;
|
|
return get_segment(0, amount, std::forward<Container>(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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut take_exact(std::size_t amount, Container&& xs)
|
|
{
|
|
return get_segment(0, amount, std::forward<Container>(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 <typename Container>
|
|
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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
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<Container>(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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
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<Container>(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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
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<Container>(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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut drop_exact(std::size_t amount, Container&& xs)
|
|
{
|
|
return get_segment(amount, size_of_cont(xs), std::forward<Container>(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 <typename Container, typename UnaryPredicate>
|
|
Container take_while(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container, typename UnaryPredicate>
|
|
Container take_last_while(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container, typename UnaryPredicate>
|
|
Container drop_while(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container, typename UnaryPredicate>
|
|
Container drop_last_while(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename F, typename Container, typename Acc>
|
|
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 F, typename Container>
|
|
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 <typename F, typename Container>
|
|
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 F, typename Container>
|
|
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 <typename F, typename Container, typename Acc>
|
|
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 <typename F, typename Container>
|
|
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 <typename F, typename ContainerIn, typename Acc>
|
|
auto scan_left(F f, const Acc& init, const ContainerIn& xs)
|
|
{
|
|
using ContainerOut =
|
|
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::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 <typename F, typename ContainerIn>
|
|
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<decltype(*beginIt)>,
|
|
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 <typename F, typename ContainerIn,
|
|
typename Acc = typename utils::function_traits<F>::template arg<1>::type,
|
|
typename ContainerOut = typename internal::same_cont_new_t<ContainerIn, Acc, 1>::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 <typename F, typename ContainerIn,
|
|
typename Acc = typename ContainerIn::value_type,
|
|
typename ContainerOut = typename internal::same_cont_new_t<ContainerIn, Acc, 0>::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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
T product(const Container& xs)
|
|
{
|
|
T result{1};
|
|
for (const auto& x : xs)
|
|
{
|
|
result = result * x;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename T, typename Container>
|
|
Container append_elem(internal::reuse_container_t, const T& y, Container&& xs)
|
|
{
|
|
*internal::get_back_inserter(xs) = y;
|
|
return std::forward<Container>(xs);
|
|
}
|
|
|
|
template <typename T, typename Container>
|
|
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 Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>,
|
|
typename T = typename ContainerOut::value_type>
|
|
ContainerOut append_elem(const T& y, Container&& xs)
|
|
{
|
|
return internal::append_elem(internal::can_reuse_v<Container>{},
|
|
y, std::forward<Container>(xs));
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename T>
|
|
std::list<T> prepend_elem(internal::reuse_container_t,
|
|
const T& y, std::list<T>&& xs)
|
|
{
|
|
xs.push_front(y);
|
|
return std::forward<std::list<T>>(xs);
|
|
}
|
|
|
|
template <typename T, typename Container>
|
|
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<Container>(xs);
|
|
}
|
|
|
|
template <typename T, typename Container>
|
|
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 Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>,
|
|
typename T = typename ContainerOut::value_type>
|
|
ContainerOut prepend_elem(const T& y, Container&& xs)
|
|
{
|
|
return internal::prepend_elem(internal::can_reuse_v<Container>{},
|
|
y, std::forward<Container>(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 <typename ContainerIn1, typename ContainerIn2 = ContainerIn1, typename ContainerOut = ContainerIn1>
|
|
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 <typename ContainerOut, typename ContainerIn1, typename ContainerIn2 = ContainerIn1>
|
|
ContainerOut append_convert(const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
return append<ContainerIn1, ContainerIn2, ContainerOut>(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 <typename ContainerIn,
|
|
typename ContainerOut = typename ContainerIn::value_type>
|
|
ContainerOut concat(const ContainerIn& xss)
|
|
{
|
|
std::size_t length = sum(
|
|
transform(size_of_cont<typename ContainerIn::value_type>, 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 <typename Container>
|
|
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<Container>(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 <typename Container>
|
|
std::pair<Container, Container> unweave(const Container& xs)
|
|
{
|
|
std::pair<Container, Container> 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<Container>(result.first);
|
|
auto it_odd = internal::get_back_inserter<Container>(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 <typename Compare, typename T>
|
|
std::list<T> sort_by(internal::reuse_container_t, Compare comp,
|
|
std::list<T>&& xs)
|
|
{
|
|
xs.sort(comp);
|
|
return std::forward<std::list<T>>(xs);
|
|
}
|
|
|
|
template <typename Compare, typename T>
|
|
std::list<T> sort_by(internal::create_new_container_t, Compare comp,
|
|
const std::list<T>& xs)
|
|
{
|
|
auto result = xs;
|
|
result.sort(comp);
|
|
return result;
|
|
}
|
|
|
|
template <typename Compare, typename Container>
|
|
Container sort_by(internal::reuse_container_t, Compare comp, Container&& xs)
|
|
{
|
|
std::sort(std::begin(xs), std::end(xs), comp);
|
|
return std::forward<Container>(xs);
|
|
}
|
|
|
|
template <typename Compare, typename Container>
|
|
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 <typename Compare, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut sort_by(Compare comp, Container&& xs)
|
|
{
|
|
return internal::sort_by(internal::can_reuse_v<Container>{},
|
|
comp, std::forward<Container>(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 <typename F>
|
|
struct is_less_by_struct
|
|
{
|
|
is_less_by_struct(F f) : f_(f) {};
|
|
template <typename T>
|
|
bool operator()(const T& x, const T& y)
|
|
{
|
|
return f_(x) < f_(y);
|
|
}
|
|
private:
|
|
F f_;
|
|
};
|
|
template <typename F>
|
|
struct is_equal_by_struct
|
|
{
|
|
is_equal_by_struct(F f) : f_(f) {};
|
|
template <typename T>
|
|
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 <typename F, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut sort_on(F f, Container&& xs)
|
|
{
|
|
return sort_by(internal::is_less_by_struct<F>(f),
|
|
std::forward<Container>(xs));
|
|
}
|
|
|
|
// API search type: sort : [a] -> [a]
|
|
// fwd bind count: 0
|
|
// Sort a sequence to ascending order using std::less.
|
|
template <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut sort(Container&& xs)
|
|
{
|
|
typedef typename std::remove_reference<Container>::type::value_type T;
|
|
return sort_by(std::less<T>(), std::forward<Container>(xs));
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Compare, typename T>
|
|
std::list<T> stable_sort_by(internal::reuse_container_t, Compare comp,
|
|
std::list<T>&& xs)
|
|
{
|
|
xs.sort(comp); // std::list<T>::sort ist already stable.
|
|
return std::forward<std::list<T>>(xs);
|
|
}
|
|
|
|
template <typename Compare, typename T>
|
|
std::list<T> stable_sort_by(internal::create_new_container_t, Compare comp,
|
|
const std::list<T>& xs)
|
|
{
|
|
auto result = xs;
|
|
result.sort(comp); // std::list<T>::sort ist already stable.
|
|
return result;
|
|
}
|
|
|
|
template <typename Compare, typename Container>
|
|
Container stable_sort_by(internal::reuse_container_t, Compare comp,
|
|
Container&& xs)
|
|
{
|
|
std::sort(std::begin(xs), std::end(xs), comp);
|
|
return std::forward<Container>(xs);
|
|
}
|
|
|
|
template <typename Compare, typename Container>
|
|
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 <typename Compare, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut stable_sort_by(Compare comp, Container&& xs)
|
|
{
|
|
return internal::stable_sort_by(internal::can_reuse_v<Container>{},
|
|
comp, std::forward<Container>(xs));
|
|
}
|
|
|
|
// API search type: stable_sort_on : ((a -> b), [a]) -> [a]
|
|
// fwd bind count: 1
|
|
// Sort a sequence stably by given transformer.
|
|
template <typename F, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut stable_sort_on(F f, Container&& xs)
|
|
{
|
|
return stable_sort_by(internal::is_less_by_struct<F>(f),
|
|
std::forward<Container>(xs));
|
|
}
|
|
|
|
// API search type: stable_sort : [a] -> [a]
|
|
// fwd bind count: 0
|
|
// Sort a sequence stably to ascending order using std::less.
|
|
template <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut stable_sort(Container&& xs)
|
|
{
|
|
typedef typename std::remove_reference<Container>::type::value_type T;
|
|
return stable_sort_by(std::less<T>(), std::forward<Container>(xs));
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Compare, typename Container>
|
|
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<Container>(get_segment(internal::reuse_container_t(),
|
|
0, count, xs));
|
|
}
|
|
|
|
template <typename Compare, typename Container>
|
|
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 <typename Compare, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut partial_sort_by(Compare comp, std::size_t count, Container&& xs)
|
|
{
|
|
return internal::partial_sort_by(internal::can_reuse_v<Container>{},
|
|
comp, count, std::forward<Container>(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 <typename F, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut partial_sort_on(F f, std::size_t count, Container&& xs)
|
|
{
|
|
return partial_sort_by(internal::is_less_by_struct<F>(f), count,
|
|
std::forward<Container>(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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut partial_sort(std::size_t count, Container&& xs)
|
|
{
|
|
typedef typename std::remove_reference<Container>::type::value_type T;
|
|
return partial_sort_by(std::less<T>(), count,
|
|
std::forward<Container>(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 <typename Compare, typename Container,
|
|
typename T = typename Container::value_type>
|
|
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 <typename F, typename Container,
|
|
typename T = typename Container::value_type>
|
|
T nth_element_on(F f, std::size_t n, const Container& xs)
|
|
{
|
|
return nth_element_by(internal::is_less_by_struct<F>(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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
T nth_element(std::size_t n, const Container& xs)
|
|
{
|
|
return nth_element_by(std::less<T>(), n, xs);
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename BinaryPredicate, typename Container>
|
|
Container unique_by(internal::reuse_container_t,
|
|
BinaryPredicate pred, Container&& xs)
|
|
{
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, Container>();
|
|
const auto it_end = std::unique(std::begin(xs), std::end(xs), pred);
|
|
xs.erase(it_end, std::end(xs));
|
|
return std::forward<Container>(xs);
|
|
}
|
|
|
|
template <typename BinaryPredicate, typename Container>
|
|
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 <typename BinaryPredicate, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut unique_by(BinaryPredicate pred, Container&& xs)
|
|
{
|
|
return internal::unique_by(internal::can_reuse_v<Container>{},
|
|
pred, std::forward<Container>(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 <typename F, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut unique_on(F f, Container&& xs)
|
|
{
|
|
return unique_by(internal::is_equal_by_struct<F>(f),
|
|
std::forward<Container>(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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut unique(Container&& xs)
|
|
{
|
|
typedef typename std::remove_reference<Container>::type::value_type T;
|
|
return unique_on(identity<T>, std::forward<Container>(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 <typename Container,
|
|
typename X = typename Container::value_type>
|
|
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<std::size_t>(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 <typename Container,
|
|
typename X = typename Container::value_type>
|
|
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 <typename Container,
|
|
typename Inner = typename Container::value_type,
|
|
typename X = typename Inner::value_type>
|
|
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 <typename UnaryPredicate, typename Container>
|
|
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 <typename Container>
|
|
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 <typename Container, typename BinaryPredicate>
|
|
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 <typename Container, typename F>
|
|
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 <typename Container>
|
|
Container nub(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
return nub_by(std::equal_to<T>(), 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 <typename Container, typename BinaryPredicate>
|
|
bool all_unique_by_eq(BinaryPredicate p, const Container& xs)
|
|
{
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, 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 <typename Container, typename F>
|
|
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 <typename Container>
|
|
bool all_unique(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
auto comp = std::equal_to<T>();
|
|
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 <typename Container, typename Compare>
|
|
bool is_strictly_sorted_by(Compare comp, const Container& xs)
|
|
{
|
|
internal::check_compare_for_container<Compare, 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 <typename Container, typename F>
|
|
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 <typename Container>
|
|
bool is_strictly_sorted(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
auto comp = std::less<T>();
|
|
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 <typename Container, typename Compare>
|
|
bool is_sorted_by(Compare comp, const Container& xs)
|
|
{
|
|
internal::check_compare_for_container<Compare, 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 <typename Container, typename F>
|
|
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 <typename Container>
|
|
bool is_sorted(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
auto comp = std::less<T>();
|
|
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 <typename Container>
|
|
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 <typename Container>
|
|
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 <typename UnaryPredicate, typename Container>
|
|
bool all_by(UnaryPredicate p, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container>
|
|
bool all(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
return all_by(identity<T>, 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 <typename Container, typename BinaryPredicate>
|
|
bool all_the_same_by(BinaryPredicate p, const Container& xs)
|
|
{
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, 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 <typename Container, typename F>
|
|
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 <typename Container>
|
|
bool all_the_same(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
auto binaryPredicate = std::equal_to<T>();
|
|
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 <typename T,
|
|
typename ContainerOut = std::vector<T>>
|
|
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<std::size_t>((end - start) / step);
|
|
internal::prepare_container(result, size);
|
|
auto it = internal::get_back_inserter<ContainerOut>(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 <typename T,
|
|
typename ContainerOut = std::vector<T>>
|
|
ContainerOut numbers(const T start, const T end)
|
|
{
|
|
return numbers_step<T, ContainerOut>(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 <typename T, typename ContainerOut = std::vector<T>>
|
|
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 <typename Container>
|
|
std::vector<std::size_t> all_idxs(const Container& xs)
|
|
{
|
|
return numbers<std::size_t>(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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut init(Container&& xs)
|
|
{
|
|
assert(!is_empty(xs));
|
|
return get_segment(0, size_of_cont(std::forward<Container>(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 <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut tail(Container&& xs)
|
|
{
|
|
assert(!is_empty(xs));
|
|
return get_segment(1, size_of_cont(std::forward<Container>(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>
|
|
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>
|
|
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 <typename Result, typename Container>
|
|
std::pair<Result, Result> mean_stddev(const Container& xs)
|
|
{
|
|
assert(size_of_cont(xs) != 0);
|
|
|
|
// http://stackoverflow.com/a/7616783/1866775
|
|
Result sum = static_cast<Result>(
|
|
internal::accumulate(xs.begin(), xs.end(),
|
|
static_cast<typename Container::value_type>(0)));
|
|
Result mean = sum / static_cast<Result>(xs.size());
|
|
|
|
std::vector<Result> 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<Result>(0));
|
|
Result stddev = std::sqrt(sq_sum / static_cast<Result>(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 <typename F, typename ContainerIn>
|
|
auto count_occurrences_by(F f, const ContainerIn& xs)
|
|
{
|
|
using In = typename ContainerIn::value_type;
|
|
using MapOut =
|
|
std::map<std::decay_t<internal::invoke_result_t<F, In>>, std::size_t>;
|
|
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type>();
|
|
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 <typename ContainerIn,
|
|
typename MapOut = typename std::map<
|
|
typename ContainerIn::value_type, std::size_t>>
|
|
MapOut count_occurrences(const ContainerIn& xs)
|
|
{
|
|
return count_occurrences_by(identity<typename ContainerIn::value_type>, 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 <typename Container, typename BinaryPredicate>
|
|
bool lexicographical_less_by(BinaryPredicate p,
|
|
const Container& xs, const Container& ys)
|
|
{
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, 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 <typename Container>
|
|
bool lexicographical_less(const Container& xs, const Container& ys)
|
|
{
|
|
return lexicographical_less_by(
|
|
is_less<typename Container::value_type>, xs, ys);
|
|
}
|
|
|
|
// API search type: lexicographical_sort : [[a]] -> [[a]]
|
|
// fwd bind count: 0
|
|
// sort by lexicographical_less
|
|
template <typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut lexicographical_sort(Container&& xs)
|
|
{
|
|
typedef typename std::remove_reference<Container>::type::value_type T;
|
|
return sort_by(lexicographical_less<T>, std::forward<Container>(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 <typename T,
|
|
typename ContainerOut = std::vector<T>>
|
|
ContainerOut replicate(std::size_t n, const T& x)
|
|
{
|
|
return ContainerOut(n, x);
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename UnaryPredicate, typename T>
|
|
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<T>(x);
|
|
}
|
|
|
|
template <typename UnaryPredicate, typename T>
|
|
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 <typename UnaryPredicate, typename T, typename TAlt>
|
|
auto instead_of_if(UnaryPredicate pred, const TAlt& alt, T&& x)
|
|
{
|
|
return internal::instead_of_if(internal::can_reuse_v<T>{},
|
|
pred, alt, std::forward<T>(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 <typename Container, typename ContainerAlt,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut instead_of_if_empty(const ContainerAlt& alt, Container&& xs)
|
|
{
|
|
return instead_of_if(
|
|
is_empty<internal::remove_const_and_ref_t<Container>>,
|
|
alt, std::forward<Container>(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 <cassert>
|
|
#include <functional>
|
|
#include <memory>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
template <typename Ok, typename Error>
|
|
class result;
|
|
|
|
template <typename Ok, typename Error>
|
|
result<Ok, Error> ok(const Ok& val);
|
|
|
|
template <typename Ok, typename Error>
|
|
result<Ok, Error> error(const Error& error);
|
|
|
|
// Can hold a value of type Ok or an error of type Error.
|
|
template <typename Ok, typename Error>
|
|
class result
|
|
{
|
|
public:
|
|
bool is_ok() const { return static_cast<bool>(ptr_ok_); }
|
|
bool is_error() const { return static_cast<bool>(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<Ok, Error>& 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<Ok, Error>& operator = (const result<Ok, Error>& 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<Ok> ptr_ok;
|
|
typedef std::unique_ptr<Error> ptr_error;
|
|
friend result<Ok, Error> ok<Ok, Error>(const Ok& ok);
|
|
friend result<Ok, Error> error<Ok, 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 <typename Ok, typename Error>
|
|
bool is_ok(const result<Ok, Error>& result)
|
|
{
|
|
return result.is_ok();
|
|
}
|
|
|
|
// API search type: is_error : Result a b -> Bool
|
|
// fwd bind count: 0
|
|
// Is not OK?
|
|
template <typename Ok, typename Error>
|
|
bool is_error(const result<Ok, Error>& 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 <typename Ok, typename Error>
|
|
Ok unsafe_get_ok(const result<Ok, Error>& 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 <typename Ok, typename Error>
|
|
Error unsafe_get_error(const result<Ok, Error>& 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 <typename Ok, typename Error>
|
|
Ok ok_with_default(const Ok& defaultValue, const result<Ok, Error>& 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 <typename Ok, typename Error>
|
|
result<Ok, Error> ok(const Ok& val)
|
|
{
|
|
result<Ok, Error> 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 <typename Ok, typename Error>
|
|
result<Ok, Error> error(const Error& error)
|
|
{
|
|
result<Ok, Error> 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 <typename Ok, typename Error>
|
|
maybe<Ok> to_maybe(const result<Ok, Error>& result)
|
|
{
|
|
if (is_ok(result))
|
|
return just<Ok>(unsafe_get_ok(result));
|
|
else
|
|
return nothing<Ok>();
|
|
}
|
|
|
|
// API search type: from_maybe : (b, Maybe a) -> Result a b
|
|
// fwd bind count: 1
|
|
// Convert just to ok, nothing to error.
|
|
template <typename Error, typename Ok>
|
|
result<Ok, Error> from_maybe(const Error& err, const maybe<Ok>& maybe)
|
|
{
|
|
if (is_just(maybe))
|
|
return ok<Ok, Error>(unsafe_get_just(maybe));
|
|
else
|
|
return error<Ok, 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 <typename E, typename Ok, typename Error>
|
|
Ok throw_on_error(const E& e, const result<Ok, Error>& 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 <typename E, typename Ok, typename Error>
|
|
Ok throw_type_on_error(const result<Ok, Error>& 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 <typename Ok, typename Error>
|
|
bool operator == (const result<Ok, Error>& x, const result<Ok, Error>& 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 <typename Ok, typename Error>
|
|
bool operator != (const result<Ok, Error>& x, const result<Ok, Error>& 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<int, Err> into a result<string, Err>.
|
|
// An error stays the same error, regardless of the conversion.
|
|
template <typename Error, typename F, typename A>
|
|
auto lift_result(F f, const result<A, Error>& r)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, A>();
|
|
|
|
using B = std::decay_t<internal::invoke_result_t<F, A>>;
|
|
|
|
if (is_ok(r))
|
|
return ok<B, Error>(internal::invoke(f, unsafe_get_ok(r)));
|
|
return error<B, 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 <typename F, typename G, typename A, typename B>
|
|
auto lift_result_both(F f, G g, const result<A, B>& r)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, A>();
|
|
internal::trigger_static_asserts<internal::unary_function_tag, G, B>();
|
|
|
|
using C = std::decay_t<internal::invoke_result_t<F, A>>;
|
|
using D = std::decay_t<internal::invoke_result_t<G, B>>;
|
|
|
|
if (is_ok(r))
|
|
return ok<C, D>(internal::invoke(f, unsafe_get_ok(r)));
|
|
return error<C, D>(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 <typename F, typename G, typename A, typename B>
|
|
auto unify_result(F f, G g, const result<A, B>& r)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, A>();
|
|
internal::trigger_static_asserts<internal::unary_function_tag, G, B>();
|
|
static_assert(std::is_same<internal::invoke_result_t<F, A>,
|
|
internal::invoke_result_t<G, B>>::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 <typename OK, typename Error>
|
|
result<OK, Error> join_result(const result<result<OK, Error>, Error>& r)
|
|
{
|
|
if (is_ok(r))
|
|
return unsafe_get_ok(r);
|
|
else
|
|
return error<OK, 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 <typename Ok, typename Error, typename F>
|
|
auto and_then_result(F f, const result<Ok, Error>& r)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, Ok>();
|
|
|
|
using FOut = std::decay_t<internal::invoke_result_t<F, Ok>>;
|
|
static_assert(std::is_same<Error, typename FOut::error_t>::value,
|
|
"Error type must stay the same.");
|
|
if (is_ok(r))
|
|
return internal::invoke(f, unsafe_get_ok(r));
|
|
else
|
|
return error<typename FOut::ok_t, typename FOut::error_t>(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 <typename... Callables>
|
|
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<internal::check_arity_tag,
|
|
decltype(f),
|
|
decltype(args)...>();
|
|
#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<decltype(f), decltype(args)...>> {};
|
|
#else
|
|
using FOut = std::decay_t<
|
|
internal::invoke_result_t<decltype(f), decltype(args)...>>;
|
|
#endif
|
|
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
decltype(g),
|
|
typename FOut::ok_t>();
|
|
#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<decltype(g), typename FOut::ok_t>> {};
|
|
#else
|
|
using GOut = std::decay_t<
|
|
internal::invoke_result_t<decltype(g), typename FOut::ok_t>>;
|
|
#endif
|
|
static_assert(std::is_same<typename FOut::error_t,
|
|
typename GOut::error_t>::value,
|
|
"Error type must stay the same.");
|
|
|
|
auto resultB =
|
|
internal::invoke(f, std::forward<decltype(args)>(args)...);
|
|
if (is_ok(resultB))
|
|
return internal::invoke(g, unsafe_get_ok(resultB));
|
|
return error<typename GOut::ok_t, typename GOut::error_t>(
|
|
unsafe_get_error(resultB));
|
|
};
|
|
};
|
|
return internal::compose_binary_lift(bind_result,
|
|
std::forward<Callables>(callables)...);
|
|
}
|
|
} // namespace fplus
|
|
|
|
#include <algorithm>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Pred, typename Container>
|
|
Container keep_if(internal::reuse_container_t, Pred pred, Container&& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<Pred, Container>();
|
|
xs.erase(std::remove_if(
|
|
std::begin(xs), std::end(xs), logical_not(pred)), std::end(xs));
|
|
return std::forward<Container>(xs);
|
|
}
|
|
|
|
template <typename Pred, typename Container>
|
|
Container keep_if(internal::create_new_container_t, Pred pred,
|
|
const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<Pred, Container>();
|
|
Container result;
|
|
auto it = internal::get_back_inserter<Container>(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 <typename Pred, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut keep_if(Pred pred, Container&& xs)
|
|
{
|
|
return internal::keep_if(internal::can_reuse_v<Container>{},
|
|
pred, std::forward<Container>(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 <typename Pred, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut drop_if(Pred pred, Container&& xs)
|
|
{
|
|
return keep_if(logical_not(pred), std::forward<Container>(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 <typename Container,
|
|
typename T = typename Container::value_type,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut without(T elem, Container&& xs)
|
|
{
|
|
return drop_if(is_equal_to(elem), std::forward<Container>(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 <typename Container, typename ContainerElems,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut without_any(const ContainerElems& elems, Container&& xs)
|
|
{
|
|
static_assert(std::is_same<
|
|
typename ContainerElems::value_type,
|
|
typename std::remove_reference<Container>::type::value_type>::value,
|
|
"Container values must be of the same type.");
|
|
const auto pred = bind_2nd_of_2(is_elem_of<ContainerElems>, elems);
|
|
return drop_if(pred, std::forward<Container>(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 <typename Pred, typename Container>
|
|
Container keep_if_with_idx(Pred pred, const Container& xs)
|
|
{
|
|
internal::check_index_with_type_predicate_for_container<Pred, Container>();
|
|
Container ys;
|
|
auto it = internal::get_back_inserter<Container>(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 <typename Pred, typename Container>
|
|
Container drop_if_with_idx(Pred pred, const Container& xs)
|
|
{
|
|
internal::check_index_with_type_predicate_for_container<Pred, 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 <typename UnaryPredicate, typename Container>
|
|
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<Container>(xs);
|
|
}
|
|
|
|
template <typename UnaryPredicate, typename Container>
|
|
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 <typename UnaryPredicate, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut keep_by_idx(UnaryPredicate pred, Container&& xs)
|
|
{
|
|
internal::check_unary_predicate_for_type<UnaryPredicate, std::size_t>();
|
|
return internal::keep_by_idx(internal::can_reuse_v<Container>{},
|
|
pred, std::forward<Container>(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 <typename UnaryPredicate, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut drop_by_idx(UnaryPredicate pred, Container&& xs)
|
|
{
|
|
internal::check_unary_predicate_for_type<UnaryPredicate, std::size_t>();
|
|
return keep_by_idx(logical_not(pred), std::forward<Container>(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 <typename ContainerIdxs, typename Container>
|
|
Container keep_idxs(const ContainerIdxs& idxs_to_keep, const Container& xs)
|
|
{
|
|
static_assert(std::is_same<typename ContainerIdxs::value_type, std::size_t>::value,
|
|
"Indices must be std::size_t");
|
|
auto idxs_left = convert_container<std::list<std::size_t>>(
|
|
unique(sort(idxs_to_keep)));
|
|
Container ys;
|
|
auto it = internal::get_back_inserter<Container>(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 <typename ContainerIdxs, typename Container>
|
|
Container drop_idxs(const ContainerIdxs& idxs_to_drop, const Container& xs)
|
|
{
|
|
static_assert(std::is_same<typename ContainerIdxs::value_type, std::size_t>::value,
|
|
"Indices must be std::size_t");
|
|
auto idxs_left = convert_container<std::list<std::size_t>>(
|
|
unique(sort(idxs_to_drop)));
|
|
Container ys;
|
|
auto it = internal::get_back_inserter<Container>(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 <typename Container>
|
|
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<T> the nothings are dropped
|
|
// and the values inside the justs are returned in a new container.
|
|
template <typename ContainerIn,
|
|
typename ContainerOut =
|
|
typename internal::same_cont_new_t<ContainerIn,
|
|
typename ContainerIn::value_type::type>::type>
|
|
ContainerOut justs(const ContainerIn& xs)
|
|
{
|
|
typedef typename ContainerIn::value_type::type T;
|
|
auto justsInMaybes = keep_if(is_just<T>, xs);
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, fplus::size_of_cont(justsInMaybes));
|
|
auto itOut = internal::get_back_inserter<ContainerOut>(ys);
|
|
std::transform(std::begin(justsInMaybes), std::end(justsInMaybes),
|
|
itOut, unsafe_get_just<T>);
|
|
return ys;
|
|
}
|
|
|
|
// API search type: oks : [Result a b] -> [a]
|
|
// fwd bind count: 0
|
|
// From a Container filled with Result<Ok, Error> the errors are dropped
|
|
// and the values inside the ok are returned in a new container.
|
|
template <typename ContainerIn,
|
|
typename ContainerOut =
|
|
typename internal::same_cont_new_t<ContainerIn,
|
|
typename ContainerIn::value_type::ok_t>::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<Ok, Error>, xs);
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, fplus::size_of_cont(oksInResults));
|
|
auto itOut = internal::get_back_inserter<ContainerOut>(ys);
|
|
std::transform(std::begin(oksInResults), std::end(oksInResults),
|
|
itOut, unsafe_get_ok<Ok, Error>);
|
|
return ys;
|
|
}
|
|
|
|
// API search type: errors : [Result a b] -> [b]
|
|
// fwd bind count: 0
|
|
// From a Container filled with Result<Ok, Error> the oks are dropped
|
|
// and the values inside the errors are returned in a new container.
|
|
template <typename ContainerIn,
|
|
typename ContainerOut =
|
|
typename internal::same_cont_new_t<ContainerIn,
|
|
typename ContainerIn::value_type::error_t>::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<Ok, Error>, xs);
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, fplus::size_of_cont(errorsInResults));
|
|
auto itOut = internal::get_back_inserter<ContainerOut>(ys);
|
|
std::transform(std::begin(errorsInResults), std::end(errorsInResults),
|
|
itOut, unsafe_get_error<Ok, 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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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 <typename Container>
|
|
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 <typename Container, typename UnaryPredicate>
|
|
Container trim_right_by(UnaryPredicate p, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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 <typename Container>
|
|
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 <typename Container, typename UnaryPredicate>
|
|
Container trim_by(UnaryPredicate p, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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 <typename Container>
|
|
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 <typename BinaryPredicate, typename Container>
|
|
Container adjacent_keep_snd_if(BinaryPredicate p, const Container& xs)
|
|
{
|
|
if (is_empty(xs))
|
|
{
|
|
return {};
|
|
}
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, Container>();
|
|
Container result;
|
|
auto it = internal::get_back_inserter<Container>(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 <typename BinaryPredicate, typename Container>
|
|
Container adjacent_drop_fst_if(BinaryPredicate p, const Container& xs)
|
|
{
|
|
if (is_empty(xs))
|
|
{
|
|
return {};
|
|
}
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, Container>();
|
|
Container result;
|
|
auto it = internal::get_back_inserter<Container>(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 <typename BinaryPredicate, typename Container>
|
|
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 <typename BinaryPredicate, typename Container>
|
|
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 <typename ContainerOut, typename F>
|
|
ContainerOut generate(F f, std::size_t amount)
|
|
{
|
|
internal::trigger_static_asserts<internal::nullary_function_tag, F>();
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, amount);
|
|
auto it = internal::get_back_inserter<ContainerOut>(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 <typename ContainerOut, typename F>
|
|
ContainerOut generate_by_idx(F f, std::size_t amount)
|
|
{
|
|
internal::
|
|
trigger_static_asserts<internal::unary_function_tag, F, std::size_t>();
|
|
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, amount);
|
|
auto it = internal::get_back_inserter<ContainerOut>(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 <typename Container>
|
|
Container repeat(std::size_t n, const Container& xs)
|
|
{
|
|
std::vector<Container> 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 <typename ContainerIn,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut infixes(std::size_t length, const ContainerIn& xs)
|
|
{
|
|
assert(length > 0);
|
|
static_assert(std::is_convertible<ContainerIn,
|
|
typename ContainerOut::value_type>::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 <typename F, typename Pred, typename Container1, typename Container2>
|
|
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<F, X, Y>;
|
|
using ContainerOut = std::vector<std::decay_t<FOut>>;
|
|
|
|
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 <typename F, typename Container1, typename Container2>
|
|
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 <typename Pred, typename Container1, typename Container2>
|
|
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 <typename Container1, typename Container2>
|
|
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 <typename T>
|
|
std::vector<std::vector<T>> helper_carthesian_product_n_idxs
|
|
(std::size_t power, const std::vector<T>& xs)
|
|
{
|
|
static_assert(std::is_same<T, std::size_t>::value,
|
|
"T must be std::size_t");
|
|
typedef std::vector<T> Vec;
|
|
typedef std::vector<Vec> 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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut carthesian_product_n(std::size_t power, const ContainerIn& xs_in)
|
|
{
|
|
if (power == 0)
|
|
return ContainerOut(1);
|
|
std::vector<T> xs = convert_container<std::vector<T>>(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<std::size_t>& indices)
|
|
{
|
|
return convert_container_and_elems<ContainerOutInner>(
|
|
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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut permutations(std::size_t power, const ContainerIn& xs_in)
|
|
{
|
|
if (power == 0)
|
|
return ContainerOut(1);
|
|
std::vector<T> xs = convert_container<std::vector<T>>(xs_in);
|
|
auto idxs = all_idxs(xs);
|
|
typedef std::vector<std::size_t> idx_vec;
|
|
auto result_idxss = keep_if(all_unique<idx_vec>,
|
|
internal::helper_carthesian_product_n_idxs(power, idxs));
|
|
typedef typename ContainerOut::value_type ContainerOutInner;
|
|
auto to_result_cont = [&](const std::vector<std::size_t>& indices)
|
|
{
|
|
return convert_container_and_elems<ContainerOutInner>(
|
|
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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut combinations(std::size_t power, const ContainerIn& xs_in)
|
|
{
|
|
if (power == 0)
|
|
return ContainerOut(1);
|
|
std::vector<T> xs = convert_container<std::vector<T>>(xs_in);
|
|
auto idxs = all_idxs(xs);
|
|
typedef std::vector<std::size_t> idx_vec;
|
|
auto result_idxss = keep_if(is_strictly_sorted<idx_vec>,
|
|
internal::helper_carthesian_product_n_idxs(power, idxs));
|
|
typedef typename ContainerOut::value_type ContainerOutInner;
|
|
auto to_result_cont = [&](const std::vector<std::size_t>& indices)
|
|
{
|
|
return convert_container_and_elems<ContainerOutInner>(
|
|
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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut combinations_with_replacement(std::size_t power,
|
|
const ContainerIn& xs_in)
|
|
{
|
|
if (power == 0)
|
|
return ContainerOut(1);
|
|
std::vector<T> xs = convert_container<std::vector<T>>(xs_in);
|
|
auto idxs = all_idxs(xs);
|
|
typedef std::vector<std::size_t> idx_vec;
|
|
auto result_idxss = keep_if(is_sorted<idx_vec>,
|
|
internal::helper_carthesian_product_n_idxs(power, idxs));
|
|
typedef typename ContainerOut::value_type ContainerOutInner;
|
|
auto to_result_cont = [&](const std::vector<std::size_t>& indices)
|
|
{
|
|
return convert_container_and_elems<ContainerOutInner>(
|
|
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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut power_set(const ContainerIn& xs_in)
|
|
{
|
|
return concat(
|
|
generate_by_idx<std::vector<ContainerOut>>(
|
|
bind_1st_of_2(
|
|
flip(combinations<ContainerIn, T, ContainerOut>),
|
|
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 <typename F,
|
|
typename T,
|
|
typename ContainerOut = std::vector<T>>
|
|
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 <typename F,
|
|
typename T,
|
|
typename ContainerOut = std::vector<T>>
|
|
ContainerOut iterate_maybe(F f, const T& x)
|
|
{
|
|
ContainerOut result;
|
|
auto it_out = internal::get_back_inserter(result);
|
|
maybe<T> 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 <typename ContainerIn, typename F>
|
|
auto adjacent_difference_by(F f, const ContainerIn& xs)
|
|
{
|
|
using X = typename ContainerIn::value_type;
|
|
using TOut = internal::invoke_result_t<F, X, X>;
|
|
using ContainerOut = std::vector<std::decay_t<TOut>>;
|
|
|
|
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 <typename Container>
|
|
Container adjacent_difference(const Container& xs)
|
|
{
|
|
return adjacent_difference_by(
|
|
std::minus<typename Container::value_type>(), 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 <typename Container>
|
|
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 <typename Container>
|
|
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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut rotations_left(const ContainerIn& xs_in)
|
|
{
|
|
return iterate(rotate_left<ContainerIn>, 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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut rotations_right(const ContainerIn& xs_in)
|
|
{
|
|
return iterate(rotate_right<ContainerIn>, 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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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<T, Container>(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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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<T, Container>(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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
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 <typename F, typename X, typename Y>
|
|
struct function_traits_asserts<apply_to_pair_tag, F, X, Y>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 2,
|
|
"Function must take two parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function does not take pair.first type as first Parameter.");
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Function does not take pair.second type as second Parameter.");
|
|
};
|
|
|
|
template <typename F, typename X, typename Y>
|
|
struct function_traits_asserts<zip_with_tag, F, X, Y>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 2,
|
|
"Function must take two parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function does not take elements from first Container as first Parameter.");
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Function does not take elements from second Container as second Parameter.");
|
|
};
|
|
|
|
template <typename F, typename X, typename Y, typename Z>
|
|
struct function_traits_asserts<zip_with_3_tag, F, X, Y, Z>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 3,
|
|
"Function must take two parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
typedef typename utils::function_traits<F>::template arg<2>::type FIn2;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function does not take elements from first Container as first Parameter.");
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Function does not take elements from second Container as second Parameter.");
|
|
static_assert(std::is_convertible<Z, FIn2>::value,
|
|
"Function does not take elements from third Container as third Parameter.");
|
|
};
|
|
|
|
template <typename F, typename X>
|
|
struct function_traits_asserts<transform_fst_tag, F, X>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 1,
|
|
"Function must take one parameter.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function does not take pair.first type as first Parameter.");
|
|
};
|
|
|
|
template <typename F, typename X>
|
|
struct function_traits_asserts<transform_snd_tag, F, X>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 1,
|
|
"Function must take one parameter.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function does not take pair.second type as first Parameter.");
|
|
};
|
|
|
|
template <typename F, typename X, typename Y>
|
|
struct function_traits_asserts<inner_product_with_tag, F, X, Y>
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 2,
|
|
"Function must take two parameters.");
|
|
typedef typename utils::function_traits<F>::template arg<0>::type FIn0;
|
|
typedef typename utils::function_traits<F>::template arg<1>::type FIn1;
|
|
static_assert(std::is_convertible<X, FIn0>::value,
|
|
"Function does not take elements from first Container as first Parameter.");
|
|
static_assert(std::is_convertible<Y, FIn1>::value,
|
|
"Function does not take elements from second Container as second Parameter.");
|
|
};
|
|
}
|
|
}
|
|
|
|
#include <utility>
|
|
|
|
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 <typename F, typename FIn0, typename FIn1>
|
|
auto apply_to_pair(F f, const std::pair<FIn0, FIn1>& p)
|
|
{
|
|
internal::trigger_static_asserts<internal::apply_to_pair_tag, F, FIn0, FIn1>();
|
|
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 ContainerIn1,
|
|
typename ContainerIn2,
|
|
typename F,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type,
|
|
typename TOut = std::decay_t<internal::invoke_result_t<F, X, Y>>,
|
|
typename ContainerOut = std::vector<TOut>>
|
|
ContainerOut zip_with(F f, const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
internal::trigger_static_asserts<internal::zip_with_tag, F, X, Y>();
|
|
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<internal::invoke_result_t<F, X, Y, Z>>,
|
|
typename ContainerOut = typename std::vector<TOut>>
|
|
ContainerOut zip_with_3(F f,
|
|
const ContainerIn1& xs,
|
|
const ContainerIn2& ys,
|
|
const ContainerIn3& zs)
|
|
{
|
|
internal::trigger_static_asserts<internal::zip_with_3_tag, F, X, Y, Z>();
|
|
static_assert(std::is_same<
|
|
typename internal::same_cont_new_t<ContainerIn1, void>::type,
|
|
typename internal::same_cont_new_t<ContainerIn2, void>::type>::value,
|
|
"All three Containers must be of same outer type.");
|
|
static_assert(std::is_same<
|
|
typename internal::same_cont_new_t<ContainerIn2, void>::type,
|
|
typename internal::same_cont_new_t<ContainerIn3, void>::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<internal::zip_with_tag, F, X, Y>();
|
|
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<X, ContainerIn1>(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<Y, ContainerIn2>(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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type>
|
|
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 <typename ContainerIn1, typename ContainerIn2>
|
|
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 <typename ContainerIn,
|
|
typename TIn = typename ContainerIn::value_type,
|
|
typename X = typename TIn::first_type,
|
|
typename Y = typename TIn::second_type,
|
|
typename ContainerOutX = typename internal::same_cont_new_t<ContainerIn, X>::type,
|
|
typename ContainerOutY = typename internal::same_cont_new_t<ContainerIn, Y>::type>
|
|
std::pair<ContainerOutX, ContainerOutY> 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 <typename X, typename Y>
|
|
X fst(const std::pair<X, Y>& 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 <typename X, typename Y>
|
|
Y snd(const std::pair<X, Y>& 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 <typename X, typename Y, typename F,
|
|
typename ResultFirst = std::decay_t<internal::invoke_result_t<F, X>>>
|
|
std::pair<ResultFirst, Y> transform_fst(F f, const std::pair<X, Y>& pair)
|
|
{
|
|
internal::trigger_static_asserts<internal::transform_fst_tag, F, X>();
|
|
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 <typename X, typename Y, typename F,
|
|
typename ResultSecond = std::decay_t<internal::invoke_result_t<F, Y>>>
|
|
std::pair<X, ResultSecond> transform_snd(F f, const std::pair<X, Y>& pair)
|
|
{
|
|
internal::trigger_static_asserts<internal::transform_snd_tag, F, Y>();
|
|
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<internal::invoke_result_t<F, X>>,
|
|
typename ResultSecond = std::decay_t<internal::invoke_result_t<G, Y>>>
|
|
std::pair<ResultFirst, ResultSecond> transform_pair(F f,
|
|
G g,
|
|
const std::pair<X, Y>& pair)
|
|
{
|
|
internal::trigger_static_asserts<internal::transform_fst_tag, F, X>();
|
|
internal::trigger_static_asserts<internal::transform_snd_tag, G, Y>();
|
|
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 <typename X, typename Y>
|
|
std::pair<Y, X> swap_pair_elems(const std::pair<X, Y>& 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 <typename ContainerIn,
|
|
typename X = typename ContainerIn::value_type::first_type,
|
|
typename Y = typename ContainerIn::value_type::second_type>
|
|
auto swap_pairs_elems(const ContainerIn& xs)
|
|
{
|
|
return fplus::transform(swap_pair_elems<X, Y>, 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 <typename Container,
|
|
typename ContainerOut =
|
|
typename internal::same_cont_new_t<Container,
|
|
std::pair<
|
|
typename Container::value_type,
|
|
typename Container::value_type>>::type>
|
|
ContainerOut adjacent_pairs(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
static_assert(std::is_convertible<
|
|
std::pair<T, T>,
|
|
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 <typename Container,
|
|
typename ContainerOut =
|
|
typename internal::same_cont_new_t<Container,
|
|
std::pair<
|
|
typename Container::value_type,
|
|
typename Container::value_type>, -1>::type>
|
|
ContainerOut overlapping_pairs(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
static_assert(std::is_convertible<
|
|
std::pair<T, T>,
|
|
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 <typename Container,
|
|
typename ContainerOut =
|
|
typename internal::same_cont_new_t<Container,
|
|
std::pair<
|
|
typename Container::value_type,
|
|
typename Container::value_type>, 0>::type>
|
|
ContainerOut overlapping_pairs_cyclic(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
static_assert(std::is_convertible<
|
|
std::pair<T, T>,
|
|
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 <typename Container>
|
|
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<OP2, X, Y>>
|
|
auto inner_product_with(OP1 op1,
|
|
OP2 op2,
|
|
const Acc& value,
|
|
const ContainerIn1& xs,
|
|
const ContainerIn2& ys)
|
|
{
|
|
internal::trigger_static_asserts<internal::inner_product_with_tag, OP2, X, Y>();
|
|
internal::trigger_static_asserts<internal::inner_product_with_tag, OP1, Acc, OP2Out>();
|
|
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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename Z>
|
|
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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename BinaryPredicate>
|
|
maybe<std::size_t> 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<std::size_t>();
|
|
}
|
|
|
|
// 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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename BinaryPredicate,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type,
|
|
typename TOut = std::pair<X, Y>>
|
|
maybe<TOut> 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<TOut>();
|
|
}
|
|
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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename F,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type,
|
|
typename TOut = std::pair<X, Y>>
|
|
maybe<std::size_t> first_mismatch_idx_on(F f,
|
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
static_assert(std::is_same<X, Y>::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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename F,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type,
|
|
typename TOut = std::pair<X, Y>>
|
|
maybe<TOut> first_mismatch_on(F f,
|
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
static_assert(std::is_same<X, Y>::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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type>
|
|
maybe<std::size_t> first_mismatch_idx(
|
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
static_assert(std::is_same<X, Y>::value,
|
|
"Both containers must have the same element type.");
|
|
return first_mismatch_idx_by(std::equal_to<X>(), 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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type,
|
|
typename TOut = std::pair<X, Y>>
|
|
maybe<TOut> first_mismatch(const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
static_assert(std::is_same<X, Y>::value,
|
|
"Both containers must have the same element type.");
|
|
return first_mismatch_by(std::equal_to<X>(), 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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename F,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type>
|
|
maybe<std::size_t> 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<std::size_t>();
|
|
}
|
|
|
|
// 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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename F,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type,
|
|
typename TOut = std::pair<X, Y>>
|
|
maybe<TOut> 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<TOut>();
|
|
}
|
|
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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename F,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type>
|
|
maybe<std::size_t> first_match_idx_on(F f,
|
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
static_assert(std::is_same<X, Y>::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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename F,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type,
|
|
typename TOut = std::pair<X, Y>>
|
|
maybe<TOut> first_match_on(F f, const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
static_assert(std::is_same<X, Y>::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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type>
|
|
maybe<std::size_t> first_match_idx(
|
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
static_assert(std::is_same<X, Y>::value,
|
|
"Both containers must have the same element type.");
|
|
return first_match_idx_by(std::equal_to<X>(), 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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename X = typename ContainerIn1::value_type,
|
|
typename Y = typename ContainerIn2::value_type,
|
|
typename TOut = std::pair<X, Y>>
|
|
maybe<TOut> first_match(const ContainerIn1& xs, const ContainerIn2& ys)
|
|
{
|
|
static_assert(std::is_same<X, Y>::value,
|
|
"Both containers must have the same element type.");
|
|
return first_match_by(std::equal_to<X>(), xs, ys);
|
|
}
|
|
|
|
} // namespace fplus
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <functional>
|
|
#include <limits>
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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 <typename X>
|
|
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 <typename X>
|
|
bool is_positive(X x)
|
|
{
|
|
return !is_negative(x);
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
template <typename X>
|
|
typename std::enable_if<std::is_unsigned<X>::value, X>::type
|
|
abs_helper(X x)
|
|
{
|
|
return x;
|
|
}
|
|
|
|
template <typename X>
|
|
typename std::enable_if<!std::is_unsigned<X>::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 <typename X>
|
|
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 <typename X>
|
|
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 <typename X>
|
|
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 <typename X>
|
|
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 <typename X>
|
|
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 <typename X>
|
|
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 <typename Out, typename X>
|
|
Out integral_cast_throw(X x)
|
|
{
|
|
#ifdef _MSC_VER
|
|
__pragma(warning(push))
|
|
__pragma(warning(disable:4127))
|
|
#endif
|
|
static_assert(std::is_integral<X>::value, "type must be integral");
|
|
static_assert(std::is_integral<Out>::value, "type must be integral");
|
|
if (std::is_signed<X>::value && std::is_signed<Out>::value)
|
|
{
|
|
if (static_cast<std::int64_t>(x) <
|
|
static_cast<std::int64_t>(std::numeric_limits<Out>::lowest()))
|
|
{
|
|
throw std::underflow_error("");
|
|
}
|
|
if (static_cast<std::int64_t>(x) >
|
|
static_cast<std::int64_t>(std::numeric_limits<Out>::max()))
|
|
{
|
|
throw std::overflow_error("");
|
|
}
|
|
return static_cast<Out>(x);
|
|
}
|
|
else if (!std::is_signed<X>::value && !std::is_signed<Out>::value)
|
|
{
|
|
if (static_cast<std::uint64_t>(x) <
|
|
static_cast<std::uint64_t>(std::numeric_limits<Out>::lowest()))
|
|
{
|
|
throw std::underflow_error("");
|
|
}
|
|
if (static_cast<std::uint64_t>(x) >
|
|
static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
|
|
{
|
|
throw std::overflow_error("");
|
|
}
|
|
return static_cast<Out>(x);
|
|
}
|
|
else if (std::is_signed<X>::value && !std::is_signed<Out>::value)
|
|
{
|
|
if (x < 0)
|
|
return 0;
|
|
if (static_cast<std::uint64_t>(x) >
|
|
static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
|
|
{
|
|
throw std::overflow_error("");
|
|
}
|
|
return static_cast<Out>(x);
|
|
}
|
|
else if (!std::is_signed<X>::value && std::is_signed<Out>::value)
|
|
{
|
|
if (static_cast<std::uint64_t>(x) >
|
|
static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
|
|
{
|
|
throw std::overflow_error("");
|
|
}
|
|
return static_cast<Out>(x);
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
return static_cast<Out>(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 <typename Out, typename X>
|
|
Out integral_cast_clamp(X x)
|
|
{
|
|
static_assert(std::is_integral<X>::value, "type must be integral");
|
|
static_assert(std::is_integral<Out>::value, "type must be integral");
|
|
if (std::is_signed<X>::value && std::is_signed<Out>::value)
|
|
{
|
|
if (static_cast<std::int64_t>(x) <
|
|
static_cast<std::int64_t>(std::numeric_limits<Out>::lowest()))
|
|
{
|
|
return std::numeric_limits<Out>::lowest();
|
|
}
|
|
if (static_cast<std::int64_t>(x) >
|
|
static_cast<std::int64_t>(std::numeric_limits<Out>::max()))
|
|
{
|
|
return std::numeric_limits<Out>::max();
|
|
}
|
|
return static_cast<Out>(x);
|
|
}
|
|
else if (!std::is_signed<X>::value && !std::is_signed<Out>::value)
|
|
{
|
|
if (static_cast<std::uint64_t>(x) <
|
|
static_cast<std::uint64_t>(std::numeric_limits<Out>::lowest()))
|
|
{
|
|
return std::numeric_limits<Out>::lowest();
|
|
}
|
|
if (static_cast<std::uint64_t>(x) >
|
|
static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
|
|
{
|
|
return std::numeric_limits<Out>::max();
|
|
}
|
|
return static_cast<Out>(x);
|
|
}
|
|
else if (std::is_signed<X>::value && !std::is_signed<Out>::value)
|
|
{
|
|
if (x < 0)
|
|
return 0;
|
|
if (static_cast<std::uint64_t>(x) >
|
|
static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
|
|
{
|
|
return std::numeric_limits<Out>::max();
|
|
}
|
|
return static_cast<Out>(x);
|
|
}
|
|
else if (!std::is_signed<X>::value && std::is_signed<Out>::value)
|
|
{
|
|
if (static_cast<std::uint64_t>(x) >
|
|
static_cast<std::uint64_t>(std::numeric_limits<Out>::max()))
|
|
{
|
|
return std::numeric_limits<Out>::max();
|
|
}
|
|
return static_cast<Out>(x);
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
return static_cast<Out>(x);
|
|
}
|
|
}
|
|
|
|
// API search type: round : a -> Int
|
|
// fwd bind count: 0
|
|
// Converts a value to the nearest integer.
|
|
template <typename X, typename Out = int>
|
|
Out round(X x)
|
|
{
|
|
static_assert(!std::is_integral<X>::value, "type must be non-integral");
|
|
static_assert(std::is_integral<Out>::value, "type must be integral");
|
|
if (static_cast<double>(x) < static_cast<double>(std::numeric_limits<Out>::lowest()))
|
|
return std::numeric_limits<Out>::lowest();
|
|
if (static_cast<double>(x) > static_cast<double>(std::numeric_limits<Out>::max()))
|
|
return std::numeric_limits<Out>::max();
|
|
if (is_negative(x))
|
|
x -= 1;
|
|
return static_cast<Out>(x + 0.5);
|
|
}
|
|
|
|
// API search type: floor : a -> b
|
|
// fwd bind count: 0
|
|
// Converts a value to the nearest smaller integer.
|
|
template <typename X, typename Out = int>
|
|
Out floor(X x)
|
|
{
|
|
static_assert(!std::is_integral<X>::value, "type must be non-integral");
|
|
static_assert(std::is_integral<Out>::value, "type must be integral");
|
|
if (is_negative(x))
|
|
x -= 1;
|
|
return static_cast<Out>(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 <typename X>
|
|
X floor_to_int_mult(X n, X x)
|
|
{
|
|
static_assert(std::is_integral<X>::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>(x - 1);
|
|
return static_cast<X>((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 <typename X>
|
|
X ceil_to_int_mult(X n, X x)
|
|
{
|
|
return floor_to_int_mult(n, static_cast<X>(x + abs(n) - 1));
|
|
}
|
|
|
|
// API search type: ceil : a -> b
|
|
// fwd bind count: 0
|
|
// Converts a value to the nearest greater integer.
|
|
template <typename X, typename Out = int>
|
|
Out ceil(X x)
|
|
{
|
|
static_assert(!std::is_integral<X>::value, "type must be non-integral");
|
|
static_assert(std::is_integral<Out>::value, "type must be integral");
|
|
return floor<X, Out>(x) + 1;
|
|
}
|
|
|
|
// API search type: int_power : (Int, Int) -> Int
|
|
// fwd bind count: 1
|
|
// integer power, only exponents >= 0
|
|
template <typename X>
|
|
X int_power(X base, X exp)
|
|
{
|
|
static_assert(std::is_integral<X>::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 <typename F, typename FirstT, typename... FIn>
|
|
auto helper_min_on(F f, const FirstT& first, const FIn&... v) ->
|
|
typename std::common_type<FirstT, FIn...>::type
|
|
{
|
|
using rettype = typename std::common_type<FirstT, FIn...>::type;
|
|
using f_rettype = std::decay_t<internal::invoke_result_t<F, decltype(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<int>{
|
|
((v_trans = internal::invoke(f, v), v_trans < result_trans)
|
|
? (result = static_cast<rettype>(v), result_trans = v_trans, 0)
|
|
: 0)...};
|
|
return result;
|
|
}
|
|
|
|
template <typename F>
|
|
struct helper_min_on_t
|
|
{
|
|
helper_min_on_t(F _f) : f(_f) {}
|
|
template <typename T, typename... Ts>
|
|
auto operator()(T&& x, Ts&&... xs) -> typename std::common_type<T, Ts...>::type
|
|
{
|
|
return helper_min_on(std::forward<F>(f), std::forward<T>(x), std::forward<Ts>(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 <typename F>
|
|
auto min_on(F f) -> internal::helper_min_on_t<F>
|
|
{
|
|
return internal::helper_min_on_t<F>{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 <typename F, typename T>
|
|
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 <typename F, typename FirstT, typename... FIn>
|
|
auto helper_max_on(F f, const FirstT& first, const FIn&... v) ->
|
|
typename std::common_type<FirstT, FIn...>::type
|
|
{
|
|
using rettype = typename std::common_type<FirstT, FIn...>::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<int>{
|
|
((v_trans = internal::invoke(f, v), v_trans > result_trans)
|
|
? (result = static_cast<rettype>(v), result_trans = v_trans, 0)
|
|
: 0)...};
|
|
return result;
|
|
}
|
|
|
|
template <typename F>
|
|
struct helper_max_on_t
|
|
{
|
|
helper_max_on_t(F _f) : f(_f) {}
|
|
template <typename T, typename... Ts>
|
|
auto operator()(T&& x, Ts&&... xs) -> typename std::common_type<T, Ts...>::type
|
|
{
|
|
return helper_max_on(std::forward<F>(f), std::forward<T>(x), std::forward<Ts>(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 <typename F>
|
|
auto max_on(F f) -> internal::helper_max_on_t<F>
|
|
{
|
|
return internal::helper_max_on_t<F>{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 <typename F, typename T>
|
|
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 <typename U, typename... V>
|
|
auto min(const U& u, const V&... v) -> typename std::common_type<U, V...>::type
|
|
{
|
|
using rettype = typename std::common_type<U, V...>::type;
|
|
rettype result = static_cast<rettype>(u);
|
|
(void)std::initializer_list<int>{((v < result) ? (result = static_cast<rettype>(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 <typename T>
|
|
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 <typename U, typename... V>
|
|
auto max(const U& u, const V&... v) -> typename std::common_type<U, V...>::type
|
|
{
|
|
using rettype = typename std::common_type<U, V...>::type;
|
|
rettype result = static_cast<rettype>(u);
|
|
(void)std::initializer_list<int>{((v > result) ? (result = static_cast<rettype>(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 <typename T>
|
|
T max_2(const T& x, const T& y)
|
|
{
|
|
return y > x ? y : x;
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
template <typename X>
|
|
typename std::enable_if<std::is_floating_point<X>::value, X>::type
|
|
cyclic_value_helper_mod(X x, X y)
|
|
{
|
|
return std::fmod(x, y);
|
|
}
|
|
|
|
template <typename X>
|
|
typename std::enable_if<std::is_integral<X>::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 <typename X>
|
|
std::function<X(X)> 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 <typename X>
|
|
std::function<X(X, X)> 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 <typename X>
|
|
std::function<X(X, X)> 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 <typename X>
|
|
std::function<X(X, X)> 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 <typename T>
|
|
T deg_to_rad(T x)
|
|
{
|
|
static_assert(std::is_floating_point<T>::value, "Please use a floating-point type.");
|
|
return static_cast<T>(x * pi() / 180.0);
|
|
}
|
|
|
|
// API search type: rad_to_deg : Float -> Float
|
|
// fwd bind count: 0
|
|
// converts radians to degrees
|
|
template <typename T>
|
|
T rad_to_deg(T x)
|
|
{
|
|
static_assert(std::is_floating_point<T>::value, "Please use a floating-point type.");
|
|
return static_cast<T>(x * 180.0 / pi());
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Container, typename T>
|
|
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<Container>(xs);
|
|
}
|
|
|
|
template <typename Container, typename T>
|
|
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 <typename Container,
|
|
typename T = typename internal::remove_const_and_ref_t<Container>::value_type>
|
|
auto normalize_min_max(const T& lower, const T& upper, Container&& xs)
|
|
{
|
|
return internal::normalize_min_max(internal::can_reuse_v<Container>{},
|
|
lower, upper, std::forward<Container>(xs));
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Container, typename T>
|
|
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<T>(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<Container>(xs);
|
|
}
|
|
|
|
template <typename Container, typename T>
|
|
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 <typename Container,
|
|
typename T = typename internal::remove_const_and_ref_t<Container>::value_type>
|
|
auto normalize_mean_stddev(
|
|
const T& mean, const T& stddev, Container&& xs)
|
|
{
|
|
return internal::normalize_mean_stddev(internal::can_reuse_v<Container>{},
|
|
mean, stddev, std::forward<Container>(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 <typename Container>
|
|
auto standardize(Container&& xs)
|
|
{
|
|
typedef typename internal::remove_const_and_ref_t<Container>::value_type T;
|
|
T mean(0);
|
|
T stddev(1);
|
|
return normalize_mean_stddev(mean, stddev, std::forward<Container>(xs));
|
|
}
|
|
|
|
// API search type: add_to : a -> (a -> a)
|
|
// Provide a function adding to a given constant.
|
|
// add_to(3)(2) == 5
|
|
template <typename X>
|
|
std::function<X(X)> 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 <typename X>
|
|
std::function<X(X)> 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 <typename X>
|
|
std::function<X(X)> 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 <typename X>
|
|
std::function<X(X)> 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 <typename X>
|
|
std::function<X(X)> 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 ContainerIn,
|
|
typename ContainerIntervals,
|
|
typename ContainerOut =
|
|
std::vector<
|
|
std::pair<
|
|
typename ContainerIntervals::value_type,
|
|
std::size_t>>,
|
|
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 <typename T>
|
|
std::vector<std::pair<T, T>> generate_consecutive_intervals(
|
|
const T& first_lower_bound, const T& step, std::size_t count)
|
|
{
|
|
const auto count_as_T = static_cast<T>(count);
|
|
return zip(
|
|
numbers_step<T>(
|
|
first_lower_bound,
|
|
first_lower_bound + count_as_T * step,
|
|
step),
|
|
numbers_step<T>(
|
|
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 ContainerIn,
|
|
typename ContainerOut =
|
|
std::vector<
|
|
std::pair<
|
|
typename ContainerIn::value_type,
|
|
std::size_t>>,
|
|
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 <typename T>
|
|
std::vector<T> modulo_chain(const std::vector<T>& factors, T val)
|
|
{
|
|
std::vector<T> 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 <typename T>
|
|
T line_equation(const std::pair<T, T>& a, const std::pair<T, T>& b, T x)
|
|
{
|
|
static_assert(std::is_floating_point<T>::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 <algorithm>
|
|
|
|
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 <typename Container, typename UnaryPredicate,
|
|
typename T = typename Container::value_type>
|
|
maybe<T> find_first_by(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, Container>();
|
|
auto it = std::find_if(std::begin(xs), std::end(xs), pred);
|
|
if (it == std::end(xs))
|
|
return nothing<T>();
|
|
return just<T>(*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 <typename Container, typename UnaryPredicate,
|
|
typename T = typename Container::value_type>
|
|
maybe<T> find_last_by(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container, typename UnaryPredicate>
|
|
maybe<std::size_t> find_first_idx_by
|
|
(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, Container>();
|
|
auto it = std::find_if(std::begin(xs), std::end(xs), pred);
|
|
if (it == std::end(xs))
|
|
return nothing<std::size_t>();
|
|
return static_cast<std::size_t>(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 <typename Container, typename UnaryPredicate>
|
|
maybe<std::size_t> find_last_idx_by
|
|
(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container>
|
|
maybe<std::size_t> 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 <typename Container>
|
|
maybe<std::size_t> 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 ContainerOut = std::vector<std::size_t>,
|
|
typename UnaryPredicate, typename Container>
|
|
ContainerOut find_all_idxs_by(UnaryPredicate p, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 ContainerOut = std::vector<std::size_t>,
|
|
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 ContainerOut =
|
|
std::vector<std::size_t>, 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 ContainerOut = std::vector<std::size_t>, typename Container>
|
|
ContainerOut find_all_instances_of_token_non_overlapping
|
|
(const Container& token, const Container& xs)
|
|
{
|
|
auto overlapping_instances = find_all_instances_of_token<ContainerOut>(
|
|
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 <typename Container>
|
|
maybe<std::size_t> find_first_instance_of_token
|
|
(const Container& token, const Container& xs)
|
|
{
|
|
if (size_of_cont(token) > size_of_cont(xs))
|
|
return nothing<std::size_t>();
|
|
|
|
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<std::size_t>();
|
|
}
|
|
|
|
} // 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 <algorithm>
|
|
#include <set>
|
|
#include <unordered_set>
|
|
|
|
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 <typename SetType>
|
|
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 <typename UnorderSetType>
|
|
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 <typename SetType>
|
|
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 <typename UnorderSetType>
|
|
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 <typename SetType>
|
|
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 <typename UnorderSetType>
|
|
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 <typename SetType>
|
|
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 <typename UnorderSetType>
|
|
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 <typename SetType>
|
|
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 <typename UnorderSetType>
|
|
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 <typename SetType>
|
|
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 <typename UnorderSetType>
|
|
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 <typename ContainerIn,
|
|
typename SetType = typename ContainerIn::value_type>
|
|
SetType sets_intersection(const ContainerIn& sets)
|
|
{
|
|
return fold_left_1(set_intersection<SetType>, 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 <typename ContainerIn,
|
|
typename UnordSetType = typename ContainerIn::value_type>
|
|
UnordSetType unordered_sets_intersection(const ContainerIn& sets)
|
|
{
|
|
return fold_left_1(unordered_set_intersection<UnordSetType>, sets);
|
|
}
|
|
|
|
} // namespace fplus
|
|
|
|
|
|
#include <algorithm>
|
|
#include <numeric>
|
|
#include <type_traits>
|
|
|
|
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 <typename UnaryPredicate, typename Container>
|
|
bool any_by(UnaryPredicate p, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container>
|
|
bool any(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
return any_by(identity<T>, 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 <typename UnaryPredicate, typename Container>
|
|
bool none_by(UnaryPredicate p, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container>
|
|
bool none(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
return none_by(identity<T>, 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 Compare, typename Container>
|
|
typename std::size_t minimum_idx_by(Compare comp,
|
|
const Container& xs)
|
|
{
|
|
internal::check_compare_for_container<Compare, Container>();
|
|
assert(is_not_empty(xs));
|
|
return static_cast<std::size_t>(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 <typename Compare, typename Container>
|
|
maybe<typename std::size_t> 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 Compare, typename Container>
|
|
typename std::size_t maximum_idx_by(Compare comp,
|
|
const Container& xs)
|
|
{
|
|
internal::check_compare_for_container<Compare, Container>();
|
|
assert(is_not_empty(xs));
|
|
return static_cast<std::size_t>(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 <typename Compare, typename Container>
|
|
maybe<typename std::size_t> 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 Container>
|
|
typename std::size_t minimum_idx(const Container& xs)
|
|
{
|
|
return minimum_idx_by(is_less<typename Container::value_type>, 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 <typename Container>
|
|
maybe<typename std::size_t> 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 Container>
|
|
typename std::size_t maximum_idx(const Container& xs)
|
|
{
|
|
return maximum_idx_by(is_less<typename Container::value_type>, 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 <typename Container>
|
|
maybe<typename std::size_t> 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 <typename F, typename Container>
|
|
std::size_t minimum_idx_on(F f, const Container& xs)
|
|
{
|
|
using Result = internal::invoke_result_t<F, typename Container::value_type>;
|
|
auto transformed = transform_convert<std::vector<std::decay_t<Result>>>(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 <typename F, typename Container>
|
|
maybe<typename std::size_t> 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 <typename F, typename Container>
|
|
std::size_t maximum_idx_on(F f, const Container& xs)
|
|
{
|
|
using Result = internal::invoke_result_t<F, typename Container::value_type>;
|
|
auto transformed = transform_convert<std::vector<std::decay_t<Result>>>(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 <typename F, typename Container>
|
|
maybe<typename std::size_t> 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 Compare, typename Container>
|
|
typename Container::value_type minimum_by(Compare comp,
|
|
const Container& xs)
|
|
{
|
|
internal::check_compare_for_container<Compare, 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 <typename Compare, typename Container>
|
|
maybe<typename Container::value_type> 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 Compare, typename Container>
|
|
typename Container::value_type maximum_by(Compare comp,
|
|
const Container& xs)
|
|
{
|
|
internal::check_compare_for_container<Compare, 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 <typename Compare, typename Container>
|
|
maybe<typename Container::value_type> 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>
|
|
typename Container::value_type minimum(const Container& xs)
|
|
{
|
|
return minimum_by(is_less<typename Container::value_type>, 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 <typename Container>
|
|
maybe<typename Container::value_type> 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>
|
|
typename Container::value_type maximum(const Container& xs)
|
|
{
|
|
return maximum_by(is_less<typename Container::value_type>, 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 <typename Container>
|
|
maybe<typename Container::value_type> 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 F, typename Container>
|
|
typename Container::value_type minimum_on(F f, const Container& xs)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, typename Container::value_type>();
|
|
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 <typename F, typename Container>
|
|
maybe<typename Container::value_type> 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 F, typename Container>
|
|
typename Container::value_type maximum_on(F f, const Container& xs)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, typename Container::value_type>();
|
|
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 <typename F, typename Container>
|
|
maybe<typename Container::value_type> 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 <typename Result, typename Container>
|
|
Result mean(const Container& xs)
|
|
{
|
|
assert(size_of_cont(xs) != 0);
|
|
typedef typename Container::value_type T;
|
|
return static_cast<Result>(sum(xs) / static_cast<T>(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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
T mean_obj_div_double(const Container& xs)
|
|
{
|
|
assert(size_of_cont(xs) != 0);
|
|
return sum(xs) / static_cast<double>(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 <typename Result, typename Container>
|
|
Result mean_using_doubles(const Container& xs)
|
|
{
|
|
assert(size_of_cont(xs) != 0);
|
|
auto xs_as_doubles = convert_elems<double>(xs);
|
|
auto result_as_double = mean<double>(xs_as_doubles);
|
|
if (!std::is_integral<Result>::value)
|
|
return static_cast<Result>(result_as_double);
|
|
else
|
|
return round<double, Result>(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 <typename Container,
|
|
typename Result = typename Container::value_type>
|
|
Result median(const Container& xs)
|
|
{
|
|
assert(is_not_empty(xs));
|
|
|
|
if (size_of_cont(xs) == 1)
|
|
return static_cast<Result>(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<Result>(*it);
|
|
}
|
|
else
|
|
{
|
|
auto it1 = std::begin(xsSorted);
|
|
internal::advance_iterator(it1, size_of_cont(xsSorted) / 2 - 1);
|
|
auto it2 = it1;
|
|
++it2;
|
|
return static_cast<Result>(*it1 + *it2) / static_cast<Result>(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 <typename Container, typename Compare>
|
|
bool all_unique_by_less(Compare comp, const Container& xs)
|
|
{
|
|
internal::check_compare_for_container<Compare, 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 <typename Container>
|
|
bool all_unique_less(const Container& xs)
|
|
{
|
|
typedef typename Container::value_type T;
|
|
auto comp = std::less<T>();
|
|
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 <typename Container>
|
|
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 <typename Container>
|
|
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<std::list<T>>(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 <typename UnaryPredicate, typename Container>
|
|
std::size_t count_if(UnaryPredicate p, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename Container>
|
|
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 <typename UnaryPredicate, typename Container>
|
|
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 <typename Container>
|
|
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 <typename Container>
|
|
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 ContainerIn,
|
|
typename ContainerOut = std::vector<std::size_t>,
|
|
typename T = typename ContainerIn::value_type>
|
|
ContainerOut fill_pigeonholes_to(std::size_t idx_end, const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_integral<T>::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<std::size_t>(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 ContainerIn,
|
|
typename ContainerOut = std::vector<std::size_t>,
|
|
typename T = typename ContainerIn::value_type>
|
|
ContainerOut fill_pigeonholes(const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_integral<T>::value,
|
|
"Type must be integral.");
|
|
|
|
if (is_empty(xs))
|
|
return {};
|
|
|
|
return(fill_pigeonholes_to<ContainerIn, ContainerOut>(
|
|
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 ContainerIn,
|
|
typename ContainerOut = std::vector<std::uint8_t>,
|
|
typename T = typename ContainerIn::value_type>
|
|
ContainerOut fill_pigeonholes_bool_to(std::size_t idx_end, const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_integral<T>::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<std::size_t>(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 ContainerIn,
|
|
typename ContainerOut = std::vector<std::uint8_t>,
|
|
typename T = typename ContainerIn::value_type>
|
|
ContainerOut fill_pigeonholes_bool(const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_integral<T>::value,
|
|
"Type must be integral.");
|
|
|
|
if (is_empty(xs))
|
|
return {};
|
|
|
|
return(fill_pigeonholes_bool_to<ContainerIn, ContainerOut>(
|
|
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 <typename ContainerIn,
|
|
typename SubContainerIn = typename ContainerIn::value_type,
|
|
typename T = typename SubContainerIn::value_type,
|
|
typename ContainerOut = std::vector<T>>
|
|
ContainerOut present_in_all(const ContainerIn& xs)
|
|
{
|
|
return convert_container<ContainerOut>(
|
|
fplus::sets_intersection(
|
|
transform(
|
|
convert_container<std::set<T>, 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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
maybe<T> elem_at_idx_or_nothing(signed int idx, const Container& xs)
|
|
{
|
|
if (idx < 0 || idx >= static_cast<signed int>(size_of_cont(xs)))
|
|
{
|
|
return {};
|
|
}
|
|
auto it = std::begin(xs);
|
|
internal::advance_iterator(it, static_cast<std::size_t>(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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
T elem_at_idx_or_constant(const T& c, signed int idx, const Container& xs)
|
|
{
|
|
if (idx < 0 || idx >= static_cast<signed int>(size_of_cont(xs)))
|
|
{
|
|
return c;
|
|
}
|
|
auto it = std::begin(xs);
|
|
internal::advance_iterator(it, static_cast<std::size_t>(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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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<signed int>(size_of_cont(xs)))
|
|
{
|
|
return xs.back();
|
|
}
|
|
auto it = std::begin(xs);
|
|
internal::advance_iterator(it, static_cast<std::size_t>(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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
T elem_at_idx_or_wrap(signed int idx, const Container& xs)
|
|
{
|
|
assert(is_not_empty(xs));
|
|
const signed int cont_size = static_cast<signed int>(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<std::size_t>(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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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<Container>(ys);
|
|
const signed int idx_end = static_cast<signed int>(xs_size + count_end);
|
|
const signed int idx_start = -static_cast<signed int>(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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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<Container>(ys);
|
|
const signed int idx_end = static_cast<signed int>(xs_size + count_end);
|
|
const signed int idx_start = -static_cast<signed int>(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 <cmath>
|
|
|
|
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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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<std::size_t>(floor(idx));
|
|
std::size_t idx_ceil = static_cast<std::size_t>(ceil(idx));
|
|
if (idx_ceil >= size_of_cont(xs))
|
|
{
|
|
return xs.back();
|
|
}
|
|
double idx_floor_float = static_cast<double>(idx_floor);
|
|
double idx_ceil_float = static_cast<double>(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 <map>
|
|
#include <unordered_map>
|
|
|
|
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 <typename MapOut, typename ContainerIn>
|
|
MapOut pairs_to_map(const ContainerIn& pairs)
|
|
{
|
|
return convert_container_and_elems<MapOut>(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 <typename ContainerIn,
|
|
typename Key = typename ContainerIn::value_type::first_type,
|
|
typename SingleValue = typename ContainerIn::value_type::second_type,
|
|
typename MapOut = std::map<Key, std::vector<SingleValue>>>
|
|
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 <typename ContainerIn,
|
|
typename Key = typename ContainerIn::value_type::first_type,
|
|
typename SingleValue = typename ContainerIn::value_type::second_type,
|
|
typename MapOut = std::unordered_map<Key, std::vector<SingleValue>>>
|
|
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 <typename MapType,
|
|
typename MapPair = typename MapType::value_type,
|
|
typename Key = typename std::remove_const<typename MapPair::first_type>::type,
|
|
typename Val = typename std::remove_const<typename MapPair::second_type>::type,
|
|
typename OutPair = std::pair<Key, Val>,
|
|
typename ContainerOut = std::vector<OutPair>>
|
|
ContainerOut map_to_pairs(const MapType& dict)
|
|
{
|
|
return convert_container_and_elems<ContainerOut>(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 <typename F, typename MapIn>
|
|
auto transform_map_values(F f, const MapIn& map)
|
|
{
|
|
using MapInPair = typename MapIn::value_type;
|
|
using Key = std::remove_const_t<typename MapInPair::first_type>;
|
|
using InVal = std::remove_const_t<typename MapInPair::second_type>;
|
|
using OutVal = std::decay_t<internal::invoke_result_t<F, InVal>>;
|
|
using MapOut = typename internal::SameMapTypeNewTypes<MapIn, Key, OutVal>::type;
|
|
|
|
return pairs_to_map<MapOut>(
|
|
transform(
|
|
bind_1st_of_2(transform_snd<Key, InVal, F>, 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 <typename F, typename MapIn>
|
|
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<decltype(both), Key, SingleValue,
|
|
typename internal::SameMapTypeNewTypes<MapIn, Key, std::vector<SingleValue>>::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 <typename MapType>
|
|
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<typename MapType>
|
|
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<std::vector<Key>, 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 <typename MapType,
|
|
typename ContainerOut =
|
|
std::vector<typename std::remove_const<typename MapType::key_type>::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<ContainerOut>(
|
|
fst<FirstType, SecondType>, 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 <typename MapType,
|
|
typename ContainerOut =
|
|
std::vector<typename std::remove_const<typename MapType::mapped_type>::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<ContainerOut>(
|
|
snd<FirstType, SecondType>, 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 <typename MapIn,
|
|
typename MapInPair = typename MapIn::value_type,
|
|
typename InKey = typename MapInPair::first_type,
|
|
typename InVal = typename MapInPair::second_type,
|
|
typename OutKey = InVal,
|
|
typename OutVal = typename std::remove_const<InKey>::type,
|
|
typename MapOut = typename internal::SameMapTypeNewTypes<MapIn, OutKey, OutVal>::type>
|
|
MapOut swap_keys_and_values(const MapIn& dict)
|
|
{
|
|
auto inAsPairs = map_to_pairs(dict);
|
|
auto outAsPairs = transform(swap_pair_elems<InKey, InVal>, inAsPairs);
|
|
return pairs_to_map<MapOut>(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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename Key = typename std::remove_const<typename ContainerIn1::value_type>::type,
|
|
typename Val = typename std::remove_const<typename ContainerIn2::value_type>::type,
|
|
typename MapOut = std::map<Key, Val>>
|
|
MapOut create_map(const ContainerIn1& keys, const ContainerIn2& values)
|
|
{
|
|
auto pairs = zip(keys, values);
|
|
return pairs_to_map<MapOut>(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 <typename ContainerIn, typename F>
|
|
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 <typename ContainerIn, typename F>
|
|
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 <typename ContainerIn1, typename ContainerIn2,
|
|
typename Key = typename std::remove_const<typename ContainerIn1::value_type>::type,
|
|
typename Val = typename std::remove_const<typename ContainerIn2::value_type>::type,
|
|
typename MapOut = std::unordered_map<Key, Val >>
|
|
MapOut create_unordered_map(
|
|
const ContainerIn1& keys,
|
|
const ContainerIn2& values)
|
|
{
|
|
auto pairs = zip(keys, values);
|
|
return pairs_to_map<MapOut>(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 <typename ContainerIn, typename F>
|
|
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 <typename ContainerIn, typename F>
|
|
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 <typename MapType,
|
|
typename Key = typename MapType::key_type,
|
|
typename Val = typename MapType::mapped_type>
|
|
maybe<Val> get_from_map(const MapType& map, const Key& key)
|
|
{
|
|
auto it = map.find(key);
|
|
if (it == std::end(map))
|
|
return nothing<Val>();
|
|
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 <typename MapType,
|
|
typename Key = typename MapType::key_type,
|
|
typename Val = typename MapType::mapped_type>
|
|
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 <typename MapType,
|
|
typename Key = typename MapType::key_type,
|
|
typename Val = typename MapType::mapped_type>
|
|
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 <typename MapType,
|
|
typename KeysContainer,
|
|
typename Key = typename MapType::key_type,
|
|
typename Val = typename MapType::mapped_type>
|
|
maybe<Val> get_first_from_map(const MapType& map, const KeysContainer& keys)
|
|
{
|
|
static_assert(std::is_same<typename KeysContainer::value_type, Key>::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<Val>();
|
|
}
|
|
|
|
// 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 <typename MapType,
|
|
typename KeysContainer,
|
|
typename Key = typename MapType::key_type,
|
|
typename Val = typename MapType::mapped_type>
|
|
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 <typename MapType,
|
|
typename KeysContainer,
|
|
typename Key = typename MapType::key_type,
|
|
typename Val = typename MapType::mapped_type>
|
|
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 <typename MapType, typename Key = typename MapType::key_type>
|
|
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 <typename MapType, typename Pred>
|
|
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 <typename MapType, typename Pred>
|
|
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 <typename MapType, typename KeyContainer>
|
|
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<KeyContainer>, 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 <typename MapType, typename KeyContainer>
|
|
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<KeyContainer>, 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 <typename MapType, typename Pred>
|
|
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 <typename MapType, typename Pred>
|
|
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 <typename MapType, typename ValueContainer>
|
|
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<ValueContainer>, 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 <typename MapType, typename ValueContainer>
|
|
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<ValueContainer>, 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 MapContainer,
|
|
typename ContainerOut =
|
|
std::vector<maybe<typename MapContainer::value_type::mapped_type>>,
|
|
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<ContainerOut>(
|
|
bind_2nd_of_2(get_from_map<MapType>, 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<typename Key, typename Val>
|
|
maybe<Val> choose(const std::vector<std::pair<Key, Val>>& pairs, const Key& x)
|
|
{
|
|
if (count(x, transform(fst<Key, Val>, pairs)) != 1)
|
|
return {};
|
|
return get_from_map(pairs_to_map<std::unordered_map<Key, Val>>(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<typename Key, typename Val>
|
|
maybe<Val> choose_by(
|
|
const std::vector<std::pair<std::function<bool(const Key&)>, Val>>& pairs,
|
|
const Key& x)
|
|
{
|
|
maybe<Val> result;
|
|
for (const auto& p : pairs)
|
|
{
|
|
if (internal::invoke(p.first, x))
|
|
{
|
|
if (is_just(result))
|
|
{
|
|
return nothing<Val>();
|
|
}
|
|
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 <typename Key, typename ValStub>
|
|
auto choose_lazy(const std::vector<std::pair<Key, ValStub>>& pairs,
|
|
const Key& x)
|
|
{
|
|
using Ret = maybe<std::decay_t<internal::invoke_result_t<ValStub>>>;
|
|
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 <typename Key, typename ValStub>
|
|
auto choose_by_lazy(
|
|
const std::vector<std::pair<std::function<bool(const Key&)>, ValStub>>& pairs,
|
|
const Key& x)
|
|
{
|
|
using Ret = maybe<std::decay_t<internal::invoke_result_t<ValStub>>>;
|
|
|
|
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<typename Key, typename Val>
|
|
Val choose_def(const Val& def,
|
|
const std::vector<std::pair<Key, Val>>& pairs, const Key& x)
|
|
{
|
|
if (count(x, transform(fst<Key, Val>, pairs)) != 1)
|
|
return def;
|
|
return get_from_map_with_def(
|
|
pairs_to_map<std::unordered_map<Key, Val>>(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<typename Key, typename Val>
|
|
Val choose_by_def(const Val& def,
|
|
const std::vector<std::pair<std::function<bool(const Key&)>, 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 <typename Key, typename ValStub>
|
|
auto choose_def_lazy(const ValStub& def,
|
|
const std::vector<std::pair<Key, ValStub>>& 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 <typename Key, typename ValStub>
|
|
auto choose_by_def_lazy(
|
|
const ValStub& def,
|
|
const std::vector<std::pair<std::function<bool(const Key&)>, 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 <iterator>
|
|
#include <utility>
|
|
|
|
|
|
|
|
namespace fplus
|
|
{
|
|
namespace internal
|
|
{
|
|
template <typename GroupByCallable, typename F, typename ContainerIn>
|
|
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 <typename BinaryPredicate, typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
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<BinaryPredicate, ContainerIn>();
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::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 <typename F, typename ContainerIn>
|
|
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 <typename F, typename ContainerIn>
|
|
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 <typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
ContainerOut group(const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::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<decltype(pred), ContainerIn, ContainerOut>(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 <typename BinaryPredicate, typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
ContainerOut group_globally_by(BinaryPredicate p, const ContainerIn& xs)
|
|
{
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, ContainerIn>();
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::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 <typename F, typename ContainerIn>
|
|
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 <typename F, typename ContainerIn>
|
|
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 <typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
ContainerOut group_globally(const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::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 <typename BinaryPredicate, typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
ContainerOut cluster_by(BinaryPredicate p, const ContainerIn& xs)
|
|
{
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, ContainerIn>();
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::value,
|
|
"Containers do not match.");
|
|
|
|
typedef std::vector<unsigned char> bools;
|
|
bools zero_filled_row(size_of_cont(xs), 0);
|
|
|
|
// adjecency matrix
|
|
typedef std::vector<bools> 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<std::size_t> idxs;
|
|
typedef std::vector<idxs> 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<void(std::size_t)> 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<InnerContainerOut>(idx_to_val, sort(val_idxs));
|
|
};
|
|
|
|
return transform_convert<ContainerOut>(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 <typename UnaryPredicate, typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
ContainerOut split_by
|
|
(UnaryPredicate pred, bool allow_empty, const ContainerIn& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, ContainerIn>();
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::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 <typename UnaryPredicate, typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
ContainerOut split_by_keep_separators
|
|
(UnaryPredicate pred, const ContainerIn& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, ContainerIn>();
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type>
|
|
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 <typename ContainerIn,
|
|
typename ContainerDelims>
|
|
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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type>
|
|
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 <typename Container>
|
|
std::pair<Container, Container> 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 <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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<Container>(
|
|
{
|
|
splitted.first,
|
|
singleton_seq<T, Container>(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 <typename UnaryPredicate, typename Container>
|
|
std::pair<Container, Container> partition
|
|
(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
internal::check_unary_predicate_for_container<UnaryPredicate, 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 <typename ContainerIdxs, typename ContainerIn,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut split_at_idxs(const ContainerIdxs& idxsIn, const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_same<typename ContainerIdxs::value_type, std::size_t>::value,
|
|
"Indices must be std::size_t");
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::value,
|
|
"Containers do not match.");
|
|
ContainerIdxs idxStartC = {0};
|
|
ContainerIdxs idxEndC = {size_of_cont(xs)};
|
|
std::vector<ContainerIdxs> 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 <typename ContainerIn,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut split_every(std::size_t n, const ContainerIn& xs)
|
|
{
|
|
return split_at_idxs<
|
|
std::vector<std::size_t>,
|
|
ContainerIn,
|
|
ContainerOut>(
|
|
numbers_step<std::size_t>(
|
|
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 <typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
ContainerOut split_by_token(const ContainerIn& token,
|
|
bool allow_empty, const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::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<std::size_t>(size_of_cont(token)), token_begins);
|
|
assert(is_sorted(interweave(token_begins, token_ends)));
|
|
|
|
typedef std::vector<std::size_t> 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 <typename BinaryPredicate,
|
|
typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type,
|
|
typename ContainerOut =
|
|
typename std::vector<std::pair<std::size_t, T>>>
|
|
ContainerOut run_length_encode_by(BinaryPredicate pred, const ContainerIn& xs)
|
|
{
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, ContainerIn>();
|
|
ContainerOut result;
|
|
auto groups = group_by(pred, xs);
|
|
auto group_to_pair = [](const ContainerIn& group) -> std::pair<std::size_t, T>
|
|
{
|
|
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 <typename ContainerIn,
|
|
typename T = typename ContainerIn::value_type>
|
|
auto run_length_encode(const ContainerIn& xs)
|
|
{
|
|
return run_length_encode_by(is_equal<T>, 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 <typename ContainerIn,
|
|
typename Pair = typename ContainerIn::value_type,
|
|
typename Cnt = typename Pair::first_type>
|
|
auto run_length_decode(const ContainerIn& pairs)
|
|
{
|
|
static_assert(std::is_convertible<Cnt, std::size_t>::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 <typename Container, typename UnaryPredicate>
|
|
std::pair<Container, Container> span(UnaryPredicate pred, const Container& xs)
|
|
{
|
|
auto maybeIdx = find_first_idx_by(logical_not(pred), xs);
|
|
return {
|
|
take(just_with_default<std::size_t>(size_of_cont(xs), maybeIdx), xs),
|
|
drop(just_with_default<std::size_t>(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 <typename ContainerIn,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
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<std::size_t>(
|
|
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 <typename ContainerIn,
|
|
typename ContainerOut = std::vector<ContainerIn>>
|
|
ContainerOut aperture(std::size_t length, const ContainerIn& xs)
|
|
{
|
|
assert(length > 0);
|
|
const auto start_idxs =
|
|
numbers<std::size_t>(
|
|
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 <typename Container>
|
|
Container stride(std::size_t step, const Container& xs)
|
|
{
|
|
assert(step > 0);
|
|
Container ys;
|
|
auto it = internal::get_back_inserter<Container>(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 <typename Container>
|
|
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<double, std::size_t>(
|
|
trim_ratio * static_cast<double>(size_of_cont(xs_sorted)));
|
|
amount = std::min(size_of_cont(xs_sorted) / 2, amount);
|
|
const auto parts = split_at_idxs(
|
|
std::vector<std::size_t>({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>({
|
|
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 <typename F, typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
ContainerOut separate_on(F f, const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::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 <typename ContainerIn,
|
|
typename ContainerOut = typename std::vector<ContainerIn>>
|
|
ContainerOut separate(const ContainerIn& xs)
|
|
{
|
|
static_assert(std::is_same<ContainerIn,
|
|
typename ContainerOut::value_type>::value,
|
|
"Containers do not match.");
|
|
typedef typename ContainerIn::value_type T;
|
|
return separate_on(identity<T>, xs);
|
|
}
|
|
|
|
} // namespace fplus
|
|
|
|
|
|
#include <algorithm>
|
|
#include <future>
|
|
#include <iterator>
|
|
#include <mutex>
|
|
#include <random>
|
|
|
|
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 <typename F, typename ContainerIn,
|
|
typename ContainerOut = typename internal::same_cont_new_t_from_binary_f<
|
|
ContainerIn, F, std::size_t, typename ContainerIn::value_type, 0>::type>
|
|
ContainerOut transform_with_idx(F f, const ContainerIn& xs)
|
|
{
|
|
internal::trigger_static_asserts<internal::binary_function_tag, F>();
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, size_of_cont(xs));
|
|
auto it = internal::get_back_inserter<ContainerOut>(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 <typename F, typename ContainerIn>
|
|
auto transform_and_keep_justs(F f, const ContainerIn& xs)
|
|
{
|
|
using X = typename ContainerIn::value_type;
|
|
internal::
|
|
trigger_static_asserts<internal::unary_function_tag, F, X>();
|
|
|
|
using ContainerOut = typename internal::same_cont_new_t<
|
|
ContainerIn,
|
|
typename std::decay_t<internal::invoke_result_t<F, X>>::type>::type;
|
|
|
|
auto transformed = transform(f, xs);
|
|
return justs<decltype(transformed), ContainerOut>(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 <typename F, typename ContainerIn>
|
|
auto transform_and_keep_oks(F f, const ContainerIn& xs)
|
|
{
|
|
using X = typename ContainerIn::value_type;
|
|
internal::
|
|
trigger_static_asserts<internal::unary_function_tag, F, X>();
|
|
|
|
using ContainerOut = typename internal::same_cont_new_t<
|
|
ContainerIn,
|
|
typename std::decay_t<internal::invoke_result_t<F, X>>::ok_t>::type;
|
|
auto transformed = transform(f, xs);
|
|
return oks<decltype(transformed), ContainerOut>(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 <typename F, typename ContainerIn>
|
|
auto transform_and_concat(F f, const ContainerIn& xs)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type>();
|
|
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 <typename Container>
|
|
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<T, Container>, 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 <typename ContainerIn,
|
|
typename ContainerOut = typename ContainerIn::value_type>
|
|
ContainerOut interleave(const ContainerIn& xss)
|
|
{
|
|
typedef typename ContainerIn::value_type inner_t;
|
|
typedef std::vector<typename inner_t::const_iterator> 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<its_t>(inner_cbegin, xss),
|
|
transform_convert<its_t>(inner_cend, xss));
|
|
|
|
ContainerOut result;
|
|
const std::size_t length = sum(transform(size_of_cont<inner_t>, xss));
|
|
internal::prepare_container(result, length);
|
|
auto it_out = internal::get_back_inserter<ContainerOut>(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 <typename Container>
|
|
Container transpose(const Container& rows)
|
|
{
|
|
if (is_empty(rows))
|
|
{
|
|
return {};
|
|
}
|
|
return split_every<typename Container::value_type, Container>(
|
|
size_of_cont(rows), interleave(rows));
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Container>
|
|
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<Container>(xs);
|
|
}
|
|
|
|
template <typename Container>
|
|
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 <typename Container>
|
|
auto shuffle(std::uint_fast32_t seed, Container&& xs)
|
|
{
|
|
return(internal::shuffle(internal::can_reuse_v<Container>{},
|
|
seed, std::forward<Container>(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 <typename Container>
|
|
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>
|
|
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<std::size_t> 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 <typename Container>
|
|
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<std::size_t> dis(0, size_of_cont(xs) - 1);
|
|
const auto draw = [&]() -> typename Container::value_type
|
|
{
|
|
return elem_at_idx(dis(gen), xs);
|
|
};
|
|
return generate<Container>(draw, n);
|
|
}
|
|
|
|
// API search type: apply_functions : ([(a -> b)], a) -> [b]
|
|
// fwd bind count: 1
|
|
// Applies a list of functions to a value.
|
|
template <typename FunctionContainer,
|
|
typename F = typename FunctionContainer::value_type,
|
|
typename FIn>
|
|
auto apply_functions(const FunctionContainer& functions, const FIn& x)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, FIn>();
|
|
|
|
using FOut = std::decay_t<internal::invoke_result_t<F, FIn>>;
|
|
using ContainerOut =
|
|
typename internal::same_cont_new_t<FunctionContainer, FOut, 0>::type;
|
|
|
|
ContainerOut ys;
|
|
internal::prepare_container(ys, size_of_cont(functions));
|
|
auto it = internal::get_back_inserter<ContainerOut>(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 <typename F, typename FIn>
|
|
auto apply_function_n_times(F f, std::size_t n, const FIn& x)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, FIn>();
|
|
using FOut = std::decay_t<internal::invoke_result_t<F, FIn>>;
|
|
static_assert(std::is_same<FOut, FIn>::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 <typename F, typename ContainerIn>
|
|
auto transform_parallelly(F f, const ContainerIn& xs)
|
|
{
|
|
using ContainerOut = typename internal::
|
|
same_cont_new_t_from_unary_f<ContainerIn, F, 0>::type;
|
|
using X = typename ContainerIn::value_type;
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, X>();
|
|
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<ContainerOut>(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 <typename F, typename ContainerIn>
|
|
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<ContainerIn, F, 0>::type;
|
|
using X = typename ContainerIn::value_type;
|
|
using Y = internal::invoke_result_t<F, X>;
|
|
using x_ptr_t = const X*;
|
|
auto queue = transform_convert<std::vector<x_ptr_t>>(
|
|
[](const X& x) -> x_ptr_t
|
|
{
|
|
return &x;
|
|
}, xs);
|
|
|
|
std::mutex queue_mutex;
|
|
std::mutex thread_results_mutex;
|
|
std::map<std::size_t, std::decay_t<Y>> thread_results;
|
|
std::size_t queue_idx = 0;
|
|
|
|
const auto worker_func = [&]()
|
|
{
|
|
for (;;)
|
|
{
|
|
std::size_t idx = std::numeric_limits<std::size_t>::max();
|
|
x_ptr_t x_ptr = nullptr;
|
|
{
|
|
std::lock_guard<std::mutex> 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<std::mutex> 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<std::vector<std::thread>>(create_thread, n);
|
|
|
|
for (auto& thread : threads)
|
|
{
|
|
thread.join();
|
|
}
|
|
|
|
return get_map_values<decltype(thread_results), ContainerOut>(
|
|
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 F, typename Container>
|
|
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<T, T>& 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 F, typename Container>
|
|
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<T, T>& 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 F, typename Container>
|
|
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<T, T>& 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 F, typename Container>
|
|
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<T, T>& 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 <typename Pred, typename Container>
|
|
Container keep_if_parallelly(Pred pred, const Container& xs)
|
|
{
|
|
// Avoid a temporary std::vector<bool>.
|
|
const auto idxs = find_all_idxs_by(
|
|
is_equal_to<std::uint8_t>(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 <typename Pred, typename Container>
|
|
Container keep_if_parallelly_n_threads(
|
|
std::size_t n, Pred pred, const Container& xs)
|
|
{
|
|
// Avoid a temporary std::vector<bool>.
|
|
const auto idxs = find_all_idxs_by(
|
|
is_equal_to<std::uint8_t>(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 <typename UnaryF, typename BinaryF, typename Container, typename Acc>
|
|
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 <typename UnaryF, typename BinaryF, typename Container>
|
|
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 <typename UnaryF, typename BinaryF, typename Container, typename Acc>
|
|
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 <typename UnaryF, typename BinaryF, typename Container, typename Acc>
|
|
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 <typename UnaryF, typename BinaryF, typename Container>
|
|
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 <typename UnaryF, typename BinaryF, typename Container>
|
|
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 <array>
|
|
#include <chrono>
|
|
#include <functional>
|
|
|
|
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 <std::size_t N, typename F, typename pos_t = std::array<double, N>>
|
|
pos_t minimize_downhill(
|
|
F objective_function,
|
|
double epsilon,
|
|
const pos_t& init_pos,
|
|
maybe<double> fixed_step_size = nothing<double>(),
|
|
double momentum_conservation = 0.5,
|
|
double sufficing_value = std::numeric_limits<double>::lowest(),
|
|
double min_step_factor = std::numeric_limits<double>::min(),
|
|
std::size_t max_iterations = std::numeric_limits<std::size_t>::max(),
|
|
long int max_milliseconds = std::numeric_limits<long int>::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<long int>::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<std::chrono::milliseconds>(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<double>::min() &&
|
|
dist_to_origin(new_momentum) <= std::numeric_limits<double>::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 <condition_variable>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <mutex>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
// A thread-safe queue.
|
|
template <typename T>
|
|
class queue
|
|
{
|
|
public:
|
|
queue() :
|
|
queue_(),
|
|
mutex_(),
|
|
cond_()
|
|
{}
|
|
fplus::maybe<T> pop()
|
|
{
|
|
std::unique_lock<std::mutex> lock(mutex_);
|
|
if (queue_.empty())
|
|
{
|
|
return {};
|
|
}
|
|
auto item = queue_.front();
|
|
queue_.pop_front();
|
|
return item;
|
|
}
|
|
|
|
void push(const T& item)
|
|
{
|
|
{
|
|
std::unique_lock<std::mutex> lock(mutex_);
|
|
queue_.push_back(item);
|
|
}
|
|
cond_.notify_one();
|
|
}
|
|
|
|
std::vector<T> pop_all()
|
|
{
|
|
std::unique_lock<std::mutex> mlock(mutex_);
|
|
const auto result = fplus::convert_container<std::vector<T>>(queue_);
|
|
queue_.clear();
|
|
return result;
|
|
}
|
|
|
|
std::vector<T> wait_and_pop_all()
|
|
{
|
|
std::unique_lock<std::mutex> mlock(mutex_);
|
|
cond_.wait(mlock, [&]() -> bool { return !queue_.empty(); });
|
|
const auto result = fplus::convert_container<std::vector<T>>(queue_);
|
|
queue_.clear();
|
|
return result;
|
|
}
|
|
|
|
std::vector<T> wait_for_and_pop_all(std::int64_t max_wait_time_us)
|
|
{
|
|
std::unique_lock<std::mutex> 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<std::vector<T>>(queue_);
|
|
queue_.clear();
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
std::deque<T> 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 <memory>
|
|
|
|
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 <typename T>
|
|
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 <typename XT, typename...XTypes>
|
|
friend shared_ref<XT> make_shared_ref(XTypes&&...args);
|
|
|
|
private:
|
|
std::shared_ptr<T> m_ptr;
|
|
shared_ref(T* value) :m_ptr(value) { assert(value != nullptr); }
|
|
};
|
|
|
|
// http://stackoverflow.com/a/41976419/1866775
|
|
template <typename T, typename...Types>
|
|
shared_ref<T> make_shared_ref(Types&&...args)
|
|
{
|
|
return shared_ref<T>(new T(std::forward<Types>(args)...));
|
|
}
|
|
|
|
} // namespace fplus
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
// A generic RAII class.
|
|
// It is recommended to use make_raii for constructing an instance.
|
|
template <typename INIT, typename QUIT>
|
|
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 <typename INIT, typename QUIT>
|
|
shared_ref<raii<INIT, QUIT>> make_raii(INIT init, QUIT quit)
|
|
{
|
|
return make_shared_ref<raii<INIT, QUIT>>(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 <string>
|
|
#include <type_traits>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
namespace internal
|
|
{
|
|
template <typename T>
|
|
struct helper_read_value_struct {};
|
|
|
|
template <>
|
|
struct helper_read_value_struct <int>
|
|
{
|
|
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 <long>
|
|
{
|
|
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 <long long>
|
|
{
|
|
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 <unsigned int>
|
|
{
|
|
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<unsigned int>(result_u_l);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct helper_read_value_struct <unsigned long>
|
|
{
|
|
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 <unsigned long long>
|
|
{
|
|
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 <float>
|
|
{
|
|
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 <double>
|
|
{
|
|
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 <long double>
|
|
{
|
|
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 <std::string>
|
|
{
|
|
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 <typename T>
|
|
result<T, std::string> read_value_result(const std::string& str)
|
|
{
|
|
try
|
|
{
|
|
T result;
|
|
std::size_t num_chars_used = 0;
|
|
internal::helper_read_value_struct<T>::read(str,
|
|
result, num_chars_used);
|
|
if (num_chars_used != str.size())
|
|
{
|
|
return error<T>(std::string("String not fully parsable."));
|
|
}
|
|
return ok<T, std::string>(result);
|
|
} catch(const std::invalid_argument& e) {
|
|
return error<T, std::string>(e.what());
|
|
} catch(const std::out_of_range& e) {
|
|
return error<T, std::string>(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<unsigned int>("42") == 42
|
|
// etc.
|
|
template <typename T>
|
|
maybe<T> read_value(const std::string& str)
|
|
{
|
|
return to_maybe(read_value_result<T>(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<unsigned int>(3, "42") == 42
|
|
// read_value_with_default<unsigned int>(3, "") == 3
|
|
// read_value_with_default<unsigned int>(3, "foo") == 3
|
|
// etc.
|
|
template <typename T>
|
|
T read_value_with_default(const T& def, const std::string& str)
|
|
{
|
|
return just_with_default(def, to_maybe(read_value_result<T>(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<unsigned int>("42") == 42
|
|
// read_value_unsafe<unsigned int>("") == crash
|
|
// read_value_unsafe<unsigned int>("foo") == crash
|
|
// See read_value and read_value_with_default for safe versions.
|
|
// etc.
|
|
template <typename T>
|
|
T read_value_unsafe(const std::string& str)
|
|
{
|
|
return unsafe_get_just(to_maybe(read_value_result<T>(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 <typename UnaryPredicate, typename T, typename Container>
|
|
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<Container>(xs);
|
|
}
|
|
|
|
template <typename UnaryPredicate, typename T, typename Container>
|
|
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 <typename UnaryPredicate, typename Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>>
|
|
ContainerOut replace_if(UnaryPredicate p,
|
|
const typename ContainerOut::value_type& dest, Container&& xs)
|
|
{
|
|
return internal::replace_if(internal::can_reuse_v<Container>{},
|
|
p, dest, std::forward<Container>(xs));
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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<Container>(xs);
|
|
}
|
|
|
|
template <typename Container,
|
|
typename T = typename Container::value_type>
|
|
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 Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>,
|
|
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<Container>{},
|
|
idx, dest, std::forward<Container>(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 Container,
|
|
typename ContainerOut = internal::remove_const_and_ref_t<Container>,
|
|
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<T>, 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 <typename Container>
|
|
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 <iomanip>
|
|
#include <ios>
|
|
#include <list>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <tuple>
|
|
|
|
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<std::list<T>> to String
|
|
// std::vector<T> to String
|
|
template <typename T>
|
|
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 <typename T, typename A>
|
|
std::string show(const std::vector<T, A>& xs);
|
|
|
|
template <typename T, typename A>
|
|
std::string show(const std::list<T, A>& xs);
|
|
|
|
// {1, "one"} -> "(1, one)"
|
|
template <typename X, typename Y>
|
|
std::string show(const std::pair<X, Y>& p)
|
|
{
|
|
return std::string("(") + show(p.first) + ", " + show(p.second) + ")";
|
|
}
|
|
|
|
template <typename Container> std::string show_cont(const Container& xs);
|
|
|
|
template <typename T, typename A>
|
|
std::string show(const std::vector<T, A>& xs)
|
|
{
|
|
return show_cont(xs);
|
|
}
|
|
|
|
template <typename T, typename A>
|
|
std::string show(const std::list<T, A>& xs)
|
|
{
|
|
return show_cont(xs);
|
|
}
|
|
|
|
template <typename T, typename A>
|
|
std::string show(const std::set<T, A>& xs)
|
|
{
|
|
return show_cont(xs);
|
|
}
|
|
|
|
template <typename T, typename A>
|
|
std::string show(const std::deque<T, A>& 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 <typename Container>
|
|
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<std::string> 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 <typename Container>
|
|
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 <typename Container>
|
|
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 <typename Container>
|
|
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 <typename T>
|
|
std::string show_maybe(const maybe<T>& 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 <typename Ok, typename Error>
|
|
std::string show_result(const result<Ok, Error>& 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<double>(0, 3, pi) == "3.142"
|
|
// show_float<double>(1, 3, pi) == "3.142"
|
|
// show_float<double>(2, 3, pi) == "03.142"
|
|
// show_float<double>(3, 3, pi) == "003.142"
|
|
// show_float<double>(1, 2, pi) == "3.14"
|
|
// show_float<double>(1, 4, pi) == "3.1416"
|
|
// show_float<double>(1, 7, pi) == "3.1415900"
|
|
// show_float<double>(0, 3, -pi) == "-3.142"
|
|
// show_float<double>(1, 3, -pi) == "-3.142"
|
|
// show_float<double>(2, 3, -pi) == "-3.142"
|
|
// show_float<double>(3, 3, -pi) == "-03.142"
|
|
// show_float<double>(4, 3, -pi) == "-003.142"
|
|
// show_float<double>(0, 3, 0.142) == "0.142";
|
|
// show_float<double>(1, 3, 0.142) == "0.142";
|
|
// show_float<double>(2, 3, 0.142) == "00.142";
|
|
// fill_left(8, ' ', show_float<double>(0, 3, -pi)) == " -3.142"
|
|
template <typename T>
|
|
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<int>(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<double>(' ', 8, 3, pi) == " 3.142"
|
|
// show_float_fill_left<double>(' ', 8, 6, pi) == "3.141590"
|
|
// show_float_fill_left<double>(' ', 8, 3, -pi) == " -3.142"
|
|
// show_float_fill_left<double>(' ', 2, 3, -pi) == "-3.142"
|
|
template <typename T>
|
|
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<T>(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<int>(' ', 4, 3) == " 3"
|
|
// show_fill_left<int>('0', 4, 3) == "0003"
|
|
// show_fill_left<int>(' ', 4, 12345) == "12345"
|
|
template <typename T>
|
|
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<T>(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<int>(' ', 4, 3) == "3 "
|
|
// show_fill_right<int>(' ', 4, 12345) == "12345"
|
|
template <typename T>
|
|
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<T>(x));
|
|
}
|
|
|
|
// Based on https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat
|
|
// Case N, recursive
|
|
template<class Tuple, std::size_t N>
|
|
struct TupleStreamer {
|
|
static void stream(const Tuple& t, std::list<std::string>& sl)
|
|
{
|
|
TupleStreamer<Tuple, N-1>::stream(t,sl);
|
|
std::stringstream ss;
|
|
ss << std::get<N-1>(t);
|
|
sl.emplace_back(ss.str());
|
|
}
|
|
};
|
|
|
|
// Based on https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat
|
|
// Case N=1
|
|
template<class Tuple>
|
|
struct TupleStreamer<Tuple, 1> {
|
|
static void stream(const Tuple& t, std::list<std::string>& 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<typename... Args, std::enable_if_t<sizeof...(Args) == 0, int> = 0>
|
|
void stream(const std::tuple<Args...>& , std::list<std::string>& )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Based on https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat
|
|
// Example:
|
|
// std::tuple<int, std::string, float> t1(10, "Test", 3.14);
|
|
// std::list<std::string> lt1 = stream(t1);
|
|
// std::cout << fplus::show_cont(lt1);
|
|
template<typename... Args, std::enable_if_t<sizeof...(Args) != 0, int> = 0>
|
|
std::list<std::string> stream(const std::tuple<Args...>& t)
|
|
{
|
|
std::list<std::string> sl;
|
|
TupleStreamer<decltype(t), sizeof...(Args)>::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 <cctype>
|
|
#include <string>
|
|
#include <locale>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
// API search type: is_letter_or_digit : Char -> Bool
|
|
// fwd bind count: 0
|
|
// Is character alphanumerical?
|
|
template <typename String>
|
|
bool is_letter_or_digit(const typename String::value_type& c)
|
|
{
|
|
return
|
|
std::isdigit(static_cast<unsigned char>(c)) ||
|
|
std::isalpha(static_cast<unsigned char>(c));
|
|
}
|
|
|
|
// API search type: is_whitespace : Char -> Bool
|
|
// fwd bind count: 0
|
|
// Is character a whitespace.
|
|
template <typename String>
|
|
bool is_whitespace(const typename String::value_type& c)
|
|
{
|
|
return (c == 32 || is_in_interval(9, 14, static_cast<int>(c)));
|
|
}
|
|
|
|
// API search type: is_line_break : Char -> Bool
|
|
// fwd bind count: 0
|
|
// Newline character ('\n')?
|
|
template <typename String>
|
|
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 <typename String>
|
|
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 <typename String, typename ContainerOut = std::vector<String>>
|
|
ContainerOut split_words(const bool allowEmpty, const String& str)
|
|
{
|
|
return split_by(logical_not(is_letter_or_digit<String>), 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 <typename String, typename ContainerOut = std::vector<String>>
|
|
ContainerOut split_lines(bool allowEmpty, const String& str)
|
|
{
|
|
return split_by(is_line_break<String>, allowEmpty, clean_newlines(str));
|
|
}
|
|
|
|
// API search type: trim_whitespace_left : String -> String
|
|
// fwd bind count: 0
|
|
// trim_whitespace_left(" text ") == "text "
|
|
template <typename String>
|
|
String trim_whitespace_left(const String& str)
|
|
{
|
|
return drop_while(is_whitespace<String>, 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 <typename String>
|
|
String trim_whitespace_right(const String& str)
|
|
{
|
|
return trim_right_by(is_whitespace<String>, 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 <typename String>
|
|
String trim_whitespace(const String& str)
|
|
{
|
|
return trim_by(is_whitespace<String>, 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 <typename String>
|
|
String to_lower_case(const String& str)
|
|
{
|
|
typedef typename String::value_type Char;
|
|
return transform([](Char c) -> Char
|
|
{
|
|
return static_cast<Char>(
|
|
std::tolower(static_cast<unsigned char>(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 <typename String>
|
|
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<Char>(
|
|
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 <typename String>
|
|
String to_upper_case(const String& str)
|
|
{
|
|
typedef typename String::value_type Char;
|
|
return transform([](Char c) -> Char
|
|
{
|
|
return static_cast<Char>(
|
|
std::toupper(static_cast<unsigned char>(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 <typename String>
|
|
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<Char>(
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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 <vector>
|
|
#include <queue>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
template <typename T>
|
|
struct tree
|
|
{
|
|
tree (const T& value, const std::vector<tree<T>>& children) :
|
|
value_(value), children_(children) {}
|
|
T value_;
|
|
std::vector<tree<T>> children_;
|
|
};
|
|
|
|
namespace internal
|
|
{
|
|
template <typename T>
|
|
tree<T> make_singleton_tree(const T& x)
|
|
{
|
|
return {x, {}};
|
|
}
|
|
} // namespace internal
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename BinaryPredicate, typename T>
|
|
std::vector<tree<T>> presort_trees(BinaryPredicate tree_is_child_of,
|
|
std::vector<tree<T>> xs_orig)
|
|
{
|
|
auto xs = fplus::convert_container<std::list<tree<T>>>(xs_orig);
|
|
std::vector<tree<T>> 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 <typename BinaryPredicate, typename TreeCont> // 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 <typename BinaryPredicate, typename Container> // todo: name?
|
|
std::vector<tree<typename Container::value_type>> trees_from_sequence(
|
|
BinaryPredicate is_child_of, const Container& xs)
|
|
{
|
|
internal::check_binary_predicate_for_container<BinaryPredicate, Container>();
|
|
typedef typename Container::value_type T;
|
|
typedef tree<T> Tree;
|
|
const auto singletons = transform_convert<std::vector<Tree>>(
|
|
internal::make_singleton_tree<T>, xs);
|
|
const auto tree_is_child_of =
|
|
[is_child_of](const tree<T>& a, const tree<T>& 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 <typename T>
|
|
int tree_cmp(const tree<T>& a, const tree<T>& b)
|
|
{
|
|
if(a.value_ < b.value_)
|
|
{
|
|
return -1;
|
|
}
|
|
else if(b.value_ < a.value_)
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
const auto results = zip_with(tree_cmp<T>,
|
|
sort_by(tree_cmp<T>, a.children_),
|
|
sort_by(tree_cmp<T>, b.children_));
|
|
return just_with_default(0, find_first_by(
|
|
bind_1st_of_2(is_not_equal<int>, 0),
|
|
results));
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
bool tree_less(const tree<T>& a, const tree<T>& b)
|
|
{
|
|
return tree_cmp(a, b) < 0;
|
|
}
|
|
|
|
} // namespace internal
|
|
|
|
namespace internal
|
|
{
|
|
|
|
template <typename T>
|
|
bool are_normalized_trees_equal(const tree<T>& a, const tree<T>& b)
|
|
{
|
|
if (a.value_ != b.value_ || a.children_.size() != b.children_.size())
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return all(zip_with(are_normalized_trees_equal<T>,
|
|
a.children_, b.children_));
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
tree<T> normalize_tree(tree<T> x)
|
|
{
|
|
x.children_ = sort_by(
|
|
internal::tree_less<T>,
|
|
transform(normalize_tree<T>, x.children_));
|
|
return x;
|
|
}
|
|
|
|
} // namespace internal
|
|
|
|
// API search type: are_trees_equal : (Tree a, Tree a) -> Bool
|
|
// fwd bind count: 1
|
|
template <typename T>
|
|
bool are_trees_equal(const tree<T>& a, const tree<T>& 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 <typename T>
|
|
std::size_t tree_size(const tree<T>& x)
|
|
{
|
|
return 1 + sum(transform(tree_size<T>, 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 <typename T>
|
|
std::size_t tree_depth(const tree<T>& x)
|
|
{
|
|
return 1 + just_with_default<std::size_t>(0,
|
|
maximum_maybe(transform(tree_depth<T>, x.children_)));
|
|
}
|
|
|
|
// API search type: flatten_tree_depth_first : Tree a -> [a]
|
|
// fwd bind count: 0
|
|
template <typename T>
|
|
std::vector<T> flatten_tree_depth_first(const tree<T>& x)
|
|
{
|
|
return prepend_elem(x.value_,
|
|
transform_and_concat(flatten_tree_depth_first<T>, x.children_));
|
|
}
|
|
|
|
// API search type: flatten_tree_breadth_first : Tree a -> [a]
|
|
// fwd bind count: 0
|
|
template <typename T>
|
|
std::vector<T> flatten_tree_breadth_first(const tree<T>& x)
|
|
{
|
|
std::vector<T> result;
|
|
result.reserve(tree_size(x));
|
|
std::queue<const tree<T>*> 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 <atomic>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <cstdint>
|
|
#include <fstream>
|
|
#include <functional>
|
|
#include <future>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <streambuf>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
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<void(std::int64_t)> 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<std::mutex> lock(control_mutex_);
|
|
return is_running_;
|
|
}
|
|
bool start()
|
|
{
|
|
std::lock_guard<std::mutex> 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<std::mutex> 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<std::chrono::microseconds>(
|
|
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<void()> 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<void()> 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<void()> 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 <typename Container>
|
|
auto execute_serially(const Container& effs)
|
|
{
|
|
using Effect = typename Container::value_type;
|
|
using Result = internal::invoke_result_t<Effect>;
|
|
|
|
return [effs]
|
|
{
|
|
std::vector<std::decay_t<Result>> 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 <typename Container>
|
|
auto execute_serially_until_success(const Container& effs)
|
|
{
|
|
using Effect = typename Container::value_type;
|
|
using Result = internal::invoke_result_t<Effect>;
|
|
static_assert(std::is_convertible<Result, bool>::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 <typename Result, typename Effect>
|
|
std::function<Result()> 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 <typename Effect>
|
|
std::function<internal::invoke_result_t<Effect> ()> 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 <typename Effect>
|
|
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<typename Effect>
|
|
auto execute_n_times(std::size_t n, const Effect& eff)
|
|
{
|
|
for (auto _ : fplus::numbers(static_cast<size_t>(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<typename F, typename Container>
|
|
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<typename F, typename Container>
|
|
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<typename F, typename Container>
|
|
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 <typename Container>
|
|
std::function<bool()> execute_serially_until_failure(const Container& effs)
|
|
{
|
|
using Effect = typename Container::value_type;
|
|
using Result = internal::invoke_result_t<Effect>;
|
|
static_assert(std::is_convertible<Result, bool>::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 <typename Container>
|
|
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 <typename Container>
|
|
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 <typename Effect>
|
|
std::function<void()> 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<maybe<std::string>()> read_text_file_maybe(
|
|
const std::string& filename)
|
|
{
|
|
return [filename]() -> maybe<std::string>
|
|
{
|
|
std::ifstream input(filename);
|
|
if (!input.good())
|
|
return {};
|
|
return just(std::string(
|
|
std::istreambuf_iterator<std::string::value_type>(input),
|
|
std::istreambuf_iterator<std::string::value_type>()));
|
|
};
|
|
}
|
|
|
|
// 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<std::string()> 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<maybe<std::vector<std::uint8_t>>()> read_binary_file_maybe(
|
|
const std::string& filename)
|
|
{
|
|
return [filename]() -> maybe<std::vector<std::uint8_t>>
|
|
{
|
|
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<std::streamsize>(0))
|
|
return {};
|
|
file.seekg(0, std::ios::beg);
|
|
std::vector<std::uint8_t> vec(static_cast<std::size_t>(fileSize), 0);
|
|
file.read(reinterpret_cast<char*>(&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<std::vector<std::uint8_t>()> read_binary_file(
|
|
const std::string& filename)
|
|
{
|
|
return [filename]() -> std::vector<std::uint8_t>
|
|
{
|
|
return just_with_default(
|
|
std::vector<std::uint8_t>(),
|
|
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<maybe<std::vector<std::string>>()> read_text_file_lines_maybe(
|
|
bool allow_empty, const std::string& filename)
|
|
{
|
|
return [filename, allow_empty]() -> maybe<std::vector<std::string>>
|
|
{
|
|
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<std::vector<std::string>()> read_text_file_lines(
|
|
bool allow_empty, const std::string& filename)
|
|
{
|
|
return [filename, allow_empty]() -> std::vector<std::string>
|
|
{
|
|
return just_with_default(
|
|
std::vector<std::string>(),
|
|
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<bool()> 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<bool()> write_binary_file(const std::string& filename,
|
|
const std::vector<uint8_t>& content)
|
|
{
|
|
return [filename, content]() -> bool
|
|
{
|
|
std::ofstream file(filename, std::ios::binary);
|
|
file.write(reinterpret_cast<const char*>(&content[0]),
|
|
static_cast<std::streamsize>(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<bool()> write_text_file_lines(bool trailing_newline,
|
|
const std::string& filename,
|
|
const std::vector<std::string>& 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 <typename F>
|
|
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 <typename F>
|
|
std::function<void()> interact(F f)
|
|
{
|
|
return [f]() -> void
|
|
{
|
|
std::cout << f(std::string(
|
|
std::istreambuf_iterator<char>(std::cin.rdbuf()),
|
|
std::istreambuf_iterator<char>()));
|
|
};
|
|
}
|
|
|
|
// 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 <typename Effect, typename X>
|
|
std::function<bool()> execute_with_maybe(Effect eff, const maybe<X>& 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 <chrono>
|
|
|
|
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<second>
|
|
(clock::now() - beg_).count(); }
|
|
private:
|
|
typedef std::chrono::high_resolution_clock clock;
|
|
typedef std::chrono::duration<double, std::ratio<1>> second;
|
|
std::chrono::time_point<clock> 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 <iostream>
|
|
#include <memory>
|
|
#include <tuple>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
namespace internal
|
|
{
|
|
|
|
// http://stackoverflow.com/a/18987405/1866775
|
|
|
|
template <typename...>
|
|
struct is_one_of;
|
|
|
|
template <typename F>
|
|
struct is_one_of<F>
|
|
{
|
|
static constexpr bool value = false;
|
|
};
|
|
|
|
template <typename F, typename S, typename... T>
|
|
struct is_one_of<F, S, T...>
|
|
{
|
|
static constexpr bool value = std::is_same<F, S>::value
|
|
|| is_one_of<F, T...>::value;
|
|
};
|
|
|
|
template <typename F, typename... T>
|
|
struct is_one_of<F, std::tuple<T...>>
|
|
{
|
|
static constexpr bool value = is_one_of<F, T...>::value;
|
|
};
|
|
|
|
template <typename...>
|
|
struct is_unique;
|
|
|
|
template <>
|
|
struct is_unique<> {
|
|
static constexpr bool value = true;
|
|
};
|
|
|
|
template<typename F, typename... T>
|
|
struct is_unique<F, T...>
|
|
{
|
|
static constexpr bool value = is_unique<T...>::value
|
|
&& !is_one_of<F, T...>::value;
|
|
};
|
|
|
|
template<typename... T>
|
|
struct is_unique<std::tuple<T...>>
|
|
{
|
|
static constexpr bool value = is_unique<T...>::value;
|
|
};
|
|
|
|
template <typename...>
|
|
struct are_same;
|
|
|
|
template <>
|
|
struct are_same<> {
|
|
static constexpr bool value = true;
|
|
};
|
|
|
|
template<typename F1, typename F2>
|
|
struct are_same<F1, F2>
|
|
{
|
|
static constexpr bool value = std::is_same<F1, F2>::value;
|
|
};
|
|
|
|
template<typename F1, typename F2, typename... T>
|
|
struct are_same<F1, F2, T...>
|
|
{
|
|
static constexpr bool value = are_same<F2, T...>::value
|
|
&& std::is_same<F1, F2>::value;
|
|
};
|
|
|
|
template<typename... T>
|
|
struct are_same<std::tuple<T...>>
|
|
{
|
|
static constexpr bool value = are_same<T...>::value;
|
|
};
|
|
|
|
// http://stackoverflow.com/a/3273571/1866775
|
|
template<template<typename...> class List,
|
|
template<typename> class Mod,
|
|
typename ...Args>
|
|
struct transform_parameter_pack {
|
|
typedef List<typename Mod<Args>::type...> type;
|
|
};
|
|
|
|
template<typename T>
|
|
struct as_shared_pointer
|
|
{
|
|
typedef std::shared_ptr<T> type;
|
|
};
|
|
|
|
|
|
|
|
// http://stackoverflow.com/a/27588263/1866775
|
|
|
|
template <typename T, typename... Ts> struct get_index;
|
|
|
|
template <typename T, typename... Ts>
|
|
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
|
|
|
|
template <typename T, typename Tail, typename... Ts>
|
|
struct get_index<T, Tail, Ts...> :
|
|
std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};
|
|
|
|
template <typename T>
|
|
struct get_index<T>
|
|
{
|
|
// condition is always false, but should be dependant of T
|
|
static_assert(sizeof(T) == 0, "element not found");
|
|
};
|
|
|
|
|
|
template <typename T, typename ... Ts>
|
|
struct parameter_pack_head
|
|
{
|
|
typedef T type;
|
|
};
|
|
|
|
|
|
template <typename F>
|
|
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 <typename F>
|
|
struct unary_function_result_type
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 1,
|
|
"Wrong arity.");
|
|
typedef typename function_first_input_type<F>::type T;
|
|
typedef std::decay_t<internal::invoke_result_t<F, T>> type;
|
|
};
|
|
|
|
|
|
// http://stackoverflow.com/a/42493805/1866775
|
|
|
|
template <typename T>
|
|
struct tag { };
|
|
|
|
template <typename... Ts>
|
|
struct type_set_eq_helper: tag<Ts>... { };
|
|
|
|
template <typename, typename, typename = void>
|
|
struct type_set_eq: std::false_type { };
|
|
|
|
template <bool...>
|
|
struct bool_pack { };
|
|
|
|
template <bool... Bs>
|
|
using my_and = std::is_same<bool_pack<Bs..., true>, bool_pack<true, Bs...>>;
|
|
|
|
template <typename... Ts1, typename... Ts2>
|
|
struct type_set_eq<std::tuple<Ts1...>, std::tuple<Ts2...>, typename std::enable_if< (sizeof...(Ts1) == sizeof...(Ts2)) && my_and< std::is_base_of<tag<Ts2>, type_set_eq_helper<Ts1...>>::value... >::value >::type >:
|
|
std::true_type { };
|
|
|
|
|
|
// http://stackoverflow.com/a/42581257/1866775
|
|
|
|
template <typename Lhs, typename Rhs>
|
|
struct is_superset_of;
|
|
|
|
template <typename Tuple, typename T, typename... Ts>
|
|
struct is_superset_of<Tuple, std::tuple<T, Ts...>>
|
|
{
|
|
static const bool value = is_one_of<T, Tuple>::value && is_superset_of<Tuple, std::tuple<Ts...>>::value;
|
|
};
|
|
|
|
template <typename Tuple>
|
|
struct is_superset_of<Tuple, std::tuple<>>
|
|
{
|
|
static const bool value = true;
|
|
};
|
|
|
|
|
|
// http://stackoverflow.com/a/36934374/1866775
|
|
template<bool... bs>
|
|
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
|
|
|
|
} // namespace internal
|
|
|
|
|
|
template<typename ... Types>
|
|
struct variant
|
|
{
|
|
static_assert(internal::is_unique<Types...>::value, "Types must be unique.");
|
|
static_assert(internal::all_true<(!std::is_reference<Types>::value)...>::value, "No reference types allowed.");
|
|
static_assert(internal::all_true<(!std::is_const<Types>::value)...>::value, "No const types allowed.");
|
|
static_assert(sizeof...(Types) >= 1, "Please provide at least one type.");
|
|
|
|
template <typename T>
|
|
variant(const T& val) : shared_ptrs_({})
|
|
{
|
|
std::get<internal::get_index<T, Types...>::value>(shared_ptrs_) =
|
|
std::make_shared<T>(val);
|
|
}
|
|
|
|
template <typename T>
|
|
bool is() const
|
|
{
|
|
static_assert(
|
|
internal::is_one_of<T, Types...>::value
|
|
, "Type must match one possible variant type.");
|
|
|
|
const auto ptr =
|
|
std::get<internal::get_index<T, Types...>::value>(shared_ptrs_);
|
|
|
|
return static_cast<bool>(ptr);
|
|
}
|
|
|
|
friend bool operator== (
|
|
const variant<Types...>& a, const variant<Types...>& b)
|
|
{
|
|
return a.shared_ptrs_ == b.shared_ptrs_;
|
|
}
|
|
|
|
friend bool operator!= (
|
|
const variant<Types...>& a, const variant<Types...>& b)
|
|
{
|
|
return a.shared_ptrs_ != b.shared_ptrs_;
|
|
}
|
|
|
|
template <typename F>
|
|
auto visit_one(F f) const
|
|
{
|
|
using T = typename internal::function_first_input_type<F>::type;
|
|
using Ret = internal::invoke_result_t<F, T>;
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, T>();
|
|
|
|
static_assert(
|
|
internal::is_one_of<
|
|
typename internal::function_first_input_type<F>::type,
|
|
Types...>::value
|
|
, "Function input must match one variant type.");
|
|
|
|
static_assert(!std::is_same<std::decay_t<Ret>, void>::value,
|
|
"Function must return non-void type.");
|
|
|
|
const auto ptr =
|
|
std::get<internal::get_index<T, Types...>::value>(shared_ptrs_);
|
|
|
|
if (ptr)
|
|
{
|
|
return just(internal::invoke(f, *ptr));
|
|
}
|
|
|
|
return nothing<std::decay_t<Ret>>();
|
|
}
|
|
|
|
template <typename ...Fs>
|
|
auto visit(Fs ... fs) const ->
|
|
typename internal::unary_function_result_type<
|
|
typename internal::parameter_pack_head<Fs...>::type>::type
|
|
{
|
|
typedef typename internal::unary_function_result_type<
|
|
typename internal::parameter_pack_head<Fs...>::type>::type
|
|
Res;
|
|
|
|
static_assert(
|
|
sizeof...(Fs) >= std::tuple_size<shared_ptr_pack>::value,
|
|
"Too few functions provided.");
|
|
|
|
static_assert(
|
|
sizeof...(Fs) <= std::tuple_size<shared_ptr_pack>::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<function_first_input_types_tuple>::value,
|
|
"Only one function per input type allowed.");
|
|
|
|
static_assert(
|
|
internal::are_same<return_types_tuple>::value,
|
|
"All Functions must return the same type.");
|
|
|
|
static_assert(
|
|
internal::type_set_eq<function_first_input_types_tuple, std::tuple<Types...>>::value,
|
|
"Functions do not cover all possible types.");
|
|
|
|
const auto results = justs(visit_helper<Res>(fs...));
|
|
assert(size_of_cont(results) == 1);
|
|
return head(results);
|
|
}
|
|
|
|
template <typename ...Fs>
|
|
variant<Types...> transform(Fs ... fs) const
|
|
{
|
|
static_assert(
|
|
sizeof...(Fs) >= std::tuple_size<shared_ptr_pack>::value,
|
|
"Too few functions provided.");
|
|
|
|
static_assert(
|
|
sizeof...(Fs) <= std::tuple_size<shared_ptr_pack>::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<function_first_input_types_tuple, std::tuple<Types...>>::value,
|
|
"Functions do not cover all possible types.");
|
|
|
|
static_assert(
|
|
internal::is_superset_of<std::tuple<Types...>, return_types_tuple>::value,
|
|
"All Functions must return a possible variant type.");
|
|
|
|
return visit(fs...);
|
|
}
|
|
|
|
private:
|
|
template <typename Res, typename F>
|
|
std::vector<fplus::maybe<Res>> visit_helper(F f) const
|
|
{
|
|
return {visit_one(f)};
|
|
}
|
|
|
|
template <typename Res, typename F, typename ...Fs>
|
|
std::vector<fplus::maybe<Res>> visit_helper(F f, Fs ... fs) const
|
|
{
|
|
return fplus::append(visit_helper<Res>(f), visit_helper<Res>(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 <chrono>
|
|
#include <type_traits>
|
|
|
|
#include <cassert>
|
|
#include <exception>
|
|
#include <functional>
|
|
#include <memory>
|
|
|
|
namespace fplus
|
|
{
|
|
using ExecutionTime = double; // in seconds
|
|
|
|
// Holds a value of type T plus an execution time
|
|
template <typename T>
|
|
class timed : public std::pair<T, ExecutionTime>
|
|
{
|
|
using base_pair = std::pair<T, ExecutionTime>;
|
|
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<double>
|
|
std::chrono::duration<double, std::ratio<1>> duration_in_s() const
|
|
{
|
|
return std::chrono::duration<double, std::ratio<1>>(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 <typename T>
|
|
std::string show_timed(const fplus::timed<T>& v)
|
|
{
|
|
std::string result =
|
|
fplus::show(v.get()) + " (" + fplus::show(v.time_in_s() * 1000.) + "ms)";
|
|
return result;
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
template<typename Fn>
|
|
class timed_function_impl
|
|
{
|
|
public:
|
|
explicit timed_function_impl(Fn fn) : _fn(fn) {};
|
|
template<typename ...Args> auto operator()(Args&&... args)
|
|
{
|
|
return _timed_result(std::forward<Args>(args)...);
|
|
}
|
|
|
|
private:
|
|
template<typename ...Args>
|
|
auto _timed_result(Args&&... args)
|
|
{
|
|
fplus::stopwatch timer;
|
|
auto r = _fn(std::forward<Args>(args)...);
|
|
auto r_t = fplus::timed<decltype(r)>(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<int>;
|
|
// 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<class Fn>
|
|
auto make_timed_function(Fn f)
|
|
{
|
|
return internal::timed_function_impl<decltype(f)>(f);
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
template<typename Fn>
|
|
class timed_void_function_impl
|
|
{
|
|
public:
|
|
explicit timed_void_function_impl(Fn fn) : _fn(fn) {};
|
|
template<typename ...Args> auto operator()(Args&&... args)
|
|
{
|
|
return _timed_result(std::forward<Args>(args)...);
|
|
}
|
|
|
|
private:
|
|
template<typename ...Args>
|
|
auto _timed_result(Args&&... args)
|
|
{
|
|
fplus::stopwatch timer;
|
|
_fn(std::forward<Args>(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<class Fn>
|
|
auto make_timed_void_function(Fn f)
|
|
{
|
|
return internal::timed_void_function_impl<decltype(f)>(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 <vector>
|
|
#include <mutex>
|
|
|
|
|
|
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<FunctionName, benchmark_function_report> & 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<FunctionName, benchmark_function_report> report_list() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(functions_times_mutex_);
|
|
std::map<FunctionName, benchmark_function_report> 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<std::mutex> lock(functions_times_mutex_);
|
|
functions_times_[function_name].push_back(time);
|
|
}
|
|
|
|
private:
|
|
benchmark_function_report make_bench_report(
|
|
const std::vector<ExecutionTime> & times) const
|
|
{
|
|
benchmark_function_report result;
|
|
result.nb_calls = times.size();
|
|
auto mean_and_dev = fplus::mean_stddev<double>(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<FunctionName, std::vector<ExecutionTime>> functions_times_;
|
|
};
|
|
|
|
namespace internal
|
|
{
|
|
template<typename Fn>
|
|
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<typename ...Args> auto operator()(Args&&... args)
|
|
{
|
|
return _bench_result(std::forward<Args>(args)...);
|
|
}
|
|
|
|
private:
|
|
template<typename ...Args>
|
|
auto _bench_result(Args&&... args)
|
|
{
|
|
fplus::stopwatch timer;
|
|
auto r = fn_(std::forward<Args>(args)...);
|
|
benchmark_session_.store_one_time(function_name_, timer.elapsed());
|
|
return r;
|
|
}
|
|
|
|
benchmark_session & benchmark_session_;
|
|
FunctionName function_name_;
|
|
Fn fn_;
|
|
};
|
|
|
|
template<typename Fn>
|
|
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<typename ...Args> auto operator()(Args&&... args)
|
|
{
|
|
_bench_result(std::forward<Args>(args)...);
|
|
}
|
|
|
|
private:
|
|
template<typename ...Args>
|
|
auto _bench_result(Args&&... args)
|
|
{
|
|
fplus::stopwatch timer;
|
|
fn_(std::forward<Args>(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<class Fn>
|
|
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<Fn>(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<class Fn>
|
|
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<Fn>(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<std::vector<std::string>>& rows)
|
|
{
|
|
if (rows.empty() || rows[0].empty())
|
|
return "";
|
|
|
|
const std::vector<std::size_t> columns_width = [&]() {
|
|
auto string_size = [](const std::string & s) -> std::size_t { return s.size(); };
|
|
auto largest_string_size = [&](const std::vector<std::string> & 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<std::string, std::size_t> & 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<std::string> & 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<FunctionName, benchmark_function_report> > make_ordered_reports(
|
|
const std::map<FunctionName, benchmark_function_report> & 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<FunctionName, benchmark_function_report> & 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<std::string> 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<std::string> 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<decltype(fplus_curry_p1)>(fplus_curry_p1)); \
|
|
}; \
|
|
}
|
|
|
|
#define fplus_curry_define_fn_1(fplus_curry_define_fn_1_name) \
|
|
template <typename P1> \
|
|
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<decltype(fplus_curry_p2)>(fplus_curry_p2)); \
|
|
}; \
|
|
}
|
|
|
|
#define fplus_curry_define_fn_2(fplus_curry_define_fn_2_name) \
|
|
template <typename P1> \
|
|
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<decltype(fplus_curry_p3)>(fplus_curry_p3)); \
|
|
}; \
|
|
}; \
|
|
}
|
|
|
|
#define fplus_curry_define_fn_3(fplus_curry_define_fn_3_name) \
|
|
template <typename P1> \
|
|
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<decltype(fplus_curry_p4)>(fplus_curry_p4)); \
|
|
}; \
|
|
}; \
|
|
}; \
|
|
}
|
|
|
|
#define fplus_curry_define_fn_4(fplus_curry_define_fn_4_name) \
|
|
template <typename P1> \
|
|
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<decltype(fplus_curry_p5)>(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<decltype(fplus_fwd_x)>(fplus_fwd_x)); \
|
|
}; \
|
|
}
|
|
|
|
#define fplus_fwd_define_fn_1(fplus_fwd_define_fn_1_name) \
|
|
template <typename P1> \
|
|
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<decltype(fplus_fwd_x)>(fplus_fwd_x)); \
|
|
}; \
|
|
}
|
|
|
|
#define fplus_fwd_define_fn_2(fplus_fwd_define_fn_2_name) \
|
|
template <typename P1, typename P2> \
|
|
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<decltype(fplus_fwd_x)>(fplus_fwd_x)); \
|
|
}; \
|
|
}
|
|
|
|
#define fplus_fwd_define_fn_3(fplus_fwd_define_fn_3_name) \
|
|
template <typename P1, typename P2, typename P3> \
|
|
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<decltype(fplus_fwd_x)>(fplus_fwd_x)); \
|
|
}; \
|
|
}
|
|
|
|
#define fplus_fwd_define_fn_4(fplus_fwd_define_fn_4_name) \
|
|
template <typename P1, typename P2, typename P3, typename P4> \
|
|
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<decltype(fplus_fwd_x)>(fplus_fwd_x)); \
|
|
}; \
|
|
}
|
|
|
|
|
|
#define fplus_fwd_flip_define_fn_1(fplus_fwd_flip_define_fn_1_name) \
|
|
namespace flip \
|
|
{ \
|
|
template <typename P2> \
|
|
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<decltype(fplus_fwd_flip_x)>(fplus_fwd_flip_x), p2); \
|
|
}; \
|
|
} \
|
|
} // namespace flip
|
|
|
|
|
|
namespace internal
|
|
{
|
|
template<typename F, typename G>
|
|
struct compose_helper{
|
|
compose_helper(F f, G g) : f_(f), g_(g) {}
|
|
template<typename X>
|
|
decltype(auto) operator()(X&& x) const
|
|
{
|
|
return g_(f_(std::forward<X>(x)));
|
|
}
|
|
private:
|
|
F f_;
|
|
G g_;
|
|
};
|
|
} // namespace internal
|
|
template<typename F, typename G>
|
|
auto compose(F f, G g) {
|
|
return internal::compose_helper<F, G> {f, g};
|
|
}
|
|
template<typename F1, typename... Fs>
|
|
auto compose(F1 f, Fs ... args)
|
|
{
|
|
return compose(f, compose(args...));
|
|
}
|
|
|
|
template<typename X, typename... Fs>
|
|
auto apply(X&& x, Fs ... args)
|
|
{
|
|
return compose(args...)(std::forward<X>(x));
|
|
}
|
|
template<typename X, typename F>
|
|
auto apply(X&& x, F f)
|
|
{
|
|
return f(std::forward<X>(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
|