diff --git a/src/include/units/bits/numeric_concepts.h b/src/include/units/bits/numeric_concepts.h new file mode 100644 index 00000000..7dafc99e --- /dev/null +++ b/src/include/units/bits/numeric_concepts.h @@ -0,0 +1,401 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +// P1813 - A Concept Design for the Numeric Algorithms + +namespace std { + + template + struct left_identity {}; + + template + using left_identity_t = decltype(declval>()()); + + template + struct right_identity {}; + + template + using right_identity_t = decltype(declval>()()); + + namespace detail { + + template + concept has_two_sided_identity = // exposition only + requires(BOp bop, const T& t, const U& u) { + typename left_identity_t; + typename left_identity_t; + typename right_identity_t; + typename right_identity_t; + requires same_as, left_identity_t>; + requires same_as, right_identity_t>; + requires same_as, right_identity_t>; + }; + + } + + template + requires detail::has_two_sided_identity + struct two_sided_identity { + constexpr common_type_t operator()() const; + }; + + template + using two_sided_identity_t = decltype(two_sided_identity{}(declval(), declval(), declval())); + + template + struct left_zero {}; + + template + using left_zero_t = decltype(declval>()()); + + template + struct right_zero {}; + + template + using right_zero_t = decltype(declval>()()); + + namespace detail { + + template + concept has_two_sided_zero = // exposition only + requires(BOp bop, const T& t, const U& u) { + typename left_zero_t; + typename left_zero_t; + typename right_zero_t; + typename right_zero_t; + requires same_as, left_zero_t>; + requires same_as, right_zero_t>; + requires same_as, right_zero_t>; + }; + + } + + template + requires detail::has_two_sided_zero + struct two_sided_zero { + constexpr common_type_t operator()(BOp bop, T&& t, U&& u) const; + }; + + template + using two_sided_zero_t = decltype(two_sided_zero{}(declval(), declval(), declval())); + + template + struct inverse_traits {}; + + template + using inverse_operation_t = typename inverse_traits::type; + + template + concept commutative_operation = + regular_invocable && + regular_invocable && + common_with && + equality_comparable_with; + + template + concept magma = + std::common_with && + std::regular_invocable && + std::regular_invocable && + std::regular_invocable && + std::regular_invocable && + std::common_with, T> && + std::common_with, U> && + std::same_as, std::invoke_result_t>; + + template + concept semigroup = magma; + + template + concept monoid = semigroup && + requires { + typename two_sided_identity_t, remove_cvref_t>; + }; + + template + concept quasigroup = magma && + requires { + typename inverse_operation_t; + typename inverse_operation_t, T, U>; + requires same_as, T, U>>; + }; + + template + concept loop = quasigroup && + requires { + typename two_sided_identity_t, remove_cvref_t>; + }; + + template + concept group = semigroup && quasigroup; + + template + concept abelian_group = group && commutative_operation; + + template + concept weak_magmaring = magma && magma>; + + template + concept near_semiring = weak_magmaring && + monoid> && semigroup && + requires { + typename two_sided_zero_t, remove_cvref_t>; + }; + + + + namespace ranges { + + namespace detail { + + template + concept summable_with = // exposition only + default_initializable> && + default_initializable> && + common_reference_with && + requires(T&& t, U&& u) { + { std::forward(t) + std::forward(t) } -> common_with; + { std::forward(u) + std::forward(u) } -> common_with; + { std::forward(t) + std::forward(u) } -> common_with; + { std::forward(u) + std::forward(t) } -> common_with; + requires same_as(t) + std::forward(u)), + decltype(std::forward(u) + std::forward(t))>; + }; + + } + + struct plus { + template U> + constexpr decltype(auto) operator()(T&& t, U&& u) const { + return std::forward(t) + std::forward(u); + } + + using is_transparent = std::true_type; + }; + + namespace detail { + + template + concept negatable = // exposition only + summable_with && + totally_ordered && + requires(T&& t) { + { -std::forward(t) } -> common_with; + }; + + } + + struct negate { + template + constexpr decltype(auto) operator()(T&& t) const { + return -std::forward(t); + } + + using is_transparent = std::true_type; + }; + + namespace detail { + + template + concept differenceable_with = // exposition only + summable_with && + negatable && + negatable && + totally_ordered_with && + requires(T&& t, U&& u) { + { std::forward(t) - std::forward(t) } -> common_with; + { std::forward(u) - std::forward(u) } -> common_with; + { std::forward(t) - std::forward(u) } -> common_with; + { std::forward(u) - std::forward(t) } -> common_with; + requires same_as(t) - std::forward(u)), + decltype(std::forward(u) - std::forward(t))>; + }; + + } + + struct minus { + template U> + constexpr decltype(auto) operator()(T&& t, U&& u) const { + return std::forward(t) - std::forward(u); + } + + using is_transparent = std::true_type; + }; + + namespace detail { + + template + concept multiplicable_with = // exposition only + detail::summable_with && + constructible_from, int> && // specifically T{0} and T{1} + constructible_from, int> && // specifically U{0} and U{1} + constructible_from>, int> && + common_reference_with && + requires(T&& t, U&& u) { + { std::forward(t) * std::forward(t) } -> common_with; + { std::forward(u) * std::forward(u) } -> common_with; + { std::forward(t) * std::forward(u) } -> common_with; + { std::forward(u) * std::forward(t) } -> common_with; + requires same_as(t) * std::forward(u)), + decltype(std::forward(u) * std::forward(t))>; + }; + + } + + struct times { + template U> + constexpr decltype(auto) operator()(T&& t, U&& u) const + { return std::forward(t) * std::forward(u); } + + using is_transparent = std::true_type; + }; + + namespace detail { + + template + concept divisible_with = // exposition only + multiplicable_with && + subtractible_with && + requires(T&& t, U&& u) { + { std::forward(t) / std::forward(t) } -> common_with; + { std::forward(u) / std::forward(u) } -> common_with; + { std::forward(t) / std::forward(u) } -> common_with; + { std::forward(u) / std::forward(t) } -> common_with; + requires same_as(t) / std::forward(u)), + decltype(std::forward(u) / std::forward(t))>;; + }; + + } + + struct divided_by { + template U> + constexpr decltype(auto) operator()(T&& t, U&& u) const + { return std::forward(t) / std::forward(u); } + }; + + namespace detail { + + template + concept modulo_with = // exposition only + divisible_with && + requires(T&& t, Q&& q) { + { std::forward(t) % std::forward(t) } -> common_with; + { std::forward(q) % std::forward(q) } -> common_with; + { std::forward(t) % std::forward(q) } -> common_with; + { std::forward(q) % std::forward(t) } -> common_with; + requires same_as(t) % std::forward(q)), + decltype(std::forward(q) % std::forward(t))>; + }; + + } + + struct modulus { + template U> + constexpr decltype(auto) operator()(T&& t, U&& u) const + { return std::forward(t) % std::forward(u); } + }; + + } // namespace ranges + + template + requires magma + struct left_identity { + constexpr common_type_t operator()() const { return T{}; } + }; + + template + requires magma + struct right_identity { + constexpr common_type_t operator()() const { return U{}; } + }; + + template<> + struct inverse_traits { + using type = minus; + + constexpr type operator()() const noexcept { return type{}; } + }; + + template + struct right_identity : private right_identity { + using right_identity::operator(); + }; + + template<> + struct inverse_traits { + using type = ranges::plus; + constexpr type operator()() const noexcept { return type{}; } + } + + template + requires magma + struct left_identity { + constexpr common_type_t operator()() const { return T{1}; } + }; + + template + requires magma + struct right_identity { + constexpr common_type_t operator()() const { return U{1}; } + }; + + template + requires magma + struct left_zero { + constexpr common_type_t operator()() const { return T{}; } + }; + + template + requires magma + struct right_zero { + constexpr common_type_t operator()() const { return U{}; } + }; + + template<> + struct inverse_traits { + using type = divided_by; + constexpr type operator()() const noexcept { return type{}; } + }; + + template + requires magma + struct right_identity { + constexpr common_type_t operator()() const { return U{1}; } + }; + + template<> + struct inverse_traits { + using type = times; + constexpr type operator()() const noexcept { return type{}; } + }; + + template + requires magma + struct left_zero { + constexpr common_type_t operator()() const { return T{}; } + }; + +} // namespace std