Skip to content

Instantly share code, notes, and snippets.

@Dobiasd
Created December 27, 2023 17:23
Show Gist options
  • Save Dobiasd/8a1cf9d52532495ebf03d1ef2d876ddd to your computer and use it in GitHub Desktop.
Save Dobiasd/8a1cf9d52532495ebf03d1ef2d876ddd to your computer and use it in GitHub Desktop.
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <fplus/compare.hpp>
#include <fplus/composition.hpp>
#include <fplus/container_traits.hpp>
#include <fplus/maybe.hpp>
#include <fplus/internal/container_common.hpp>
#include <fplus/internal/invoke.hpp>
#include <fplus/internal/meta.hpp>
#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_;
};
} // namespace internal
// 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
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <fplus/compare.hpp>
#include <fplus/composition.hpp>
#include <fplus/container_traits.hpp>
#include <fplus/maybe.hpp>
#include <fplus/internal/container_common.hpp>
#include <fplus/internal/invoke.hpp>
#include <fplus/internal/meta.hpp>
#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
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <fplus/compare.hpp>
#include <fplus/composition.hpp>
#include <fplus/container_traits.hpp>
#include <fplus/internal/container_common.hpp>
#include <fplus/internal/invoke.hpp>
#include <fplus/internal/meta.hpp>
#include <fplus/maybe.hpp>
#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_;
};
} // namespace internal
// 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
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <fplus/compare.hpp>
#include <fplus/composition.hpp>
#include <fplus/container_traits.hpp>
#include <fplus/maybe.hpp>
#include <fplus/internal/container_common.hpp>
#include <fplus/internal/invoke.hpp>
#include <fplus/internal/meta.hpp>
#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_;
};
} // namespace internal
// 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
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <fplus/compare.hpp>
#include <fplus/composition.hpp>
#include <fplus/container_traits.hpp>
#include <fplus/maybe.hpp>
#include <fplus/internal/container_common.hpp>
#include <fplus/internal/invoke.hpp>
#include <fplus/internal/meta.hpp>
#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_;
};
} // namespace internal
// 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
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <fplus/compare.hpp>
#include <fplus/composition.hpp>
#include <fplus/container_traits.hpp>
#include <fplus/maybe.hpp>
#include <fplus/internal/container_common.hpp>
#include <fplus/internal/invoke.hpp>
#include <fplus/internal/meta.hpp>
#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
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <fplus/compare.hpp>
#include <fplus/composition.hpp>
#include <fplus/container_traits.hpp>
#include <fplus/maybe.hpp>
#include <fplus/internal/container_common.hpp>
#include <fplus/internal/invoke.hpp>
#include <fplus/internal/meta.hpp>
#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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment