Add concept for magnitude

This commit is contained in:
Chip Hogg
2021-12-29 11:38:56 -05:00
parent 0c5ae082d5
commit 0566158e91
2 changed files with 107 additions and 13 deletions

View File

@@ -24,6 +24,7 @@
#include <units/ratio.h>
#include <cstdint>
#include <numbers>
namespace units::mag
{
@@ -33,6 +34,21 @@ namespace units::mag
template <typename... base_powers>
struct magnitude;
template <typename base, ratio power = ratio{1}>
struct base_power;
template <typename T>
struct is_magnitude;
template <typename T>
inline constexpr bool is_magnitude_v = is_magnitude<T>::value;
template <typename T>
concept Magnitude = is_magnitude_v<T>;
template <std::intmax_t n>
struct int_base : std::integral_constant<std::intmax_t, n> {};
template <std::intmax_t n, std::intmax_t num = 1, std::intmax_t den = 1>
using int_base_power = base_power<int_base<n>, ratio{num, den}>;
template <typename mag_t>
struct inverse;
template <typename mag_t>
@@ -54,6 +70,17 @@ template <std::intmax_t n>
using prime_factorization_t = typename prime_factorization<n>::type;
} // namespace detail
template <std::intmax_t num = 1, std::intmax_t den = 1>
using ratio_t = quotient_t<detail::prime_factorization_t<num>, detail::prime_factorization_t<den>>;
struct pi {
static inline constexpr long double value = std::numbers::pi_v<long double>;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation details below.
////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename... base_powers>
struct magnitude {
bool operator==(magnitude) const { return true; }
@@ -62,23 +89,31 @@ struct magnitude {
bool operator==(magnitude<other_base_powers...>) const { return false; }
};
template <std::intmax_t num = 1, std::intmax_t den = 1>
using ratio_t = quotient_t<detail::prime_factorization_t<num>, detail::prime_factorization_t<den>>;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation details below.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude concept implementation.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Base powers.
template <typename T>
struct is_magnitude: std::false_type {};
template <typename base, ratio power = ratio{1}>
struct base_power;
// Check whether a tuple of (possibly heterogeneously typed) values are strictly increasing.
template <typename... Ts>
constexpr bool strictly_increasing(const std::tuple<Ts...> &ts) {
// Carefully handle different sizes, avoiding unsigned integer underflow.
constexpr auto num_comparisons = [](auto num_elements) {
return (num_elements > 1) ? (num_elements - 1) : 0;
}(sizeof...(Ts));
template <std::intmax_t n>
struct int_base : std::integral_constant<std::intmax_t, n> {};
template <std::intmax_t n, std::intmax_t num = 1, std::intmax_t den = 1>
using int_base_power = base_power<int_base<n>, ratio{num, den}>;
// Compare zero or more pairs of neighbours as needed.
return [&ts]<std::size_t... Is>(std::integer_sequence<std::size_t, Is...>) {
return ((std::get<Is>(ts) < std::get<Is + 1>(ts)) && ...);
}(std::make_index_sequence<num_comparisons>());
}
template <typename... bases, ratio... powers>
struct is_magnitude<magnitude<base_power<bases, powers>...>>
: std::bool_constant<(
strictly_increasing(std::make_tuple(bases::value...))
&& ((powers.num != 0) && ...))> {};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Inverse implementation.

View File

@@ -115,6 +115,65 @@ TEST_CASE("Magnitude supports products")
}
}
TEST_CASE("is_magnitude detects well formed magnitudes")
{
SECTION ("Arbitrary other types are not magnitudes")
{
CHECK(!is_magnitude_v<void>);
CHECK(!is_magnitude_v<int>);
CHECK(!is_magnitude_v<int_base_power<3, 1, 4>>);
}
SECTION ("Null magnitude is magnitude")
{
CHECK(is_magnitude_v<magnitude<>>);
}
SECTION ("Single-base magnitude is magnitude")
{
CHECK(is_magnitude_v<magnitude<int_base_power<3, 1, 4>>>);
}
SECTION ("Out-of-order bases disqualify magnitudes")
{
CHECK(!is_magnitude_v<magnitude<int_base_power<3>, int_base_power<2>>>);
}
SECTION ("Repeated bases disqualify magnitudes")
{
CHECK(!is_magnitude_v<magnitude<int_base_power<2, 1>, int_base_power<2, 2>>>);
}
SECTION ("Mixed base types are magnitudes if sorted")
{
CHECK(is_magnitude_v<magnitude<int_base_power<2>, base_power<pi>>>);
CHECK(is_magnitude_v<magnitude<int_base_power<3>, base_power<pi>>>);
CHECK(!is_magnitude_v<magnitude<int_base_power<5>, base_power<pi>>>);
}
}
TEST_CASE("strictly_increasing")
{
SECTION ("Empty tuple is sorted")
{
CHECK(strictly_increasing(std::make_tuple()));
}
SECTION ("Single-element tuple is sorted")
{
CHECK(strictly_increasing(std::make_tuple(3)));
CHECK(strictly_increasing(std::make_tuple(15.42)));
CHECK(strictly_increasing(std::make_tuple('c')));
}
SECTION ("Multi-element tuples compare correctly")
{
CHECK(strictly_increasing(std::make_tuple(3, 3.14)));
CHECK(!strictly_increasing(std::make_tuple(3, 3.0)));
CHECK(!strictly_increasing(std::make_tuple(4, 3.0)));
}
}
// TEST_CASE("Ratio shortcut performs prime factorization")
// {
// // CHECK(std::is_same_v<ratio<>, magnitude<>>);