basedOpinionated utility library |
git clone git://git.dimitrijedobrota.com/based.git |
Log | Files | Refs | README | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 5339e33b06119b799b79910226098157f26174db |
parent | 20980a6919309c9c7d5478d0312393322bbc2648 |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Mon, 7 Apr 2025 17:48:09 +0200 |
Rework concpets to always require domain types
Diffstat:M | example/type_traits.cpp | | | +++++++++++++++++++++++++++++++++++++--------------------------------- |
M | include/based/algorithm.hpp | | | ++++++++++++++++---- |
M | include/based/functional.hpp | | | +++++++++++++++++++++++++++++++++++++++++----------------------------------------- |
M | include/based/type_traits.hpp | | | +++++++++++++++++++--------------------------------------------------------------- |
4 files changed, 182 insertions(+), 366 deletions(-)
diff --git a/example/type_traits.cpp b/example/type_traits.cpp
@@ -57,60 +57,64 @@ int main()
using id = identity<double>;
using ii = identity<irregular>;
static_assert(std::same_as<based::domain_t<id>, double>);
static_assert(std::same_as<based::codomain_t<id>, double>);
static_assert(based::arity_v<id> == 1);
static_assert(std::same_as<based::codomain_t<id, double>, double>);
static_assert(based::arity_v<id, double> == 1);
static_assert(based::Procedure<id>);
static_assert(based::Procedure<ii>);
static_assert(based::Procedure<id, double>);
static_assert(!based::Procedure<ii, irregular>);
static_assert(based::RegularProcedure<id>);
static_assert(!based::RegularProcedure<ii>);
static_assert(based::RegularProcedure<id, double>);
static_assert(!based::RegularProcedure<ii, irregular>);
static_assert(based::FunctionalProcedure<id>);
static_assert(!based::FunctionalProcedure<ii>);
static_assert(based::FunctionalProcedure<id, double>);
static_assert(!based::FunctionalProcedure<ii, irregular>);
using ad = add<double, double>;
using ai = add<irregular, irregular>;
using aid = add<irregular, double>;
using adi = add<double, irregular>;
static_assert(std::same_as<based::domain_t<ad>, double>);
static_assert(std::same_as<based::codomain_t<ad>, double>);
static_assert(based::arity_v<ad> == 2);
static_assert(std::same_as<based::codomain_t<ad, double, double>, double>);
static_assert(based::arity_v<ad, double, double> == 2);
static_assert(based::Procedure<ad>);
static_assert(based::Procedure<ai>);
static_assert(based::Procedure<aid>);
static_assert(based::Procedure<adi>);
static_assert(based::Procedure<ad, double, double>);
static_assert(based::Procedure<ai, irregular, irregular>);
static_assert(based::Procedure<aid, irregular, double>);
static_assert(based::Procedure<adi, double, irregular>);
static_assert(based::RegularProcedure<ad>);
static_assert(!based::RegularProcedure<ai>);
static_assert(!based::RegularProcedure<aid>);
static_assert(!based::RegularProcedure<adi>);
static_assert(based::RegularProcedure<ad, double, double>);
static_assert(!based::RegularProcedure<ai, irregular, irregular>);
static_assert(!based::RegularProcedure<aid, irregular, double>);
static_assert(!based::RegularProcedure<adi, double, irregular>);
static_assert(based::FunctionalProcedure<ad>);
static_assert(!based::FunctionalProcedure<ai>);
static_assert(!based::FunctionalProcedure<aid>);
static_assert(!based::FunctionalProcedure<adi>);
static_assert(based::FunctionalProcedure<ad, double, double>);
static_assert(!based::FunctionalProcedure<ai, irregular, irregular>);
static_assert(!based::FunctionalProcedure<aid, irregular, double>);
static_assert(!based::FunctionalProcedure<adi, double, irregular>);
using md = mutate<double>;
static_assert(based::Procedure<md>);
static_assert(based::RegularProcedure<md>);
static_assert(based::Procedure<md, double*>);
static_assert(based::RegularProcedure<md, double*>);
static_assert(!based::FunctionalProcedure<md>);
static_assert(based::RegularProcedure<decltype(sub<double, double>)>);
static_assert(
based::RegularProcedure<decltype(sub<double, double>), double, double>);
static const auto l1 = [](double a) { return a; };
static_assert(based::Procedure<decltype(l1)>);
static_assert(based::RegularProcedure<decltype(l1)>);
static_assert(based::FunctionalProcedure<decltype(l1)>);
static_assert(based::Procedure<decltype(l1), double>);
static_assert(based::RegularProcedure<decltype(l1), double>);
static_assert(based::FunctionalProcedure<decltype(l1), double>);
static const auto l2 = [](irregular /* a */) { return 1; };
static_assert(based::Procedure<decltype(l2)>);
static_assert(!based::RegularProcedure<decltype(l2)>);
static_assert(!based::FunctionalProcedure<decltype(l2)>);
static_assert(!based::Procedure<decltype(l2), irregular>);
static_assert(!based::RegularProcedure<decltype(l2), irregular>);
static_assert(!based::FunctionalProcedure<decltype(l2), irregular>);
static const auto l3 = [](const irregular& /* a */) { return 1; };
static_assert(based::Procedure<decltype(l3), irregular>);
static_assert(!based::RegularProcedure<decltype(l3), irregular>);
static_assert(!based::FunctionalProcedure<decltype(l3), irregular>);
return 0;
}
diff --git a/include/based/algorithm.hpp b/include/based/algorithm.hpp
@@ -9,9 +9,21 @@
namespace based
{
namespace detail
{
template<typename P, typename Arg>
concept NoninputRelation = requires {
requires(RegularProcedure<P, Arg, Arg>);
requires(std::same_as<bool, codomain_t<P, Arg, Arg>>);
requires(arity_v<P, Arg, Arg> == 2);
};
} // namespace detail
// returns min element, first if equal
template<Relation R, BareRegular T, BareRegular U>
requires BareSameAs<T, U> && BareSameAs<T, domain_t<R>>
template<BareRegular T, BareRegular U, detail::NoninputRelation<T> R>
requires BareSameAs<T, U>
decltype(auto) min(T&& lhs, U&& rhs, R r)
{
return r(rhs, lhs) ? std::forward<U>(rhs) : std::forward<T>(lhs);
@@ -51,8 +63,8 @@ I min_element(I first, I last)
}
// returns max element, second if equal
template<Relation R, BareRegular T, BareRegular U>
requires BareSameAs<T, U> && BareSameAs<T, domain_t<R>>
template<BareRegular T, BareRegular U, detail::NoninputRelation<T> R>
requires BareSameAs<T, U>
decltype(auto) max(T&& lhs, U&& rhs, R r)
{
return r(rhs, lhs) ? std::forward<T>(lhs) : std::forward<U>(rhs);
diff --git a/include/based/functional.hpp b/include/based/functional.hpp
@@ -7,8 +7,8 @@
namespace based
{
template<Transformation F>
distance_t<F> distance(domain_t<F> x, domain_t<F> y, F f)
template<typename T, Transformation<T> F>
distance_t<F> distance(T x, T y, F f)
{
// Precondition: y is reachable from x under f
using N = distance_t<F>;
@@ -21,8 +21,8 @@ distance_t<F> distance(domain_t<F> x, domain_t<F> y, F f)
return n;
}
template<Transformation F>
domain_t<F> convergant_point(domain_t<F> x0, domain_t<F> x1, F f)
template<typename T, Transformation<T> F>
T convergant_point(T x0, T x1, F f)
{
// Precondition: (exists n from distance_t<F>) n>= 0 ^ f^n(x0) = f^n(x1)
while (x0 != x1) {
@@ -32,8 +32,8 @@ domain_t<F> convergant_point(domain_t<F> x0, domain_t<F> x1, F f)
return x0;
}
template<Transformation F, TransformUnaryPredicate<F> P>
domain_t<F> collision_point(const domain_t<F>& x, F f, P p)
template<typename T, Transformation<T> F, UnaryPredicate<T> P>
T collision_point(const T& x, F f, P p)
{
// Precondition p(x) <=> f(x) is defined
if (!p(x)) {
@@ -57,23 +57,23 @@ domain_t<F> collision_point(const domain_t<F>& x, F f, P p)
// Postcondition: return value is terminal point or collision point
}
template<Transformation F, TransformUnaryPredicate<F> P>
bool terminating(const domain_t<F>& x, F f, P p)
template<typename T, Transformation<T> F, UnaryPredicate<T> P>
bool terminating(const T& x, F f, P p)
{
// Precondition: p(x) <=> F(x) is defined
return !p(collision_point(x, f, p));
}
template<Transformation F, TransformUnaryPredicate<F> P>
bool circular(const domain_t<F>& x, F f, P p)
template<typename T, Transformation<T> F, UnaryPredicate<T> P>
bool circular(const T& x, F f, P p)
{
// Precondition: p(x) <=> F(x) is defined
const auto y = collision_point(x, f, p);
return p(y) && x == f(y);
}
template<Transformation F, TransformUnaryPredicate<F> P>
bool connection_point(const domain_t<F>& x, F f, P p)
template<typename T, Transformation<T> F, UnaryPredicate<T> P>
bool connection_point(const T& x, F f, P p)
{
// Precondition: p(x) <=> F(x) is defined
const auto y = collision_point(x, f, p);
@@ -83,9 +83,10 @@ bool connection_point(const domain_t<F>& x, F f, P p)
return convergant_point(x, f(y), f);
}
template<Transformation F, TransformUnaryPredicate<F> P>
std::tuple<distance_t<F>, distance_t<F>, domain_t<F>> orbit_structure(
const domain_t<F>& x, F f, P p)
template<typename T, Transformation<T> F, UnaryPredicate<T> P>
std::tuple<distance_t<F>, distance_t<F>, T> orbit_structure(const T& x,
F f,
P p)
{
// Precondition: p(x) <=> F(x) is defined
const auto y = connection_point(x, f, p);
@@ -94,8 +95,8 @@ std::tuple<distance_t<F>, distance_t<F>, domain_t<F>> orbit_structure(
return {m, n, y};
}
template<Transformation F>
domain_t<F> collision_point_nonterminating_orbit(const domain_t<F>& x, F f)
template<typename T, Transformation<T> F>
T collision_point_nonterminating_orbit(const T& x, F f)
{
auto slow = x;
auto fast = f(x);
@@ -108,28 +109,28 @@ domain_t<F> collision_point_nonterminating_orbit(const domain_t<F>& x, F f)
// Postcondition: return value is terminal point or collision point
}
template<Transformation F>
bool circular_nonterminating_orbit(const domain_t<F>& x, F f)
template<typename T, Transformation<T> F>
bool circular_nonterminating_orbit(const T& x, F f)
{
return x == f(collision_point_nonterminating_orbit(x, f));
}
template<Transformation F>
domain_t<F> connection_point_nonterminating_orbit(const domain_t<F>& x, F f)
template<typename T, Transformation<T> F>
T connection_point_nonterminating_orbit(const T& x, F f)
{
return convergant_point(x, f(collision_point_nonterminating_orbit(x, f)), f);
}
template<Transformation F>
std::tuple<distance_t<F>, distance_t<F>, domain_t<F>>
orbit_structure_nonterminating_orbit(const domain_t<F>& x, F f)
template<typename T, Transformation<T> F>
std::tuple<distance_t<F>, distance_t<F>, T>
orbit_structure_nonterminating_orbit(const T& x, F f)
{
const auto y = connection_point_nonterminating_orbit(x, f);
return {distance(x, y, f), distance(f(y), y, f), y};
}
template<Transformation F, Integer N>
domain_t<F> power_unary(domain_t<F> x, N n, F f)
template<typename T, Transformation<T> F, Integer N>
T power_unary(T x, N n, F f)
{
while (!zero(n)) {
n = predecessor(n);
@@ -138,25 +139,22 @@ domain_t<F> power_unary(domain_t<F> x, N n, F f)
return x;
}
template<Integer I, BinaryOperation Op>
domain_t<Op> power_left_associated(domain_t<Op> a, I n, Op op)
template<typename T, Integer I, BinaryOperation<T> Op>
T power_left_associated(T a, I n, Op op)
{
assert(n > 0);
return one(n) ? a : op(power_left_associated(a, predecessor(n), op), a);
}
template<Integer I, BinaryOperation Op>
domain_t<Op> power_right_associated(domain_t<Op> a, I n, Op op)
template<typename T, Integer I, BinaryOperation<T> Op>
T power_right_associated(T a, I n, Op op)
{
assert(n > 0);
return one(n) ? a : op(a, power_right_associated(a, predecessor(n), op));
}
template<Integer I, AssociativeBinaryOperation Op>
domain_t<Op> power_accumulate_positive(domain_t<Op> r,
domain_t<Op> a,
I n,
Op op)
template<typename T, Integer I, AssociativeBinaryOperation<T> Op>
T power_accumulate_positive(T r, T a, I n, Op op)
{
assert(n > 0);
while (true) {
@@ -171,15 +169,15 @@ domain_t<Op> power_accumulate_positive(domain_t<Op> r,
}
}
template<Integer I, AssociativeBinaryOperation Op>
domain_t<Op> power_accumulate(domain_t<Op> r, domain_t<Op> a, I n, Op op)
template<typename T, Integer I, AssociativeBinaryOperation<T> Op>
T power_accumulate(T r, T a, I n, Op op)
{
assert(n >= 0);
return zero(n) ? r : power_accumulate_positive(r, a, n, op);
}
template<Integer I, AssociativeBinaryOperation Op>
domain_t<Op> power(domain_t<Op> a, I n, Op op)
template<typename T, Integer I, AssociativeBinaryOperation<T> Op>
T power(T a, I n, Op op)
{
assert(n > 0);
while (even(n)) {
@@ -191,8 +189,8 @@ domain_t<Op> power(domain_t<Op> a, I n, Op op)
return zero(n) ? a : power_accumulate_positive(a, op(a, a), n, op);
}
template<Integer I, AssociativeBinaryOperation Op>
domain_t<Op> power(domain_t<Op> a, I n, Op op, domain_t<Op> id)
template<typename T, Integer I, AssociativeBinaryOperation<T> Op>
T power(T a, I n, Op op, T id)
{
assert(n >= 0);
return !zero(n) ? power(a, n, op) : id;
@@ -210,26 +208,28 @@ template<Integer I>
I fibonacci(I n)
{
assert(n >= 0);
return !zero(n) ? power({I {1}, I {0}}, n, fibonacci_matrix_multiply<I>).first
: I {0};
return !zero(n)
? power<std::pair<I, I>>({I {1}, I {0}}, n, fibonacci_matrix_multiply<I>)
.first
: I {0};
}
template<Relation R>
template<typename T, Relation<T> R>
auto complement(R r)
{
return [r](const domain_t<R>& a, const domain_t<R>& b) { return !r(a, b); };
return [r](const T& a, const T& b) { return !r(a, b); };
}
template<Relation R>
template<typename T, Relation<T> R>
auto converse(R r)
{
return [r](const domain_t<R>& a, const domain_t<R>& b) { return r(b, a); };
return [r](const T& a, const T& b) { return r(b, a); };
}
template<Relation R>
template<typename T, Relation<T> R>
auto complement_of_converse(R r)
{
return [r](const domain_t<R>& a, const domain_t<R>& b) { return !r(b, a); };
return [r](const T& a, const T& b) { return !r(b, a); };
}
} // namespace based
diff --git a/include/based/type_traits.hpp b/include/based/type_traits.hpp
@@ -107,325 +107,134 @@ concept ReadableIterator = requires {
// clang-format on
template<typename T>
concept Input =
std::is_same_v<T,
std::remove_cvref_t<std::remove_pointer_t<T>>>
concept Input = std::is_same_v<T, std::remove_cvref_t<std::remove_pointer_t<T>>>
|| std::is_const_v<std::remove_reference_t<T>>
|| std::is_const_v<std::remove_pointer_t<T>>;
namespace detail
{
template<typename Fun>
concept FreeProcedure = std::is_function_v<std::remove_pointer_t<Fun>>;
template<typename Fun>
concept MemberProcedure = std::is_member_function_pointer_v<std::decay_t<Fun>>;
template<typename Fun>
concept FunctorProcedure = std::is_class_v<std::decay_t<Fun>>
&& requires(Fun&& t) { &std::decay_t<Fun>::operator(); };
template<class>
struct is_regular_tuple : std::false_type
{
};
template<template<class...> class Tuple, class... Types>
requires(Regular<std::remove_cvref_t<Types>> && ...)
struct is_regular_tuple<Tuple<Types...>> : std::true_type
{
};
template<class T>
inline constexpr bool is_regular_tuple_v = is_regular_tuple<T>::value;
template<class>
struct is_input_tuple : std::false_type
{
};
template<template<class...> class Tuple, class... Types>
requires(Input<Types> && ...)
struct is_input_tuple<Tuple<Types...>> : std::true_type
{
};
template<class T>
inline constexpr bool is_input_tuple_v = is_input_tuple<T>::value;
template<class>
struct is_homogenous_tuple : std::false_type
{
};
template<template<class...> class Tuple, typename Head, typename... Tail>
requires(std::same_as<Head, Tail> && ...)
struct is_homogenous_tuple<Tuple<Head, Tail...>> : std::true_type
{
};
template<class T>
inline constexpr bool is_homogenous_tuple_v = is_homogenous_tuple<T>::value;
template<typename>
struct signature;
template<typename Ret, typename... Args>
struct signature<Ret(Args...)>
{
using arg_type = std::tuple<Args...>;
using ret_type = Ret;
using const_value = std::false_type;
using volataile_value = std::false_type;
};
template<typename Ret, typename Obj, typename... Args>
struct signature<Ret (Obj::*)(Args...)>
{
using arg_type = std::tuple<Args...>;
using ret_type = Ret;
using const_value = std::false_type;
using volataile_value = std::false_type;
};
template<typename Ret, typename Obj, typename... Args>
struct signature<Ret (Obj::*)(Args...) const>
{
using arg_type = std::tuple<Args...>;
using ret_type = Ret;
using const_value = std::true_type;
using volataile_value = std::false_type;
};
template<typename Ret, typename Obj, typename... Args>
struct signature<Ret (Obj::*)(Args...) volatile>
{
using arg_type = std::tuple<Args...>;
using ret_type = Ret;
using const_value = std::false_type;
using volataile_value = std::true_type;
};
template<typename Ret, typename Obj, typename... Args>
struct signature<Ret (Obj::*)(Args...) const volatile>
{
using arg_type = std::tuple<Args...>;
using ret_type = Ret;
using const_value = std::true_type;
using volataile_value = std::true_type;
};
template<typename>
struct domain;
template<FreeProcedure P>
struct domain<P>
{
using type = signature<std::remove_pointer_t<P>>::arg_type;
};
template<FunctorProcedure P>
struct domain<P>
{
using type = signature<decltype(&std::decay_t<P>::operator())>::arg_type;
};
template<MemberProcedure P>
struct domain<P>
{
using type = signature<std::decay_t<P>>::arg_type;
};
template<typename>
struct codomain;
template<FreeProcedure P>
struct codomain<P>
template<typename... Args>
struct is_homogeneous_domain : std::false_type
{
using type = signature<std::remove_pointer_t<P>>::ret_type;
};
template<FunctorProcedure P>
struct codomain<P>
template<typename Head, typename... Args>
requires(std::same_as<Head, Args> && ...)
struct is_homogeneous_domain<Head, Args...> : std::true_type
{
using type = signature<decltype(&std::decay_t<P>::operator())>::ret_type;
};
template<MemberProcedure P>
struct codomain<P>
{
using type = signature<std::decay_t<P>>::ret_type;
};
template<typename P>
using domain_t = domain<P>::type;
} // namespace detail
template<typename P>
using codomain_t = codomain<P>::type;
template<typename... Args>
concept RegularDomain =
requires { requires(Regular<std::remove_cvref_t<Args>> && ...); };
template<class T>
concept RegularDomain = is_regular_tuple_v<domain_t<T>>;
template<typename... Args>
concept InputDomain = requires { requires(Input<Args> && ...); };
template<class T>
concept InputDomain = is_input_tuple_v<domain_t<T>>;
template<typename... Args>
concept HomogenousDomain = detail::is_homogeneous_domain<Args...>::value;
template<class T>
concept HomogenousDomain = is_input_tuple_v<domain_t<T>>;
template<typename T>
using distance_t = std::uint64_t;
} // namespace detail
template<typename P, typename... Args>
concept Procedure = std::invocable<P, Args...>;
template<typename P>
inline constexpr auto arity_v = std::tuple_size<detail::domain_t<P>>::value;
template<typename P, typename... Args>
requires Procedure<P, Args...>
inline constexpr auto arity_v = std::tuple_size<std::tuple<Args...>>::value;
template<typename P, std::size_t Idx>
requires requires { Idx < arity_v<P>; }
template<typename P, std::size_t Idx, typename... Args>
requires Procedure<P, Args...> && requires { Idx < arity_v<Args...>; }
using domain_elem_t =
std::decay_t<std::tuple_element_t<Idx, detail::domain_t<P>>>;
template<typename P>
using domain_t =
std::conditional_t<detail::is_homogenous_tuple_v<detail::domain_t<P>>,
domain_elem_t<P, 0>,
detail::domain_t<P>>;
template<typename P>
using codomain_t = detail::codomain_t<P>;
template<typename T>
using distance_t = std::uint64_t;
std::decay_t<std::tuple_element_t<Idx, std::tuple<Args...>>>;
template<typename P>
concept Procedure = detail::FreeProcedure<P> || detail::MemberProcedure<P>
|| detail::FunctorProcedure<P>;
template<typename P, typename... Args>
requires Procedure<P, Args...>
using codomain_t = std::invoke_result_t<P, Args...>;
template<typename P>
template<typename P, typename Arg>
concept UnaryProcedure = requires {
requires(Procedure<P>);
requires(arity_v<P> == 1);
requires(Procedure<P, Arg>);
requires(arity_v<P, Arg> == 1);
};
template<typename P>
template<typename P, typename... Args>
concept RegularProcedure = requires {
requires(Procedure<P>);
requires(detail::RegularDomain<P>);
requires(Regular<codomain_t<P>>);
requires(Procedure<P, Args...>);
requires(RegularDomain<Args...>);
requires(Regular<codomain_t<P, Args...>>);
};
template<typename P>
template<typename P, typename... Args>
concept FunctionalProcedure = requires {
requires(RegularProcedure<P>);
requires(detail::InputDomain<P>);
requires(RegularProcedure<P, Args...>);
requires(InputDomain<Args...>);
};
template<typename P>
template<typename P, typename Arg>
concept UnaryFunction = requires {
requires(FunctionalProcedure<P>);
requires(UnaryProcedure<P>);
requires(FunctionalProcedure<P, Arg>);
requires(UnaryProcedure<P, Arg>);
};
template<typename P>
template<typename P, typename... Args>
concept HomogeneousFunction = requires {
requires(FunctionalProcedure<P>);
requires(arity_v<P> > 0);
requires(detail::HomogenousDomain<P>);
requires(FunctionalProcedure<P, Args...>);
requires(arity_v<P, Args...> > 0);
requires(HomogenousDomain<Args...>);
};
template<typename P>
template<typename P, typename... Args>
concept Predicate = requires {
requires(FunctionalProcedure<P>);
requires(std::same_as<bool, codomain_t<P>>);
requires(FunctionalProcedure<P, Args...>);
requires(std::same_as<bool, codomain_t<P, Args...>>);
};
template<typename P>
template<typename P, typename... Args>
concept HomogeneousPredicate = requires {
requires(Predicate<P>);
requires(HomogeneousFunction<P>);
requires(Predicate<P, Args...>);
requires(HomogeneousFunction<P, Args...>);
};
template<typename P>
template<typename P, typename Arg>
concept UnaryPredicate = requires {
requires(Predicate<P>);
requires(UnaryFunction<P>);
requires(Predicate<P, Arg>);
requires(UnaryFunction<P, Arg>);
};
template<typename P>
template<typename P, typename... Args>
concept Operation = requires {
requires(HomogeneousFunction<P>);
requires(BareSameAs<codomain_t<P>, domain_t<P>>);
requires(HomogeneousFunction<P, Args...>);
requires(BareSameAs<codomain_t<P, Args...>,
std::tuple_element_t<0, std::tuple<Args...>>>);
};
template<typename P>
template<typename P, typename Arg>
concept Transformation = requires {
requires(Operation<P>);
requires(UnaryFunction<P>);
requires(Operation<P, Arg>);
requires(UnaryFunction<P, Arg>);
};
template<typename P>
template<typename P, typename Arg>
concept BinaryOperation = requires {
requires(Operation<P>);
requires(arity_v<P> == 2);
requires(Operation<P, Arg, Arg>);
requires(arity_v<P, Arg, Arg> == 2);
};
template<typename P>
template<typename P, typename Arg>
concept AssociativeBinaryOperation = requires {
requires(BinaryOperation<P>);
// requires(P is associative)
requires(Operation<P, Arg, Arg>);
requires(arity_v<P, Arg, Arg> == 2);
};
template<typename P>
template<typename P, typename Arg>
concept Relation = requires {
requires(HomogeneousPredicate<P>);
requires(arity_v<P> == 2);
};
/* ----- Value variants ----- */
template<typename P, typename V>
concept ValUnaryProcedure = requires {
requires(UnaryProcedure<P>);
requires(SameAs<V, domain_t<P>>);
};
template<typename P, typename V>
concept ValUnaryFunction = requires {
requires(UnaryFunction<P>);
requires(SameAs<V, domain_t<P>>);
};
template<typename P, typename V>
concept ValHomogeneousPredicate = requires {
requires(HomogeneousPredicate<P>);
requires(SameAs<V, domain_t<P>>);
};
template<typename P, typename V>
concept ValUnaryPredicate = requires {
requires(UnaryPredicate<P>);
requires(SameAs<V, domain_t<P>>);
};
template<typename P, typename V>
concept ValOperation = requires {
requires(Operation<P>);
requires(SameAs<V, domain_t<P>>);
};
template<typename P, typename V>
concept ValBinaryOperation = requires {
requires(Operation<P>);
requires(SameAs<V, domain_t<P>>);
};
template<typename P, typename V>
concept ValRelation = requires {
requires(Relation<P>);
requires(SameAs<V, domain_t<P>>);
requires(HomogeneousPredicate<P, Arg, Arg>);
requires(arity_v<P, Arg, Arg> == 2);
};
/* ----- Iterator variants ----- */
@@ -433,52 +242,43 @@ concept ValRelation = requires {
template<typename P, typename I>
concept IterUnaryProcedure = requires {
requires(Iterator<I>);
requires(ValUnaryProcedure<P, iter_value_t<I>>);
requires(UnaryProcedure<P, iter_value_t<I>>);
};
template<typename P, typename I>
concept IterUnaryFunction = requires {
requires(Iterator<I>);
requires(ValUnaryFunction<P, iter_value_t<I>>);
requires(UnaryFunction<P, iter_value_t<I>>);
};
template<typename P, typename I>
template<typename P, typename... I>
concept IterHomogeneousPredicate = requires {
requires(Iterator<I>);
requires(ValHomogeneousPredicate<P, iter_value_t<I>>);
requires(Iterator<I> && ...);
requires(HomogeneousPredicate<P, iter_value_t<I>...>);
};
template<typename P, typename I>
concept IterUnaryPredicate = requires {
requires(Iterator<I>);
requires(ValUnaryPredicate<P, iter_value_t<I>>);
requires(UnaryPredicate<P, iter_value_t<I>>);
};
template<typename P, typename I>
template<typename P, typename... I>
concept IterOperation = requires {
requires(Iterator<I>);
requires(ValOperation<P, iter_value_t<I>>);
requires(Iterator<I> && ...);
requires(Operation<P, iter_value_t<I>...>);
};
template<typename P, typename I>
concept IterBinaryOperation = requires {
requires(Iterator<I>);
requires(ValBinaryOperation<P, iter_value_t<I>>);
requires(BinaryOperation<P, iter_value_t<I>>);
};
template<typename P, typename I>
concept IterRelation = requires {
requires(Iterator<I>);
requires(ValRelation<P, iter_value_t<I>>);
};
/* ----- Transformation Variant ----- */
template<typename P, typename T>
concept TransformUnaryPredicate = requires {
requires(UnaryPredicate<P>);
requires(Transformation<T>);
requires(SameAs<domain_t<T>, domain_t<P>>);
requires(Relation<P, iter_value_t<I>>);
};
/* ----- Abstract Algebra ----- */