diff --git a/source/simple/support/algorithm.hpp b/source/simple/support/algorithm.hpp index 2811321ebe922627f0b5c9838a7813ffb5a24b50..76237ab171e7555f572b21d54668baeb9a1f50b2 100644 --- a/source/simple/support/algorithm.hpp +++ b/source/simple/support/algorithm.hpp @@ -273,40 +273,107 @@ namespace simple::support template <typename... Numbers> [[nodiscard]] constexpr auto average(Numbers... n) - //TODO: noexcept account for return value construction - //TODO: cast size to result type of sum instead of int + //TODO: noexcept account for return value and default construction noexcept(noexcept((n + ...) / int(sizeof...(n)))) { - return (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 midpoint(Number a, Number b) - noexcept(noexcept(Number(a + (b - a)/2))) + Number halfway(Number from, Number to) + // TODO: implement in terms of way with rational 1/2 + noexcept(noexcept(Number(from - (from - to)/2))) { - return a + (b - a)/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(TODO)) + { + using std::numeric_limits; + static_assert( + numeric_limits<Unsigned>::max() >= + Unsigned(numeric_limits<Integer>::max()), + "Unsigned includes Integer" + ); + static_assert( + numeric_limits<Unsigned>::max()/2 <= + numeric_limits<Integer>::max(), + "Halfing Unsigned brings it to positive Integer range" + ); + static_assert(std::is_unsigned_v<Integer> || + Unsigned(numeric_limits<Integer>::max()) <= + Unsigned(-Unsigned(numeric_limits<Integer>::min())), + "Can negate a positive Integer" + ); + + Unsigned diff = Unsigned(b) - Unsigned(a); + + // 2's complement with carry as sign + Unsigned negative_2x = -diff; // neg + negative_2x /= 2; // div + Integer negative = -Integer(negative_2x); // neg + + Integer positive = diff / 2; + + auto overflew = b < a; + return a + (overflew ? negative : positive); + + // TODO: this is for geom::vector + // return a + overflew * idiff + !overflew * diff; + } template <typename Unsigned> [[nodiscard]] constexpr - Unsigned midpoint_overflow(Unsigned a, Unsigned b) + Unsigned umidpoint(Unsigned a, Unsigned b) // noexcept(noexcept(TODO)) { Unsigned diff{}; - bool overflew = sub_overflow(diff,b,a); + 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 /= Unsigned{2}; + diff /= 2; return a + (overflew ? idiff : diff); - // return a + overflew * idiff + ~overflew * diff; + // return a + overflew * idiff + !overflew * diff; } diff --git a/unit_tests/libcpp_midpoint.cpp b/unit_tests/libcpp_midpoint.cpp index c745d5009c40989cb4e1949134396e6fedbae78c..f0afd460b36683efa2d4328d35fbb9eaa1c304d1 100644 --- a/unit_tests/libcpp_midpoint.cpp +++ b/unit_tests/libcpp_midpoint.cpp @@ -10,7 +10,7 @@ // <numeric> // template <class _Tp> -// _Tp midpoint_overflow(_Tp __a, _Tp __b) noexcept +// _Tp midpoint(_Tp __a, _Tp __b) noexcept // #include <stdint.h> @@ -30,44 +30,44 @@ void signed_test() constexpr T three{3}; constexpr T four{4}; - // ASSERT_SAME_TYPE(decltype(midpoint_overflow(T(), T())), T); - // ASSERT_NOEXCEPT( midpoint_overflow(T(), T())); + static_assert(std::is_same_v<decltype(midpoint(T(), T())), T>); + // ASSERT_NOEXCEPT( midpoint(T(), T())); using limits = std::numeric_limits<T>; - static_assert(midpoint_overflow(one, three) == two, ""); - static_assert(midpoint_overflow(three, one) == two, ""); - - assert(midpoint_overflow(zero, zero) == zero); - assert(midpoint_overflow(zero, two) == one); - assert(midpoint_overflow(two, zero) == one); - assert(midpoint_overflow(two, two) == two); - - assert(midpoint_overflow(one, four) == two); - assert(midpoint_overflow(four, one) == three); - assert(midpoint_overflow(three, four) == three); - assert(midpoint_overflow(four, three) == four); - - assert(midpoint_overflow(T( 3), T( 4)) == T(3)); - assert(midpoint_overflow(T( 4), T( 3)) == T(4)); - assert(midpoint_overflow(T(-3), T( 4)) == T(0)); - assert(midpoint_overflow(T(-4), T( 3)) == T(-1)); - assert(midpoint_overflow(T( 3), T(-4)) == T(0)); - assert(midpoint_overflow(T( 4), T(-3)) == T(1)); - assert(midpoint_overflow(T(-3), T(-4)) == T(-3)); - assert(midpoint_overflow(T(-4), T(-3)) == T(-4)); - - static_assert(midpoint_overflow(limits::min(), limits::max()) == T(-1), ""); - static_assert(midpoint_overflow(limits::max(), limits::min()) == T( 0), ""); - - static_assert(midpoint_overflow(limits::min(), T(6)) == limits::min()/2 + 3, ""); - assert( midpoint_overflow(T(6), limits::min()) == limits::min()/2 + 3); - assert( midpoint_overflow(limits::max(), T(6)) == limits::max()/2 + 4); - static_assert(midpoint_overflow(T(6), limits::max()) == limits::max()/2 + 3, ""); - - assert( midpoint_overflow(limits::min(), T(-6)) == limits::min()/2 - 3); - static_assert(midpoint_overflow(T(-6), limits::min()) == limits::min()/2 - 3, ""); - static_assert(midpoint_overflow(limits::max(), T(-6)) == limits::max()/2 - 2, ""); - assert( midpoint_overflow(T(-6), limits::max()) == limits::max()/2 - 3); + static_assert(midpoint(one, three) == two, ""); + static_assert(midpoint(three, one) == two, ""); + + assert(midpoint(zero, zero) == zero); + assert(midpoint(zero, two) == one); + assert(midpoint(two, zero) == one); + assert(midpoint(two, two) == two); + + assert(midpoint(one, four) == two); + assert(midpoint(four, one) == three); + assert(midpoint(three, four) == three); + assert(midpoint(four, three) == four); + + assert(midpoint(T( 3), T( 4)) == T(3)); + assert(midpoint(T( 4), T( 3)) == T(4)); + assert(midpoint(T(-3), T( 4)) == T(0)); + assert(midpoint(T(-4), T( 3)) == T(-1)); + assert(midpoint(T( 3), T(-4)) == T(0)); + assert(midpoint(T( 4), T(-3)) == T(1)); + assert(midpoint(T(-3), T(-4)) == T(-3)); + assert(midpoint(T(-4), T(-3)) == T(-4)); + + static_assert(midpoint(limits::min(), limits::max()) == T(-1), ""); + static_assert(midpoint(limits::max(), limits::min()) == T( 0), ""); + + static_assert(midpoint(limits::min(), T(6)) == limits::min()/2 + 3, ""); + assert( midpoint(T(6), limits::min()) == limits::min()/2 + 3); + assert( midpoint(limits::max(), T(6)) == limits::max()/2 + 4); + static_assert(midpoint(T(6), limits::max()) == limits::max()/2 + 3, ""); + + assert( midpoint(limits::min(), T(-6)) == limits::min()/2 - 3); + static_assert(midpoint(T(-6), limits::min()) == limits::min()/2 - 3, ""); + static_assert(midpoint(limits::max(), T(-6)) == limits::max()/2 - 2, ""); + assert( midpoint(T(-6), limits::max()) == limits::max()/2 - 3); } template <typename T> @@ -79,46 +79,69 @@ void unsigned_test() constexpr T three{3}; constexpr T four{4}; - // ASSERT_SAME_TYPE(decltype(midpoint_overflow(T(), T())), T); - // ASSERT_NOEXCEPT( midpoint_overflow(T(), T())); + static_assert(std::is_same_v<decltype(midpoint(T(), T())), T>); + // ASSERT_NOEXCEPT( midpoint(T(), T())); using limits = std::numeric_limits<T>; const T half_way = (limits::max() - limits::min())/2; - static_assert(midpoint_overflow(one, three) == two, ""); - static_assert(midpoint_overflow(three, one) == two, ""); - - assert(midpoint_overflow(zero, zero) == zero); - assert(midpoint_overflow(zero, two) == one); - assert(midpoint_overflow(two, zero) == one); - assert(midpoint_overflow(two, two) == two); - - assert(midpoint_overflow(one, four) == two); - assert(midpoint_overflow(four, one) == three); - assert(midpoint_overflow(three, four) == three); - assert(midpoint_overflow(four, three) == four); - - assert(midpoint_overflow(limits::min(), limits::max()) == T(half_way)); - assert(midpoint_overflow(limits::max(), limits::min()) == T(half_way + 1)); - - static_assert(midpoint_overflow(limits::min(), T(6)) == limits::min()/2 + 3, ""); - assert( midpoint_overflow(T(6), limits::min()) == limits::min()/2 + 3); - assert( midpoint_overflow(limits::max(), T(6)) == half_way + 4); - static_assert(midpoint_overflow(T(6), limits::max()) == half_way + 3, ""); + static_assert(midpoint(one, three) == two, ""); + static_assert(midpoint(three, one) == two, ""); + + assert(midpoint(zero, zero) == zero); + assert(midpoint(zero, two) == one); + assert(midpoint(two, zero) == one); + assert(midpoint(two, two) == two); + + assert(midpoint(one, four) == two); + assert(midpoint(four, one) == three); + assert(midpoint(three, four) == three); + assert(midpoint(four, three) == four); + + assert(midpoint(limits::min(), limits::max()) == T(half_way)); + assert(midpoint(limits::max(), limits::min()) == T(half_way + 1)); + + static_assert(midpoint(limits::min(), T(6)) == limits::min()/2 + 3, ""); + assert( midpoint(T(6), limits::min()) == limits::min()/2 + 3); + assert( midpoint(limits::max(), T(6)) == half_way + 4); + static_assert(midpoint(T(6), limits::max()) == half_way + 3, ""); + + static_assert(std::is_same_v<decltype(umidpoint(T(), T())), T>); + // ASSERT_NOEXCEPT( midpoint(T(), T())); + static_assert(umidpoint(one, three) == two, ""); + static_assert(umidpoint(three, one) == two, ""); + + assert(umidpoint(zero, zero) == zero); + assert(umidpoint(zero, two) == one); + assert(umidpoint(two, zero) == one); + assert(umidpoint(two, two) == two); + + assert(umidpoint(one, four) == two); + assert(umidpoint(four, one) == three); + assert(umidpoint(three, four) == three); + assert(umidpoint(four, three) == four); + + assert(umidpoint(limits::min(), limits::max()) == T(half_way)); + assert(umidpoint(limits::max(), limits::min()) == T(half_way + 1)); + + static_assert(umidpoint(limits::min(), T(6)) == limits::min()/2 + 3, ""); + assert( umidpoint(T(6), limits::min()) == limits::min()/2 + 3); + assert( umidpoint(limits::max(), T(6)) == half_way + 4); + static_assert(umidpoint(T(6), limits::max()) == half_way + 3, ""); } int main(int, char**) { - // signed_test<signed char>(); - // signed_test<short>(); - // signed_test<int>(); - // signed_test<long>(); - // signed_test<long long>(); - // - // signed_test<int8_t>(); - // signed_test<int16_t>(); - // signed_test<int32_t>(); - // signed_test<int64_t>(); + signed_test<signed char>(); + signed_test<short>(); + signed_test<int>(); + signed_test<long>(); + signed_test<long long>(); + + signed_test<int8_t>(); + signed_test<int16_t>(); + signed_test<int32_t>(); + signed_test<int64_t>(); unsigned_test<unsigned char>(); unsigned_test<unsigned short>(); @@ -137,7 +160,7 @@ int main(int, char**) #endif // int_test<char>(); - // signed_test<ptrdiff_t>(); + signed_test<ptrdiff_t>(); unsigned_test<size_t>(); return 0; diff --git a/unit_tests/libstdcpp_midpoint.cpp b/unit_tests/libstdcpp_midpoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d9a2848b35a8a56e1b05e36c1751bdd92e249f20 --- /dev/null +++ b/unit_tests/libstdcpp_midpoint.cpp @@ -0,0 +1,113 @@ +// Copyright (C) 2019-2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include <climits> +#include <cassert> +#include "simple/support/algorithm.hpp" + +using simple::support::midpoint; + +static_assert(std::is_same_v<decltype(midpoint(0, 1)), int>); +static_assert(noexcept(midpoint(1, 2))); + +struct test_type { }; +template<typename T> decltype(midpoint<T>(T(), T())) try_midpoint(int); +template<typename T> test_type try_midpoint(...); +template<typename T> constexpr bool no_midpoint() +{ return std::is_same_v<decltype(try_midpoint<T>()), test_type>; } + +static_assert(no_midpoint<bool>()); +static_assert(no_midpoint<const bool>()); +static_assert(no_midpoint<const int>()); +static_assert(no_midpoint<volatile int>()); + +static_assert( midpoint(0, 0) == 0 ); +static_assert( midpoint(1, 1) == 1 ); +static_assert( midpoint(0, 1) == 0 ); +static_assert( midpoint(1, 0) == 1 ); +static_assert( midpoint(0, 2) == 1 ); +static_assert( midpoint(3, 2) == 3 ); +static_assert( midpoint(-5, 4) == -1 ); +static_assert( midpoint(5, -4) == 1 ); +static_assert( midpoint(-5, -4) == -5 ); +static_assert( midpoint(-4, -5) == -4 ); +static_assert( midpoint(INT_MIN, INT_MAX) == -1 ); +static_assert( midpoint(INT_MAX, INT_MIN) == 0 ); +static_assert( midpoint(INT_MAX, INT_MAX) == INT_MAX ); +static_assert( midpoint(INT_MAX, INT_MAX-1) == INT_MAX ); +static_assert( midpoint(INT_MAX-1, INT_MAX-1) == INT_MAX-1 ); +static_assert( midpoint(INT_MAX-1, INT_MAX) == INT_MAX-1 ); +static_assert( midpoint(INT_MAX, INT_MAX-2) == INT_MAX-1 ); + +static_assert( midpoint(0u, 0u) == 0 ); +static_assert( midpoint(0u, 1u) == 0 ); +static_assert( midpoint(1u, 0u) == 1 ); +static_assert( midpoint(0u, 2u) == 1 ); +static_assert( midpoint(3u, 2u) == 3 ); +static_assert( midpoint(0u, UINT_MAX) == UINT_MAX/2 ); +static_assert( midpoint(UINT_MAX, 0u) == (UINT_MAX/2 + 1) ); +static_assert( midpoint(UINT_MAX, UINT_MAX) == UINT_MAX ); +static_assert( midpoint(UINT_MAX, UINT_MAX-1) == UINT_MAX ); +static_assert( midpoint(UINT_MAX-1, UINT_MAX-1) == UINT_MAX-1 ); +static_assert( midpoint(UINT_MAX-1, UINT_MAX) == UINT_MAX-1 ); +static_assert( midpoint(UINT_MAX, UINT_MAX-2) == UINT_MAX-1 ); + +static_assert( midpoint<short>(0, 0) == 0 ); +static_assert( midpoint<short>(0, 1) == 0 ); +static_assert( midpoint<short>(1, 0) == 1 ); +static_assert( midpoint<short>(0, 2) == 1 ); +static_assert( midpoint<short>(3, 2) == 3 ); +static_assert( midpoint<short>(-5, 4) == -1 ); +static_assert( midpoint<short>(5, -4) == 1 ); +static_assert( midpoint<short>(-5, -4) == -5 ); +static_assert( midpoint<short>(-4, -5) == -4 ); +static_assert( midpoint<short>(SHRT_MIN, SHRT_MAX) == -1 ); +static_assert( midpoint<short>(SHRT_MAX, SHRT_MIN) == 0 ); +static_assert( midpoint<short>(SHRT_MAX, SHRT_MAX) == SHRT_MAX ); +static_assert( midpoint<short>(SHRT_MAX, SHRT_MAX-1) == SHRT_MAX ); +static_assert( midpoint<short>(SHRT_MAX-1, SHRT_MAX-1) == SHRT_MAX-1 ); +static_assert( midpoint<short>(SHRT_MAX-1, SHRT_MAX) == SHRT_MAX-1 ); +static_assert( midpoint<short>(SHRT_MAX, SHRT_MAX-2) == SHRT_MAX-1 ); + +static_assert( midpoint<signed char>(0, 0) == 0 ); +static_assert( midpoint<signed char>(1, 1) == 1 ); +static_assert( midpoint<signed char>(0, 1) == 0 ); +static_assert( midpoint<signed char>(1, 0) == 1 ); +static_assert( midpoint<signed char>(0, 2) == 1 ); +static_assert( midpoint<signed char>(3, 2) == 3 ); +static_assert( midpoint<signed char>(-5, 4) == -1 ); +static_assert( midpoint<signed char>(5, -4) == 1 ); +static_assert( midpoint<signed char>(-5, -4) == -5 ); +static_assert( midpoint<signed char>(-4, -5) == -4 ); +static_assert( midpoint<signed char>(SCHAR_MIN, SCHAR_MAX) == -1 ); +static_assert( midpoint<signed char>(SCHAR_MAX, SCHAR_MIN) == 0 ); +static_assert( midpoint<signed char>(SCHAR_MAX, SCHAR_MAX) == SCHAR_MAX ); +static_assert( midpoint<signed char>(SCHAR_MAX, SCHAR_MAX-1) == SCHAR_MAX); + +void +test01() +{ + // Test every possibility for signed char. + for (int a = SCHAR_MIN; a <= SCHAR_MAX; ++a) + for (int b = SCHAR_MIN; b <= SCHAR_MAX; ++b) + assert( midpoint(a, b) == midpoint<int>(a, b) ); +} + +int main() +{ + test01(); +}