forked from mpusz/mp-units
Add make_ratio() helper and prime factorization
This commit is contained in:
@@ -80,8 +80,10 @@ 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>
|
template <std::intmax_t N, std::intmax_t D = 1>
|
||||||
using ratio_t = quotient_t<detail::prime_factorization_t<num>, detail::prime_factorization_t<den>>;
|
auto make_ratio() {
|
||||||
|
return quotient_t<detail::prime_factorization_t<N>, detail::prime_factorization_t<D>>{};
|
||||||
|
}
|
||||||
|
|
||||||
struct pi {
|
struct pi {
|
||||||
static inline constexpr long double value = std::numbers::pi_v<long double>;
|
static inline constexpr long double value = std::numbers::pi_v<long double>;
|
||||||
@@ -207,4 +209,62 @@ struct product<T, U, Tail...>
|
|||||||
using type = product_t<product_t<T, U>, Tail...>;
|
using type = product_t<product_t<T, U>, Tail...>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Prime factorization implementation.
|
||||||
|
|
||||||
|
// Find the smallest prime factor of `n`.
|
||||||
|
constexpr std::intmax_t find_first_factor(std::intmax_t n)
|
||||||
|
{
|
||||||
|
for (std::intmax_t f = 2; f * f <= n; f += 1 + (f % 2))
|
||||||
|
{
|
||||||
|
if (n % f == 0) { return f; }
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The exponent of `factor` in the prime factorization of `n`.
|
||||||
|
constexpr std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
|
||||||
|
{
|
||||||
|
std::intmax_t m = 0;
|
||||||
|
while (n % factor == 0)
|
||||||
|
{
|
||||||
|
n /= factor;
|
||||||
|
++m;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divide a number by a given base raised to some power.
|
||||||
|
//
|
||||||
|
// Undefined unless base > 1, pow >= 0, and (base ^ pow) evenly divides n.
|
||||||
|
constexpr std::intmax_t remove_power(std::intmax_t base, std::intmax_t pow, std::intmax_t n)
|
||||||
|
{
|
||||||
|
while (pow-- > 0)
|
||||||
|
{
|
||||||
|
n /= base;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialization for the prime factorization of 1 (base case).
|
||||||
|
template <>
|
||||||
|
struct prime_factorization<1> { using type = magnitude<>; };
|
||||||
|
|
||||||
|
// Specialization for the prime factorization of larger numbers (recursive case).
|
||||||
|
template <std::intmax_t N>
|
||||||
|
struct prime_factorization {
|
||||||
|
static_assert(N > 1, "Can only factor positive integers.");
|
||||||
|
|
||||||
|
static inline constexpr std::intmax_t first_base = find_first_factor(N);
|
||||||
|
static inline constexpr std::intmax_t first_power = multiplicity(first_base, N);
|
||||||
|
static inline constexpr std::intmax_t remainder = remove_power(first_base, first_power, N);
|
||||||
|
|
||||||
|
using type = product_t<
|
||||||
|
magnitude<int_base_power<first_base, first_power>>, prime_factorization_t<remainder>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
} // namespace units::mag
|
} // namespace units::mag
|
||||||
|
@@ -25,8 +25,8 @@
|
|||||||
#include <catch2/catch.hpp>
|
#include <catch2/catch.hpp>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
using namespace units;
|
namespace units::mag
|
||||||
using namespace units::mag;
|
{
|
||||||
|
|
||||||
TEST_CASE("Magnitude is invertible")
|
TEST_CASE("Magnitude is invertible")
|
||||||
{
|
{
|
||||||
@@ -211,16 +211,74 @@ TEST_CASE("strictly_increasing")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEST_CASE("Ratio shortcut performs prime factorization")
|
TEST_CASE("make_ratio performs prime factorization correctly")
|
||||||
// {
|
{
|
||||||
// // CHECK(std::is_same_v<ratio<>, magnitude<>>);
|
SECTION("Performs prime factorization when denominator is 1")
|
||||||
// // CHECK(std::is_same_v<ratio<2>, magnitude<int_base_power<2>>>);
|
{
|
||||||
// }
|
CHECK(std::is_same_v<decltype(make_ratio<1>()), magnitude<>>);
|
||||||
//
|
CHECK(std::is_same_v<decltype(make_ratio<2>()), magnitude<int_base_power<2>>>);
|
||||||
// TEST_CASE("Equality works for ratios")
|
CHECK(std::is_same_v<decltype(make_ratio<3>()), magnitude<int_base_power<3>>>);
|
||||||
// {
|
CHECK(std::is_same_v<decltype(make_ratio<4>()), magnitude<int_base_power<2, 2>>>);
|
||||||
// CHECK(ratio<>{} == ratio<>{});
|
|
||||||
// CHECK(ratio<3>{} == ratio<3>{});
|
CHECK(std::is_same_v<
|
||||||
//
|
decltype(make_ratio<792>()),
|
||||||
// // CHECK(ratio<3>{} != ratio<4>{});
|
magnitude<int_base_power<2, 3>, int_base_power<3, 2>, int_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<int_base_power<2, -3>, int_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>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
TEST_CASE("Prime factorization")
|
||||||
|
{
|
||||||
|
SECTION ("1 factors into the null magnitude")
|
||||||
|
{
|
||||||
|
CHECK(std::is_same_v<prime_factorization_t<1>, magnitude<>>);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION ("Prime numbers factor into themselves")
|
||||||
|
{
|
||||||
|
CHECK(std::is_same_v<prime_factorization_t<2>, magnitude<int_base_power<2>>>);
|
||||||
|
CHECK(std::is_same_v<prime_factorization_t<3>, magnitude<int_base_power<3>>>);
|
||||||
|
CHECK(std::is_same_v<prime_factorization_t<5>, magnitude<int_base_power<5>>>);
|
||||||
|
CHECK(std::is_same_v<prime_factorization_t<7>, magnitude<int_base_power<7>>>);
|
||||||
|
CHECK(std::is_same_v<prime_factorization_t<11>, magnitude<int_base_power<11>>>);
|
||||||
|
|
||||||
|
CHECK(std::is_same_v<prime_factorization_t<41>, magnitude<int_base_power<41>>>);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Prime factorization finds factors and multiplicities")
|
||||||
|
{
|
||||||
|
CHECK(std::is_same_v<
|
||||||
|
prime_factorization_t<792>,
|
||||||
|
magnitude<int_base_power<2, 3>, int_base_power<3, 2>, int_base_power<11>>>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace units::mag
|
||||||
|
Reference in New Issue
Block a user