diff --git a/source/simple/support/algorithm.hpp b/source/simple/support/algorithm.hpp index fcd26d880293e19d162a049d408c1c3af60ae486..4d3640cfad8cbcf7b8a96d5ab16618fffee34827 100644 --- a/source/simple/support/algorithm.hpp +++ b/source/simple/support/algorithm.hpp @@ -1,528 +1,6 @@ -#ifndef SIMPLE_SUPPORT_ALGORITHM_HPP -#define SIMPLE_SUPPORT_ALGORITHM_HPP -#include <cassert> -#include <cmath> -#include <iterator> -#include <algorithm> - -#include "range.hpp" -#include "arithmetic.hpp" - -namespace simple::support -{ - - namespace detail - { - - class increment_step - { - public: - void operator*() const noexcept; - void operator++() const noexcept; - }; - - class null_lower_iterator - { - public: - void operator*() const noexcept; - void operator++() const noexcept; - }; - - } // namespace detail - - template <typename Itr, typename LowerItr, typename UpperItr, typename StepItr = detail::increment_step> - constexpr auto advance_vector(Itr begin, Itr end, - LowerItr lower_begin, UpperItr upper_begin, - StepItr step_begin = detail::increment_step{}) - -> std::remove_reference_t<decltype - ( - *begin, ++begin, - *lower_begin, ++lower_begin, - *upper_begin, ++upper_begin, - *step_begin, ++step_begin, - begin - )> - { - using value_type = std::remove_reference_t<decltype(*begin)>; - constexpr bool increment_step = std::is_same_v<StepItr, detail::increment_step>; - constexpr bool null_lower_iterator = std::is_same_v<LowerItr, detail::null_lower_iterator>; - // TODO: do while loop can be more optimal for most use cases, but burdens the user - while(begin != end) - { - if constexpr (increment_step) - ++(*begin); - else - *begin += *step_begin; - - if(*begin < *upper_begin) - break; - if constexpr (null_lower_iterator) - *begin = value_type{}; - else - *begin = *lower_begin; - - ++begin; - ++upper_begin; - if constexpr (!null_lower_iterator) - ++lower_begin; - if constexpr (!increment_step) - ++step_begin; - } - return begin; - } - - template <typename Itr, typename UpperItr> - constexpr auto advance_vector(Itr begin, Itr end, UpperItr upper_begin) -> std::remove_reference_t<decltype - ( - *begin, ++begin, - *upper_begin, ++upper_begin, - begin - )> - { - return advance_vector(begin, end, detail::null_lower_iterator{}, upper_begin, detail::increment_step{}); - } - - template <typename T, typename = std::nullptr_t> - struct is_range - : public std::false_type {}; - - template <typename T> - struct is_range<T, decltype( - std::begin(std::declval<T>()), - std::end(std::declval<T>()), - nullptr)> - : public std::true_type {}; - - template <typename T> - constexpr auto is_range_v = is_range<T>::value; - - template <typename Range, std::enable_if_t<is_range_v<Range>>* = nullptr> - constexpr auto distance(Range&& range) - { - return std::distance(std::begin(range), std::end(range)); - } - - template <typename Range, typename LowerRange, typename UpperRange, - std::enable_if_t< - is_range_v<Range> && - is_range_v<LowerRange> && - is_range_v<UpperRange> - >* = nullptr - > - constexpr auto advance_vector(Range& range, LowerRange lower, UpperRange upper) - { - assert(distance(range) == distance(upper)); - assert(distance(range) == distance(lower)); - return advance_vector(std::begin(range), std::end(range), std::begin(lower), std::begin(upper)); - } - - template <typename Range, typename BoundsRange, typename StepRange, - std::enable_if_t<is_range_v<Range> && is_range_v<BoundsRange> && is_range_v<StepRange>>* = nullptr> - constexpr auto advance_vector(Range& range, BoundsRange lower, BoundsRange upper, StepRange step) - { - assert(distance(range) == distance(upper)); - assert(distance(range) == distance(lower)); - assert(distance(range) == distance(step)); - return advance_vector(std::begin(range), std::end(range), std::begin(lower), std::begin(upper), std::begin(step)); - } - - template <typename Range, typename UpperRange, - std::enable_if_t< - is_range_v<Range> && - is_range_v<UpperRange> - >* = nullptr - > - constexpr auto advance_vector(Range& range, UpperRange upper) - { - assert(distance(range) == distance(upper)); - return advance_vector(std::begin(range), std::end(range), std::begin(upper)); - } - - - // TODO: next number can be implemented using advance_vector, wrapping the constants base in iterators, - // but i'm not sure about it, would it optimize well?? - template <typename Itr, typename Num = int> - constexpr Itr next_number(Itr begin, Itr end, Num base = 2) - { - while(begin != end) - { - ++(*begin); - if(*begin < base) - break; - *begin = 0; - ++begin; - } - return begin; - } - - template <typename Range, typename Num = int> - constexpr auto next_number(Range& range, Num base = 2) - { - using std::begin; - using std::end; - return next_number(begin(range), end(range), base); - } - - template <typename Itr, typename Num = int> - constexpr Itr prev_number(Itr begin, Itr end, Num base = 2) - { - while(begin != end) - { - --(*begin); - if(*begin >= 0) - break; - *begin = base - 1; - ++begin; - } - return begin; - } - - template <typename Range, typename Num = int> - constexpr auto prev_number(Range& range, Num base = 2) - { - using std::begin; - using std::end; - return prev_number(begin(range), end(range), base); - } - - - template <typename Container> - [[nodiscard]] - constexpr auto make_range(Container& container) - { - using std::begin; - using std::end; - return make_range(begin(container), end(container)); - } - - template <typename Container> - constexpr auto make_range(Container&& container) = delete; - - template <typename Container> - [[nodiscard]] - constexpr auto reverse_range(Container& container) - { - using std::rbegin; - using std::rend; - return make_range(rbegin(container), rend(container)); - } - - template <typename Container> - constexpr auto reverse_range(Container&& container) = delete; - - - template<typename Index = std::size_t, typename Container> - [[nodiscard]] constexpr - auto get_iterator_range(Container&& container, const range<Index>& index_range) - { - return make_range(std::forward<Container>(container)).sub_range(index_range); - } - - template<typename It> - [[nodiscard]] constexpr - auto distance(const range<It>& rng, const It& origin) - { - using std::begin; - using std::end; - return range{ begin(rng) - origin, end(rng) - origin }; - } - - template <typename From, typename To, typename Origin> - [[nodiscard]] constexpr - auto map_range(const From& from, To&& to, Origin origin) - { - return get_iterator_range(to, distance(from, origin)); - } - - template <typename From, typename To> - [[nodiscard]] constexpr - auto map_range(const From& from, const To& to) - { - using std::begin; - return get_iterator_range(to, distance(from, begin(from))); - } - - template <typename Itr, typename BinaryOp> - constexpr Itr variance(Itr begin, Itr end, BinaryOp bop) // TODO: oveloads that accept output iterator[s] and output range, with bound checking - { - auto prev = begin; - while(++begin != end) - { - *prev = bop(*begin, *prev); - prev = begin; - } - return prev; - } - - template <typename Itr> - constexpr Itr variance(Itr begin, Itr end) - { - return variance(begin, end, std::minus{}); - } - - template <typename Range, typename BinaryOp> - constexpr auto variance(Range& range, BinaryOp bop) - { - using std::begin; - using std::end; - return make_range( begin(range), variance(begin(range), end(range), bop) ); - } - - template <typename Range> - constexpr auto variance(Range& range) - { - using std::begin; - using std::end; - return make_range( begin(range), variance(begin(range), end(range)) ); - } - - template <typename Number, - std::enable_if_t<!std::is_floating_point_v<Number>>* = nullptr> - [[nodiscard]] constexpr - Number wrap(Number x, Number upperLimit) - noexcept(noexcept(Number((x + upperLimit) % upperLimit))) - { - return (x + upperLimit) % upperLimit; - } - - template <typename Number, - std::enable_if_t<std::is_floating_point_v<Number>>* = nullptr> - [[nodiscard]] constexpr - Number wrap(Number x, Number upperLimit) - noexcept(noexcept(Number(std::fmod(x + upperLimit, upperLimit)))) - { - return std::fmod(x + upperLimit, upperLimit); - } - - template <typename... Numbers> - [[nodiscard]] constexpr - auto average(Numbers... n) - //TODO: noexcept account for return value and default construction - noexcept(noexcept((n + ...) / int(sizeof...(n)))) - { - using Sum = decltype((n + ...)); - return (n + ...) / (Sum{} + sizeof...(n)); - } - - template <typename Number, typename Ratio> - [[nodiscard]] constexpr - Number way(Number from, Number to, Ratio ratio) - noexcept(noexcept(Number(from + (to - from)*ratio))) - { - return from + (to - from)*ratio; - } - - template <typename Number, typename Ratio> - [[nodiscard]] constexpr - Number wayback(Number from, Number to, Ratio ratio) - noexcept(noexcept(Number(from - (from - to)*ratio))) - { - return from - (from - to)*ratio; - } - - template <typename Number> - [[nodiscard]] constexpr - Number halfway(Number from, Number to) - // TODO: implement in terms of way with rational 1/2 - noexcept(noexcept(Number(from - (from - to)/2))) - { - return from + (to - from)/2; - } - - template <typename Number> - [[nodiscard]] constexpr - Number halfwayback(Number from, Number to) - // TODO: implement in terms of wayback with rational 1/2 - noexcept(noexcept(Number(from - (from - to)/2))) - { - return from - (from - to)/2; - } - - template <typename Integer, - typename Unsigned = std::make_unsigned_t<Integer>> - [[nodiscard]] constexpr - Integer midpoint(Integer a, Integer b) noexcept - // noexcept(noexcept(TODO)) - { - using std::numeric_limits; - constexpr Unsigned abs_int_min = -Unsigned(numeric_limits<Integer>::min()); - constexpr auto int_max = numeric_limits<Integer>::max(); - constexpr auto uint_max = numeric_limits<Unsigned>::max(); - - static_assert(uint_max >= int_max && uint_max - int_max >= abs_int_min, - "Unsigned includes Integer"); - - static_assert(uint_max/2 <= int_max, - "Halfing Unsigned brings it to positive Integer range"); - - static_assert(std::is_unsigned_v<Integer> || int_max <= abs_int_min, - "Can negate a positive Integer"); - - Unsigned diff = Unsigned(b) - Unsigned(a); - - // 2's complement with carry as a sign - Unsigned negative_2x = -diff; // neg - negative_2x /= 2; // div - Integer negative = -Integer(negative_2x); // neg - - Integer positive = diff / 2; - - return a + (b < a ? negative : positive); - - // TODO: this is for geom::vector - // auto overflew = b < a; - // return a + overflew * negative + !overflew * positive; - // better yet introduce a mask function, since intrinsic vector has element-wise ternary - - } - - template <typename Unsigned> - [[nodiscard]] constexpr - Unsigned umidpoint(Unsigned a, Unsigned b) - // noexcept(noexcept(TODO)) - { - - Unsigned diff{}; - auto overflew = sub_overflow(diff,b,a); - - // manual idiv - Unsigned idiff = -diff; // neg - idiff /= Unsigned{2}; // div - idiff = -idiff; // neg - // or... 0 - (0 - diff)/2, this is midpointception! TODO: -_- - // can't use halfwayback here cause promotion ToT - - diff /= 2; - - return a + (overflew ? idiff : diff); - // return a + overflew * idiff + !overflew * diff; - - } - - // std::swap is not constexpr >.< - template <typename T, std::enable_if_t< - std::is_move_constructible_v<T> && - std::is_move_assignable_v<T> - >* = nullptr> - constexpr void swap(T& one, T& other) - noexcept( - std::is_nothrow_move_constructible_v<T> && - std::is_nothrow_move_assignable_v<T> - ) - { - T temp{std::move(other)}; - other = std::move(one); - one = std::move(temp); - } - - // rangey wrappers - - template <typename Range> - constexpr auto min_element(Range& range) - //TODO: noexcept account for return value construction - noexcept(noexcept(std::min_element(std::begin(range), std::end(range)))) - { - return std::min_element(std::begin(range), std::end(range)); - } - - template <typename Range> - constexpr auto min_element(Range&& range) - //TODO: noexcept account for return value construction - noexcept(noexcept(std::min_element(std::begin(range), std::end(range)))) - { - return *std::min_element(std::begin(range), std::end(range)); - } - - template <typename Range> - constexpr auto max_element(Range& range) - //TODO: noexcept account for return value construction - noexcept(noexcept(std::max_element(std::begin(range), std::end(range)))) - { - return std::max_element(std::begin(range), std::end(range)); - } - - template <typename Range> - constexpr auto max_element(Range&& range) - //TODO: noexcept account for return value construction - noexcept(noexcept(std::max_element(std::begin(range), std::end(range)))) - { - return *std::max_element(std::begin(range), std::end(range)); - } - - template <typename Range, typename Compare> - constexpr auto min_element(Range& range, Compare comp) - //TODO: noexcept account for return value construction - noexcept(noexcept(std::min_element(std::begin(range), std::end(range), comp))) - { - return std::min_element(std::begin(range), std::end(range), comp); - } - - template <typename Range, typename Compare> - constexpr auto min_element(Range&& range, Compare comp) - //TODO: noexcept account for return value construction - noexcept(noexcept(std::min_element(std::begin(range), std::end(range), comp))) - { - return *std::min_element(std::begin(range), std::end(range), comp); - } - - template <typename Range, typename Compare> - constexpr auto max_element(Range& range, Compare comp) - //TODO: noexcept account for return value construction - noexcept(noexcept(std::max_element(std::begin(range), std::end(range), comp))) - { - return std::max_element(std::begin(range), std::end(range), comp); - } - - template <typename Range, typename Compare> - constexpr auto max_element(Range&& range, Compare comp) - //TODO: noexcept account for return value construction - noexcept(noexcept(std::max_element(std::begin(range), std::end(range), comp))) - { - return *std::max_element(std::begin(range), std::end(range), comp); - } - - template <typename Range, typename UnaryPredicate> - constexpr auto all_of(const Range& range, UnaryPredicate p) - noexcept(noexcept(std::all_of(std::begin(range), std::end(range), p))) - { - return std::all_of(std::begin(range), std::end(range), p); - } - template <typename ExecutionPolicy, typename Range, typename UnaryPredicate> - constexpr auto all_of(ExecutionPolicy&& policy, const Range& range, UnaryPredicate p) - noexcept(noexcept(std::all_of(std::begin(range), std::end(range), p))) - { - return std::all_of(std::forward<ExecutionPolicy>(policy), - std::begin(range), std::end(range), p); - } - - template <typename Range, typename UnaryPredicate> - constexpr auto any_of(const Range& range, UnaryPredicate p) - noexcept(noexcept(std::any_of(std::begin(range), std::end(range), p))) - { - return std::any_of(std::begin(range), std::end(range), p); - } - template <typename ExecutionPolicy, typename Range, typename UnaryPredicate> - constexpr auto any_of(ExecutionPolicy&& policy, const Range& range, UnaryPredicate p) - noexcept(noexcept(std::any_of(std::begin(range), std::end(range), p))) - { - return std::any_of(std::forward<ExecutionPolicy>(policy), - std::begin(range), std::end(range), p); - } - - template <typename Range, typename UnaryPredicate> - constexpr auto none_of(const Range& range, UnaryPredicate p) - noexcept(noexcept(std::none_of(std::begin(range), std::end(range), p))) - { - return std::none_of(std::begin(range), std::end(range), p); - } - template <typename ExecutionPolicy, typename Range, typename UnaryPredicate> - constexpr auto none_of(ExecutionPolicy&& policy, const Range& range, UnaryPredicate p) - noexcept(noexcept(std::none_of(std::begin(range), std::end(range), p))) - { - return std::none_of(std::forward<ExecutionPolicy>(policy), - std::begin(range), std::end(range), p); - } - -} // namespace simple::support - -#endif /* end of include guard */ +#include "algorithm/advance_vector.hpp" +#include "algorithm/numeric.hpp" +#include "algorithm/range_wrappers.hpp" +#include "algorithm/traits.hpp" +#include "algorithm/utils.hpp" +#include "algorithm/variance.hpp" diff --git a/source/simple/support/algorithm/advance_vector.hpp b/source/simple/support/algorithm/advance_vector.hpp new file mode 100644 index 0000000000000000000000000000000000000000..05efd7d74cd48b5c1b2bfe6185ce4fd94a5ad847 --- /dev/null +++ b/source/simple/support/algorithm/advance_vector.hpp @@ -0,0 +1,172 @@ +#ifndef SIMPLE_SUPPORT_ALGORITHM_ADVANCE_VECTOR_HPP +#define SIMPLE_SUPPORT_ALGORITHM_ADVANCE_VECTOR_HPP +#include "utils.hpp" + +namespace simple::support +{ + + namespace detail + { + + class increment_step + { + public: + void operator*() const noexcept; + void operator++() const noexcept; + }; + + class null_lower_iterator + { + public: + void operator*() const noexcept; + void operator++() const noexcept; + }; + + } // namespace detail + + template <typename Itr, typename LowerItr, typename UpperItr, typename StepItr = detail::increment_step> + constexpr auto advance_vector(Itr begin, Itr end, + LowerItr lower_begin, UpperItr upper_begin, + StepItr step_begin = detail::increment_step{}) + -> std::remove_reference_t<decltype + ( + *begin, ++begin, + *lower_begin, ++lower_begin, + *upper_begin, ++upper_begin, + *step_begin, ++step_begin, + begin + )> + { + using value_type = std::remove_reference_t<decltype(*begin)>; + constexpr bool increment_step = std::is_same_v<StepItr, detail::increment_step>; + constexpr bool null_lower_iterator = std::is_same_v<LowerItr, detail::null_lower_iterator>; + // TODO: do while loop can be more optimal for most use cases, but burdens the user + while(begin != end) + { + if constexpr (increment_step) + ++(*begin); + else + *begin += *step_begin; + + if(*begin < *upper_begin) + break; + if constexpr (null_lower_iterator) + *begin = value_type{}; + else + *begin = *lower_begin; + + ++begin; + ++upper_begin; + if constexpr (!null_lower_iterator) + ++lower_begin; + if constexpr (!increment_step) + ++step_begin; + } + return begin; + } + + template <typename Itr, typename UpperItr> + constexpr auto advance_vector(Itr begin, Itr end, UpperItr upper_begin) -> std::remove_reference_t<decltype + ( + *begin, ++begin, + *upper_begin, ++upper_begin, + begin + )> + { + return advance_vector(begin, end, detail::null_lower_iterator{}, upper_begin, detail::increment_step{}); + } + + template <typename Range, typename LowerRange, typename UpperRange, + std::enable_if_t< + is_range_v<Range> && + is_range_v<LowerRange> && + is_range_v<UpperRange> + >* = nullptr + > + constexpr auto advance_vector(Range& range, LowerRange lower, UpperRange upper) + { + assert(distance(range) == distance(upper)); + assert(distance(range) == distance(lower)); + using std::begin; + using std::end; + return advance_vector(begin(range), end(range), begin(lower), begin(upper)); + } + + template <typename Range, typename BoundsRange, typename StepRange, + std::enable_if_t<is_range_v<Range> && is_range_v<BoundsRange> && is_range_v<StepRange>>* = nullptr> + constexpr auto advance_vector(Range& range, BoundsRange lower, BoundsRange upper, StepRange step) + { + assert(distance(range) == distance(upper)); + assert(distance(range) == distance(lower)); + assert(distance(range) == distance(step)); + using std::begin; + using std::end; + return advance_vector(begin(range), end(range), begin(lower), begin(upper), begin(step)); + } + + template <typename Range, typename UpperRange, + std::enable_if_t< + is_range_v<Range> && + is_range_v<UpperRange> + >* = nullptr + > + constexpr auto advance_vector(Range& range, UpperRange upper) + { + assert(distance(range) == distance(upper)); + using std::begin; + using std::end; + return advance_vector(begin(range), end(range), begin(upper)); + } + + // TODO: next number can be implemented using advance_vector, wrapping the constants base in iterators, + // but i'm not sure about it, would it optimize well?? + template <typename Itr, typename Num = int> + constexpr Itr next_number(Itr begin, Itr end, Num base = 2) + { + while(begin != end) + { + ++(*begin); + if(*begin < base) + break; + *begin = 0; + ++begin; + } + return begin; + } + + template <typename Range, typename Num = int> + constexpr auto next_number(Range& range, Num base = 2) + { + using std::begin; + using std::end; + return next_number(begin(range), end(range), base); + } + + template <typename Itr, typename Num = int> + constexpr Itr prev_number(Itr begin, Itr end, Num base = 2) + { + while(begin != end) + { + --(*begin); + if(*begin >= 0) + break; + *begin = base - 1; + ++begin; + } + return begin; + } + + template <typename Range, typename Num = int> + constexpr auto prev_number(Range& range, Num base = 2) + { + using std::begin; + using std::end; + return prev_number(begin(range), end(range), base); + } + + + + +} // namespace simple::support + +#endif /* end of include guard */ diff --git a/source/simple/support/algorithm/numeric.hpp b/source/simple/support/algorithm/numeric.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d79d965e202caf2e9b2a02355df3e111281a2a4e --- /dev/null +++ b/source/simple/support/algorithm/numeric.hpp @@ -0,0 +1,138 @@ +#ifndef SIMPLE_SUPPORT_ALGORITHM_NUMERIC_HPP +#define SIMPLE_SUPPORT_ALGORITHM_NUMERIC_HPP +#include <cmath> +#include <type_traits> + +#include "../arithmetic.hpp" + +namespace simple::support +{ + + template <typename Number, + std::enable_if_t<!std::is_floating_point_v<Number>>* = nullptr> + [[nodiscard]] constexpr + Number wrap(Number x, Number upperLimit) + noexcept(noexcept(Number((x + upperLimit) % upperLimit))) + { + return (x + upperLimit) % upperLimit; + } + + template <typename Number, + std::enable_if_t<std::is_floating_point_v<Number>>* = nullptr> + [[nodiscard]] constexpr + Number wrap(Number x, Number upperLimit) + noexcept(noexcept(Number(std::fmod(x + upperLimit, upperLimit)))) + { + return std::fmod(x + upperLimit, upperLimit); + } + + template <typename... Numbers> + [[nodiscard]] constexpr + auto average(Numbers... n) + //TODO: noexcept account for return value and default construction + noexcept(noexcept((n + ...) / int(sizeof...(n)))) + { + using Sum = decltype((n + ...)); + return (n + ...) / (Sum{} + sizeof...(n)); + } + + template <typename Number, typename Ratio> + [[nodiscard]] constexpr + Number way(Number from, Number to, Ratio ratio) + noexcept(noexcept(Number(from + (to - from)*ratio))) + { + return from + (to - from)*ratio; + } + + template <typename Number, typename Ratio> + [[nodiscard]] constexpr + Number wayback(Number from, Number to, Ratio ratio) + noexcept(noexcept(Number(from - (from - to)*ratio))) + { + return from - (from - to)*ratio; + } + + template <typename Number> + [[nodiscard]] constexpr + Number halfway(Number from, Number to) + // TODO: implement in terms of way with rational 1/2 + noexcept(noexcept(Number(from - (from - to)/2))) + { + return from + (to - from)/2; + } + + template <typename Number> + [[nodiscard]] constexpr + Number halfwayback(Number from, Number to) + // TODO: implement in terms of wayback with rational 1/2 + noexcept(noexcept(Number(from - (from - to)/2))) + { + return from - (from - to)/2; + } + + template <typename Integer, + typename Unsigned = std::make_unsigned_t<Integer>> + [[nodiscard]] constexpr + Integer midpoint(Integer a, Integer b) noexcept + // noexcept(noexcept(TODO)) + { + using std::numeric_limits; + constexpr Unsigned abs_int_min = -Unsigned(numeric_limits<Integer>::min()); + constexpr auto int_max = numeric_limits<Integer>::max(); + constexpr auto uint_max = numeric_limits<Unsigned>::max(); + + static_assert(uint_max >= int_max && uint_max - int_max >= abs_int_min, + "Unsigned includes Integer"); + + static_assert(uint_max/2 <= int_max, + "Halfing Unsigned brings it to positive Integer range"); + + static_assert(std::is_unsigned_v<Integer> || int_max <= abs_int_min, + "Can negate a positive Integer"); + + Unsigned diff = Unsigned(b) - Unsigned(a); + + // 2's complement with carry as a sign + Unsigned negative_2x = -diff; // neg + negative_2x /= 2; // div + Integer negative = -Integer(negative_2x); // neg + // or... 0 - (0 - diff)/2, this is midpointception! TODO: -_- + // can't use halfwayback here cause promotion ToT + + Integer positive = diff / 2; + + return a + (b < a ? negative : positive); + + // TODO: this is for geom::vector + // auto overflew = b < a; + // return a + overflew * negative + !overflew * positive; + // better yet introduce a mask function, since intrinsic vector has element-wise ternary + + } + + template <typename Unsigned> + [[nodiscard]] constexpr + Unsigned umidpoint(Unsigned a, Unsigned b) + // noexcept(noexcept(TODO)) + { + + Unsigned diff{}; + auto overflew = sub_overflow(diff,b,a); + + // manual idiv + Unsigned idiff = -diff; // neg + idiff /= Unsigned{2}; // div + idiff = -idiff; // neg + // or... 0 - (0 - diff)/2, this is midpointception! TODO: -_- + // can't use halfwayback here cause promotion ToT + + diff /= 2; + + return a + (overflew ? idiff : diff); + // return a + overflew * idiff + !overflew * diff; + + } + +} // namespace simple::support + +#endif /* end of include guard */ diff --git a/source/simple/support/algorithm/range_wrappers.hpp b/source/simple/support/algorithm/range_wrappers.hpp new file mode 100644 index 0000000000000000000000000000000000000000..15cb0b2671c5cc2d99bebfb7fc3317e72e970681 --- /dev/null +++ b/source/simple/support/algorithm/range_wrappers.hpp @@ -0,0 +1,120 @@ +#ifndef SIMPLE_SUPPORT_ALGORITHM_RANGE_WRAPPERS_HPP +#define SIMPLE_SUPPORT_ALGORITHM_RANGE_WRAPPERS_HPP +#include <algorithm> + +namespace simple::support +{ + + // TODO: use the adl pattern for begin and end - unqualified call that defaults to std versions, noexcept being the tricky part of course + + // rangey wrappers + + template <typename Range> + constexpr auto min_element(Range& range) + //TODO: noexcept account for return value construction + noexcept(noexcept(std::min_element(std::begin(range), std::end(range)))) + { + return std::min_element(std::begin(range), std::end(range)); + } + + template <typename Range> + constexpr auto min_element(Range&& range) + //TODO: noexcept account for return value construction + noexcept(noexcept(std::min_element(std::begin(range), std::end(range)))) + { + return *std::min_element(std::begin(range), std::end(range)); + } + + template <typename Range> + constexpr auto max_element(Range& range) + //TODO: noexcept account for return value construction + noexcept(noexcept(std::max_element(std::begin(range), std::end(range)))) + { + return std::max_element(std::begin(range), std::end(range)); + } + + template <typename Range> + constexpr auto max_element(Range&& range) + //TODO: noexcept account for return value construction + noexcept(noexcept(std::max_element(std::begin(range), std::end(range)))) + { + return *std::max_element(std::begin(range), std::end(range)); + } + + template <typename Range, typename Compare> + constexpr auto min_element(Range& range, Compare comp) + //TODO: noexcept account for return value construction + noexcept(noexcept(std::min_element(std::begin(range), std::end(range), comp))) + { + return std::min_element(std::begin(range), std::end(range), comp); + } + + template <typename Range, typename Compare> + constexpr auto min_element(Range&& range, Compare comp) + //TODO: noexcept account for return value construction + noexcept(noexcept(std::min_element(std::begin(range), std::end(range), comp))) + { + return *std::min_element(std::begin(range), std::end(range), comp); + } + + template <typename Range, typename Compare> + constexpr auto max_element(Range& range, Compare comp) + //TODO: noexcept account for return value construction + noexcept(noexcept(std::max_element(std::begin(range), std::end(range), comp))) + { + return std::max_element(std::begin(range), std::end(range), comp); + } + + template <typename Range, typename Compare> + constexpr auto max_element(Range&& range, Compare comp) + //TODO: noexcept account for return value construction + noexcept(noexcept(std::max_element(std::begin(range), std::end(range), comp))) + { + return *std::max_element(std::begin(range), std::end(range), comp); + } + + template <typename Range, typename UnaryPredicate> + constexpr auto all_of(const Range& range, UnaryPredicate p) + noexcept(noexcept(std::all_of(std::begin(range), std::end(range), p))) + { + return std::all_of(std::begin(range), std::end(range), p); + } + template <typename ExecutionPolicy, typename Range, typename UnaryPredicate> + constexpr auto all_of(ExecutionPolicy&& policy, const Range& range, UnaryPredicate p) + noexcept(noexcept(std::all_of(std::begin(range), std::end(range), p))) + { + return std::all_of(std::forward<ExecutionPolicy>(policy), + std::begin(range), std::end(range), p); + } + + template <typename Range, typename UnaryPredicate> + constexpr auto any_of(const Range& range, UnaryPredicate p) + noexcept(noexcept(std::any_of(std::begin(range), std::end(range), p))) + { + return std::any_of(std::begin(range), std::end(range), p); + } + template <typename ExecutionPolicy, typename Range, typename UnaryPredicate> + constexpr auto any_of(ExecutionPolicy&& policy, const Range& range, UnaryPredicate p) + noexcept(noexcept(std::any_of(std::begin(range), std::end(range), p))) + { + return std::any_of(std::forward<ExecutionPolicy>(policy), + std::begin(range), std::end(range), p); + } + + template <typename Range, typename UnaryPredicate> + constexpr auto none_of(const Range& range, UnaryPredicate p) + noexcept(noexcept(std::none_of(std::begin(range), std::end(range), p))) + { + return std::none_of(std::begin(range), std::end(range), p); + } + template <typename ExecutionPolicy, typename Range, typename UnaryPredicate> + constexpr auto none_of(ExecutionPolicy&& policy, const Range& range, UnaryPredicate p) + noexcept(noexcept(std::none_of(std::begin(range), std::end(range), p))) + { + return std::none_of(std::forward<ExecutionPolicy>(policy), + std::begin(range), std::end(range), p); + } + +} // namespace simple::support + +#endif /* end of include guard */ diff --git a/source/simple/support/algorithm/traits.hpp b/source/simple/support/algorithm/traits.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9cf7de47c959fece0d5539bdd3a82cc6f7b1d32f --- /dev/null +++ b/source/simple/support/algorithm/traits.hpp @@ -0,0 +1,32 @@ +#ifndef SIMPLE_SUPPORT_ALGORITHM_TRAITS_HPP +#define SIMPLE_SUPPORT_ALGORITHM_TRAITS_HPP +#include <iterator> + +namespace simple::support +{ + + namespace detail + { + + using std::begin; + using std::end; + + template <typename T, typename = std::nullptr_t> + struct is_range_helper + : public std::false_type {}; + template <typename T> + struct is_range_helper<T, decltype( + begin(std::declval<T>()) != end(std::declval<T>()), + nullptr)> + : public std::true_type {}; + + } // namespace detail + + template <typename T, typename = std::nullptr_t> + struct is_range : public detail::is_range_helper<T> {}; + template <typename T> + constexpr auto is_range_v = is_range<T>::value; + +} // namespace simple::support + +#endif /* end of include guard */ diff --git a/source/simple/support/algorithm/utils.hpp b/source/simple/support/algorithm/utils.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5536e88faf9a807c20af9a9610d4b8e5038aff8f --- /dev/null +++ b/source/simple/support/algorithm/utils.hpp @@ -0,0 +1,91 @@ +#ifndef SIMPLE_SUPPORT_ALGORITHM_UTILS_HPP +#define SIMPLE_SUPPORT_ALGORITHM_UTILS_HPP +#include "traits.hpp" +#include "../range.hpp" + +namespace simple::support +{ + + template <typename Range, std::enable_if_t<is_range_v<Range>>* = nullptr> + constexpr auto distance(Range&& range) + { + using std::begin; + using std::end; + return std::distance(begin(range), end(range)); + } + + template <typename Container> + [[nodiscard]] + constexpr auto make_range(Container& container) + { + using std::begin; + using std::end; + return make_range(begin(container), end(container)); + } + + template <typename Container> + constexpr auto make_range(Container&& container) = delete; + + template <typename Container> + [[nodiscard]] + constexpr auto reverse_range(Container& container) + { + using std::rbegin; + using std::rend; + return make_range(rbegin(container), rend(container)); + } + + template <typename Container> + constexpr auto reverse_range(Container&& container) = delete; + + template<typename Index = std::size_t, typename Container> + [[nodiscard]] constexpr + auto get_iterator_range(Container&& container, const range<Index>& index_range) + { + return make_range(std::forward<Container>(container)).sub_range(index_range); + } + + template<typename It> + [[nodiscard]] constexpr + auto distance(const range<It>& rng, const It& origin) + { + using std::begin; + using std::end; + return range{ begin(rng) - origin, end(rng) - origin }; + } + + template <typename From, typename To, typename Origin> + [[nodiscard]] constexpr + auto map_range(const From& from, To&& to, Origin origin) + { + return get_iterator_range(to, distance(from, origin)); + } + + template <typename From, typename To> + [[nodiscard]] constexpr + auto map_range(const From& from, const To& to) + { + using std::begin; + return get_iterator_range(to, distance(from, begin(from))); + } + + // std::swap is not constexpr >.< + template <typename T, std::enable_if_t< + std::is_move_constructible_v<T> && + std::is_move_assignable_v<T> + >* = nullptr> + constexpr void swap(T& one, T& other) + noexcept( + std::is_nothrow_move_constructible_v<T> && + std::is_nothrow_move_assignable_v<T> + ) + { + T temp{std::move(other)}; + other = std::move(one); + one = std::move(temp); + } + + +} // namespace simple::support + +#endif /* end of include guard */ diff --git a/source/simple/support/algorithm/variance.hpp b/source/simple/support/algorithm/variance.hpp new file mode 100644 index 0000000000000000000000000000000000000000..60cdf3c54a6c519a41b0a5a69a2c4fa9a8c21aa2 --- /dev/null +++ b/source/simple/support/algorithm/variance.hpp @@ -0,0 +1,46 @@ +#ifndef SIMPLE_SUPPORT_ALGORITHM_VARIANCE_HPP +#define SIMPLE_SUPPORT_ALGORITHM_VARIANCE_HPP +#include "iterator" + +namespace simple::support +{ + + // TODO: somehow the parameter counts and types line up and don't conflict between iterator pairs and range overloads, but it's fragile, better use the is_range_v trait + + template <typename Itr, typename BinaryOp> + constexpr Itr variance(Itr begin, Itr end, BinaryOp bop) // TODO: oveloads that accept output iterator[s] and output range, with bound checking + { + auto prev = begin; + while(++begin != end) + { + *prev = bop(*begin, *prev); + prev = begin; + } + return prev; + } + + template <typename Itr> + constexpr Itr variance(Itr begin, Itr end) + { + return variance(begin, end, std::minus{}); + } + + template <typename Range, typename BinaryOp> + constexpr auto variance(Range& range, BinaryOp bop) + { + using std::begin; + using std::end; + return make_range( begin(range), variance(begin(range), end(range), bop) ); + } + + template <typename Range> + constexpr auto variance(Range& range) + { + using std::begin; + using std::end; + return make_range( begin(range), variance(begin(range), end(range)) ); + } + +} // namespace simple::support + +#endif /* end of include guard */