Finish migrating base_power to NTTP

This commit is contained in:
Chip Hogg
2022-01-08 15:08:05 -05:00
parent 9d48e6983a
commit 838b132a61
2 changed files with 74 additions and 67 deletions

View File

@@ -44,7 +44,7 @@ constexpr bool is_prime(std::intmax_t n);
// Integer rep is for prime numbers; long double is for any irrational base we permit.
template<typename T>
concept BaseRep = std::is_same_v<T, int> || std::is_same_v<T::value, long double>;
concept BaseRep = std::is_same_v<T, int> || std::is_same_v<std::remove_cvref_t<decltype(T::value)>, long double>;
/**
* @brief A basis vector in our magnitude representation, raised to some rational power.
@@ -85,7 +85,7 @@ base_power(T) -> base_power<T>;
template<typename T, typename U>
constexpr bool operator==(base_power<T> t, base_power<U> u) {
return std::is_same_v<T, U> && (t.base == u.base) && (t.power == u.power);
return std::is_same_v<T, U> && (t.get_base() == u.get_base()) && (t.power == u.power);
}
template<BaseRep T>
@@ -100,8 +100,8 @@ template<BaseRep T>
constexpr bool is_valid_base_power(const base_power<T> &bp) {
if (bp.power == 0) { return false; }
if constexpr (std::is_same_v<T, int>) { return is_prime(bp.base); }
else if constexpr (std::is_same_v<T, long double>) { return bp.base > 0; }
if constexpr (std::is_same_v<T, int>) { return is_prime(bp.get_base()); }
else if constexpr (std::is_same_v<T, long double>) { return bp.get_base() > 0; }
else { return false; } // Unreachable.
}
@@ -114,24 +114,29 @@ struct is_base_power<base_power<T>> : std::true_type {};
template<typename T>
concept BasePower = detail::is_base_power<T>::value;
template<typename... Ts>
constexpr bool strictly_increasing(Ts&&... ts);
/**
* @brief A representation for positive real numbers which optimizes taking products and rational powers.
*
* Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, and compare
* for equality using this value API.
*/
template<BasePower auto... BasePowers>
template<BasePower auto... BPs>
requires requires {
// (is_valid_base_power(BasePowers) && ... && strictly_increasing(BasePowers.base...));
(detail::is_valid_base_power(BasePowers) && ...);
(detail::is_valid_base_power(BPs) && ... && strictly_increasing(BPs.get_base()...));
}
struct magnitude {};
template<BasePower auto... LeftBPs, BasePower auto... RightBPs>
constexpr bool operator==(magnitude<LeftBPs...>, magnitude<RightBPs...>) { return ((LeftBPs == RightBPs) && ...); }
constexpr bool operator==(magnitude<LeftBPs...>, magnitude<RightBPs...>) {
if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) { return ((LeftBPs == RightBPs) && ...); }
else { return false; }
}
template<BasePower auto... BasePowers>
constexpr auto inverse(magnitude<BasePowers...>) { return magnitude<inverse(BasePowers)...>{}; }
template<BasePower auto... BPs>
constexpr auto inverse(magnitude<BPs...>) { return magnitude<inverse(BPs)...>{}; }
constexpr auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; }
@@ -144,20 +149,25 @@ constexpr auto operator*(magnitude<BPs...> m, magnitude<>) { return m; }
template<BasePower auto H1, BasePower auto... T1, BasePower auto H2, BasePower auto... T2>
constexpr auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>) {
// Shortcut for prepending, which makes it easier to implement some of the other cases.
if constexpr ((sizeof...(T1) == 0) && H1.base < H2.base) { return magnitude<H1, H2, T2...>{}; }
if constexpr ((sizeof...(T1) == 0) && H1.get_base() < H2.get_base()) { return magnitude<H1, H2, T2...>{}; }
if constexpr (H1.base == H2.base) {
if constexpr (H1.get_base() == H2.get_base()) {
constexpr auto partial_product = magnitude<T1...>{} * magnitude<T2...>{};
constexpr base_power new_head{H1.base, (H1.power + H2.power)};
// Make a new base_power with the common base of H1 and H2, whose power is their powers' sum.
constexpr auto new_head = [&](auto head) {
head.power = H1.power + H2.power;
return head;
}(H1);
if constexpr (new_head.power == 0) {
return partial_product;
} else {
return magnitude<new_head>{} * partial_product;
}
} else if constexpr(H1.base < H2.base){
} else if constexpr(H1.get_base() < H2.get_base()){
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
} else { // We know H2.base < H1.base
} else { // We know H2.get_base() < H1.get_base()
return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
}
}
@@ -177,12 +187,12 @@ constexpr auto make_ratio() { return detail::prime_factorization_v<N> / detail::
/**
* @brief A base to represent pi.
*/
template<ratio Power>
requires requires { Power != 0; }
constexpr auto pi_power() { return base_power{std::numbers::pi_v<long double>, Power}; }
struct pi_base {
static constexpr long double value = std::numbers::pi_v<long double>;
};
template<ratio Power>
constexpr auto pi_to_the() { return magnitude<pi_power<Power>()>{}; }
constexpr auto pi_to_the() { return magnitude<base_power<pi_base>{Power}>{}; }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation details below.

View File

@@ -50,49 +50,46 @@ TEST_CASE("strictly_increasing")
}
}
// TEST_CASE("make_ratio performs prime factorization correctly")
// {
// SECTION("Performs prime factorization when denominator is 1")
// {
// CHECK(std::is_same_v<decltype(make_ratio<1>()), magnitude<>>);
// CHECK(std::is_same_v<decltype(make_ratio<2>()), magnitude<base_power{2}>>);
// CHECK(std::is_same_v<decltype(make_ratio<3>()), magnitude<base_power{3}>>);
// CHECK(std::is_same_v<decltype(make_ratio<4>()), magnitude<base_power{2, 2}>>);
//
// CHECK(std::is_same_v<
// decltype(make_ratio<792>()),
// magnitude<base_power{2, 3}, base_power{3, 2}, base_power{11}>>);
// }
//
// SECTION("Reduces fractions to lowest terms")
// {
// CHECK(std::is_same_v<decltype(make_ratio<8, 8>()), magnitude<>>);
// CHECK(std::is_same_v<
// decltype(make_ratio<50, 80>()), magnitude<base_power{2, -3}, base_power{5}>>);
// }
// }
TEST_CASE("make_ratio performs prime factorization correctly")
{
SECTION("Performs prime factorization when denominator is 1")
{
CHECK(make_ratio<1>() == magnitude<>{});
CHECK(make_ratio<2>() == magnitude<base_power{2}>{});
CHECK(make_ratio<3>() == magnitude<base_power{3}>{});
CHECK(make_ratio<4>() == magnitude<base_power{2, 2}>{});
// TEST_CASE("Equality works for magnitudes")
// {
// SECTION("Equivalent ratios are equal")
// {
// CHECK(make_ratio<1>() == make_ratio<1>());
// CHECK(make_ratio<3>() == make_ratio<3>());
// CHECK(make_ratio<3, 4>() == make_ratio<9, 12>());
// }
//
// SECTION("Different ratios are unequal")
// {
// CHECK(make_ratio<3>() != make_ratio<5>());
// CHECK(make_ratio<3>() != make_ratio<3, 2>());
// }
//
// SECTION("Supports constexpr")
// {
// constexpr auto eq = (make_ratio<4, 5>() == make_ratio<4, 3>());
// CHECK(!eq);
// }
// }
CHECK(make_ratio<792>() == magnitude<base_power{2, 3}, base_power{3, 2}, base_power{11}>{});
}
SECTION("Reduces fractions to lowest terms")
{
CHECK(make_ratio<8, 8>() == magnitude<>{});
CHECK(make_ratio<50, 80>() == magnitude<base_power{2, -3}, base_power{5}>{});
}
}
TEST_CASE("Equality works for magnitudes")
{
SECTION("Equivalent ratios are equal")
{
CHECK(make_ratio<1>() == make_ratio<1>());
CHECK(make_ratio<3>() == make_ratio<3>());
CHECK(make_ratio<3, 4>() == make_ratio<9, 12>());
}
SECTION("Different ratios are unequal")
{
CHECK(make_ratio<3>() != make_ratio<5>());
CHECK(make_ratio<3>() != make_ratio<3, 2>());
}
SECTION("Supports constexpr")
{
constexpr auto eq = (make_ratio<4, 5>() == make_ratio<4, 3>());
CHECK(!eq);
}
}
TEST_CASE("Multiplication works for magnitudes")
{
@@ -106,12 +103,12 @@ TEST_CASE("Multiplication works for magnitudes")
CHECK(make_ratio<4, 5>() * make_ratio<4, 3>() == make_ratio<16, 15>());
}
//SECTION("Products handle pi correctly")
//{
// CHECK(
// pi_to_the<1>() * make_ratio<2, 3>() * pi_to_the<ratio{-1, 2}>() ==
// magnitude<base_power{2}, base_power{3, -1}, pi_power<ratio{1, 2}>()>{});
//}
SECTION("Products handle pi correctly")
{
CHECK(
pi_to_the<1>() * make_ratio<2, 3>() * pi_to_the<ratio{-1, 2}>() ==
magnitude<base_power{2}, base_power{3, -1}, base_power<pi_base>{ratio{1, 2}}>{});
}
SECTION("Supports constexpr")
{