forked from mpusz/mp-units
Add concept for magnitude
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <units/ratio.h>
|
#include <units/ratio.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <numbers>
|
||||||
|
|
||||||
namespace units::mag
|
namespace units::mag
|
||||||
{
|
{
|
||||||
@@ -33,6 +34,21 @@ namespace units::mag
|
|||||||
template <typename... base_powers>
|
template <typename... base_powers>
|
||||||
struct magnitude;
|
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>
|
template <typename mag_t>
|
||||||
struct inverse;
|
struct inverse;
|
||||||
template <typename mag_t>
|
template <typename mag_t>
|
||||||
@@ -54,6 +70,17 @@ template <std::intmax_t n>
|
|||||||
using prime_factorization_t = typename prime_factorization<n>::type;
|
using prime_factorization_t = typename prime_factorization<n>::type;
|
||||||
} // namespace detail
|
} // 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>
|
template <typename... base_powers>
|
||||||
struct magnitude {
|
struct magnitude {
|
||||||
bool operator==(magnitude) const { return true; }
|
bool operator==(magnitude) const { return true; }
|
||||||
@@ -62,23 +89,31 @@ struct magnitude {
|
|||||||
bool operator==(magnitude<other_base_powers...>) const { return false; }
|
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.
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
template <typename T>
|
||||||
// Base powers.
|
struct is_magnitude: std::false_type {};
|
||||||
|
|
||||||
template <typename base, ratio power = ratio{1}>
|
// Check whether a tuple of (possibly heterogeneously typed) values are strictly increasing.
|
||||||
struct base_power;
|
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>
|
// Compare zero or more pairs of neighbours as needed.
|
||||||
struct int_base : std::integral_constant<std::intmax_t, n> {};
|
return [&ts]<std::size_t... Is>(std::integer_sequence<std::size_t, Is...>) {
|
||||||
template <std::intmax_t n, std::intmax_t num = 1, std::intmax_t den = 1>
|
return ((std::get<Is>(ts) < std::get<Is + 1>(ts)) && ...);
|
||||||
using int_base_power = base_power<int_base<n>, ratio{num, den}>;
|
}(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.
|
// Inverse implementation.
|
||||||
|
@@ -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")
|
// TEST_CASE("Ratio shortcut performs prime factorization")
|
||||||
// {
|
// {
|
||||||
// // CHECK(std::is_same_v<ratio<>, magnitude<>>);
|
// // CHECK(std::is_same_v<ratio<>, magnitude<>>);
|
||||||
|
Reference in New Issue
Block a user