mirror of
https://github.com/mpusz/mp-units.git
synced 2025-07-31 19:04:27 +02:00
Run clang-format-15 on changed files
This commit is contained in:
@@ -29,12 +29,14 @@
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
|
||||
namespace units::detail
|
||||
{
|
||||
namespace units::detail {
|
||||
|
||||
constexpr bool is_prime_by_trial_division(std::size_t n) {
|
||||
constexpr bool is_prime_by_trial_division(std::size_t n)
|
||||
{
|
||||
for (std::size_t f = 2; f * f <= n; f += 1 + (f % 2)) {
|
||||
if (n % f == 0) { return false; }
|
||||
if (n % f == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -42,25 +44,34 @@ constexpr bool is_prime_by_trial_division(std::size_t n) {
|
||||
// Return the first factor of n, as long as it is either k or n.
|
||||
//
|
||||
// Precondition: no integer smaller than k evenly divides n.
|
||||
constexpr std::optional<std::size_t> first_factor_maybe(std::size_t n, std::size_t k) {
|
||||
if (n % k == 0) { return k; }
|
||||
if (k * k > n) { return n; }
|
||||
constexpr std::optional<std::size_t> first_factor_maybe(std::size_t n, std::size_t k)
|
||||
{
|
||||
if (n % k == 0) {
|
||||
return k;
|
||||
}
|
||||
if (k * k > n) {
|
||||
return n;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
constexpr std::array<std::size_t, N> first_n_primes() {
|
||||
constexpr std::array<std::size_t, N> first_n_primes()
|
||||
{
|
||||
std::array<std::size_t, N> primes;
|
||||
primes[0] = 2;
|
||||
for (std::size_t i = 1; i < N; ++i) {
|
||||
primes[i] = primes[i - 1] + 1;
|
||||
while (!is_prime_by_trial_division(primes[i])) { ++primes[i]; }
|
||||
while (!is_prime_by_trial_division(primes[i])) {
|
||||
++primes[i];
|
||||
}
|
||||
}
|
||||
return primes;
|
||||
}
|
||||
|
||||
template<std::size_t N, typename Callable>
|
||||
constexpr void call_for_coprimes_up_to(std::size_t n, const std::array<std::size_t, N> &basis, Callable &&call) {
|
||||
constexpr void call_for_coprimes_up_to(std::size_t n, const std::array<std::size_t, N>& basis, Callable&& call)
|
||||
{
|
||||
for (std::size_t i = 0u; i < n; ++i) {
|
||||
if (std::apply([&i](auto... primes) { return ((i % primes != 0) && ...); }, basis)) {
|
||||
call(i);
|
||||
@@ -69,14 +80,16 @@ constexpr void call_for_coprimes_up_to(std::size_t n, const std::array<std::size
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
constexpr std::size_t num_coprimes_up_to(std::size_t n, const std::array<std::size_t, N> &basis) {
|
||||
constexpr std::size_t num_coprimes_up_to(std::size_t n, const std::array<std::size_t, N>& basis)
|
||||
{
|
||||
std::size_t count = 0u;
|
||||
call_for_coprimes_up_to(n, basis, [&count](auto) { ++count; });
|
||||
return count;
|
||||
}
|
||||
|
||||
template<std::size_t ResultSize, std::size_t N>
|
||||
constexpr auto coprimes_up_to(std::size_t n, const std::array<std::size_t, N> &basis) {
|
||||
constexpr auto coprimes_up_to(std::size_t n, const std::array<std::size_t, N>& basis)
|
||||
{
|
||||
std::array<std::size_t, ResultSize> coprimes;
|
||||
std::size_t i = 0u;
|
||||
|
||||
@@ -86,7 +99,8 @@ constexpr auto coprimes_up_to(std::size_t n, const std::array<std::size_t, N> &b
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
constexpr std::size_t product(const std::array<std::size_t, N> &values) {
|
||||
constexpr std::size_t product(const std::array<std::size_t, N>& values)
|
||||
{
|
||||
return std::accumulate(std::begin(values), std::end(values), std::size_t{1u}, std::multiplies{});
|
||||
}
|
||||
|
||||
@@ -116,29 +130,36 @@ struct WheelFactorizer {
|
||||
static constexpr auto coprimes_in_first_wheel =
|
||||
coprimes_up_to<num_coprimes_up_to(wheel_size, basis)>(wheel_size, basis);
|
||||
|
||||
static constexpr std::size_t find_first_factor(std::size_t n) {
|
||||
static constexpr std::size_t find_first_factor(std::size_t n)
|
||||
{
|
||||
for (const auto& p : basis) {
|
||||
if (const auto k = first_factor_maybe(n, p)) { return *k; }
|
||||
if (const auto k = first_factor_maybe(n, p)) {
|
||||
return *k;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = std::next(std::begin(coprimes_in_first_wheel)); it != std::end(coprimes_in_first_wheel); ++it) {
|
||||
if (const auto k = first_factor_maybe(n, *it)) { return *k; }
|
||||
if (const auto k = first_factor_maybe(n, *it)) {
|
||||
return *k;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t wheel = wheel_size; wheel < n; wheel += wheel_size) {
|
||||
if (const auto k = first_factor_maybe(n, wheel + 1)) { return *k; }
|
||||
if (const auto k = first_factor_maybe(n, wheel + 1)) {
|
||||
return *k;
|
||||
}
|
||||
|
||||
for (const auto &p : coprimes_in_first_wheel) {
|
||||
if (const auto k = first_factor_maybe(n, wheel + p)) { return *k; }
|
||||
for (const auto& p : coprimes_in_first_wheel) {
|
||||
if (const auto k = first_factor_maybe(n, wheel + p)) {
|
||||
return *k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static constexpr bool is_prime(std::size_t n) {
|
||||
return (n > 1) && find_first_factor(n) == n;
|
||||
}
|
||||
static constexpr bool is_prime(std::size_t n) { return (n > 1) && find_first_factor(n) == n; }
|
||||
};
|
||||
|
||||
} // namespace units::detail
|
||||
} // namespace units::detail
|
||||
|
@@ -22,19 +22,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/ratio.h>
|
||||
#include <units/bits/external/hacks.h>
|
||||
#include <units/bits/prime.h>
|
||||
#include <units/ratio.h>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <numbers>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace units {
|
||||
namespace detail
|
||||
{
|
||||
namespace detail {
|
||||
// Higher numbers use fewer trial divisions, at the price of more storage space.
|
||||
using Factorizer = WheelFactorizer<4>;
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Any type which can be used as a basis vector in a BasePower.
|
||||
@@ -52,7 +52,8 @@ using Factorizer = WheelFactorizer<4>;
|
||||
* GCC 10) which don't yet permit floating point NTTPs.
|
||||
*/
|
||||
template<typename T>
|
||||
concept BaseRep = std::is_same_v<T, std::intmax_t> || std::is_same_v<std::remove_cvref_t<decltype(T::value)>, long double>;
|
||||
concept BaseRep = std::is_same_v<T, std::intmax_t> || 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.
|
||||
@@ -114,7 +115,7 @@ template<typename T>
|
||||
inline constexpr bool is_base_power = false;
|
||||
template<BaseRep T>
|
||||
inline constexpr bool is_base_power<base_power<T>> = true;
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Concept to detect whether a _type_ is a valid base power.
|
||||
@@ -125,34 +126,40 @@ inline constexpr bool is_base_power<base_power<T>> = true;
|
||||
template<typename T>
|
||||
concept BasePower = detail::is_base_power<T>;
|
||||
|
||||
namespace detail
|
||||
namespace detail {
|
||||
|
||||
constexpr auto inverse(BasePower auto bp)
|
||||
{
|
||||
constexpr auto inverse(BasePower auto bp) {
|
||||
bp.power.num *= -1;
|
||||
return bp;
|
||||
}
|
||||
|
||||
// `widen_t` gives the widest arithmetic type in the same category, for intermediate computations.
|
||||
template<typename T> requires std::is_arithmetic_v<T>
|
||||
using widen_t = std::conditional_t<
|
||||
std::is_floating_point_v<T>,
|
||||
long double,
|
||||
std::conditional_t<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>;
|
||||
template<typename T>
|
||||
requires std::is_arithmetic_v<T>
|
||||
using widen_t = std::conditional_t<std::is_floating_point_v<T>, long double,
|
||||
std::conditional_t<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>;
|
||||
|
||||
// Raise an arbitrary arithmetic type to a positive integer power at compile time.
|
||||
template<typename T> requires std::is_arithmetic_v<T>
|
||||
constexpr T int_power(T base, std::integral auto exp){
|
||||
template<typename T>
|
||||
requires std::is_arithmetic_v<T>
|
||||
constexpr T int_power(T base, std::integral auto exp)
|
||||
{
|
||||
// As this function should only be called at compile time, the exceptions herein function as
|
||||
// "parameter-compatible static_asserts", and should not result in exceptions at runtime.
|
||||
if (exp < 0) { throw std::invalid_argument{"int_power only supports positive integer powers"}; }
|
||||
if (exp < 0) {
|
||||
throw std::invalid_argument{"int_power only supports positive integer powers"};
|
||||
}
|
||||
|
||||
constexpr auto checked_multiply = [] (auto a, auto b) {
|
||||
constexpr auto checked_multiply = [](auto a, auto b) {
|
||||
const auto result = a * b;
|
||||
if (result / a != b) { throw std::overflow_error{"Wraparound detected"}; }
|
||||
if (result / a != b) {
|
||||
throw std::overflow_error{"Wraparound detected"};
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
constexpr auto checked_square = [checked_multiply] (auto a) { return checked_multiply(a, a); };
|
||||
constexpr auto checked_square = [checked_multiply](auto a) { return checked_multiply(a, a); };
|
||||
|
||||
// TODO(chogg): Unify this implementation with the one in pow.h. That one takes its exponent as a
|
||||
// template parameter, rather than a function parameter.
|
||||
@@ -169,7 +176,8 @@ constexpr T int_power(T base, std::integral auto exp){
|
||||
}
|
||||
|
||||
|
||||
template<typename T> requires std::is_arithmetic_v<T>
|
||||
template<typename T>
|
||||
requires std::is_arithmetic_v<T>
|
||||
constexpr widen_t<T> compute_base_power(BasePower auto bp)
|
||||
{
|
||||
// This utility can only handle integer powers. To compute rational powers at compile time, we'll
|
||||
@@ -177,8 +185,12 @@ constexpr widen_t<T> compute_base_power(BasePower auto bp)
|
||||
//
|
||||
// Note that since this function should only be called at compile time, the point of these
|
||||
// exceptions is to act as "static_assert substitutes", not to throw actual exceptions at runtime.
|
||||
if (bp.power.den != 1) { throw std::invalid_argument{"Rational powers not yet supported"}; }
|
||||
if (bp.power.exp < 0) { throw std::invalid_argument{"Unsupported exp value"}; }
|
||||
if (bp.power.den != 1) {
|
||||
throw std::invalid_argument{"Rational powers not yet supported"};
|
||||
}
|
||||
if (bp.power.exp < 0) {
|
||||
throw std::invalid_argument{"Unsupported exp value"};
|
||||
}
|
||||
|
||||
if (bp.power.num < 0) {
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
@@ -197,10 +209,10 @@ constexpr widen_t<T> compute_base_power(BasePower auto bp)
|
||||
// The input is the desired result, but in a (wider) intermediate type. The point of this function
|
||||
// is to cast to the desired type, but avoid overflow in doing so.
|
||||
template<typename To, typename From>
|
||||
requires std::is_arithmetic_v<To>
|
||||
&& std::is_arithmetic_v<From>
|
||||
&& (std::is_integral_v<To> == std::is_integral_v<From>)
|
||||
constexpr To checked_static_cast(From x) {
|
||||
requires std::is_arithmetic_v<To> && std::is_arithmetic_v<From> &&
|
||||
(std::is_integral_v<To> == std::is_integral_v<From>)
|
||||
constexpr To checked_static_cast(From x)
|
||||
{
|
||||
// This function should only ever be called at compile time. The purpose of these exceptions is
|
||||
// to produce compiler errors, because we cannot `static_assert` on function arguments.
|
||||
if constexpr (std::is_integral_v<To>) {
|
||||
@@ -215,20 +227,22 @@ constexpr To checked_static_cast(From x) {
|
||||
|
||||
return static_cast<To>(x);
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Equality detection for two base powers.
|
||||
*/
|
||||
template<BasePower T, BasePower U>
|
||||
constexpr bool operator==(T t, U u) {
|
||||
constexpr bool operator==(T t, U u)
|
||||
{
|
||||
return std::is_same_v<T, U> && (t.get_base() == u.get_base()) && (t.power == u.power);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A BasePower, raised to a rational power E.
|
||||
*/
|
||||
constexpr auto pow(BasePower auto bp, ratio p) {
|
||||
constexpr auto pow(BasePower auto bp, ratio p)
|
||||
{
|
||||
bp.power = bp.power * p;
|
||||
return bp;
|
||||
}
|
||||
@@ -240,8 +254,7 @@ namespace detail {
|
||||
constexpr std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
|
||||
{
|
||||
std::intmax_t m = 0;
|
||||
while (n % factor == 0)
|
||||
{
|
||||
while (n % factor == 0) {
|
||||
n /= factor;
|
||||
++m;
|
||||
}
|
||||
@@ -253,20 +266,26 @@ constexpr std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
|
||||
// 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; }
|
||||
while (pow-- > 0) {
|
||||
n /= base;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// A way to check whether a number is prime at compile time.
|
||||
constexpr bool is_prime(std::intmax_t n) {
|
||||
return (n >= 0) && Factorizer::is_prime(static_cast<std::size_t>(n));
|
||||
}
|
||||
constexpr bool is_prime(std::intmax_t n) { return (n >= 0) && Factorizer::is_prime(static_cast<std::size_t>(n)); }
|
||||
|
||||
constexpr bool is_valid_base_power(const BasePower auto &bp) {
|
||||
if (bp.power == 0) { return false; }
|
||||
constexpr bool is_valid_base_power(const BasePower auto& bp)
|
||||
{
|
||||
if (bp.power == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<decltype(bp.get_base()), std::intmax_t>) { return is_prime(bp.get_base()); }
|
||||
else { return bp.get_base() > 0; }
|
||||
if constexpr (std::is_same_v<decltype(bp.get_base()), std::intmax_t>) {
|
||||
return is_prime(bp.get_base());
|
||||
} else {
|
||||
return bp.get_base() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// A function object to apply a predicate to all consecutive pairs of values in a sequence.
|
||||
@@ -275,16 +294,19 @@ struct pairwise_all {
|
||||
Predicate predicate;
|
||||
|
||||
template<typename... Ts>
|
||||
constexpr bool operator()(Ts&&... ts) const {
|
||||
constexpr bool operator()(Ts&&... ts) const
|
||||
{
|
||||
// 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));
|
||||
|
||||
// Compare zero or more pairs of neighbours as needed.
|
||||
return [this]<std::size_t... Is>(std::tuple<Ts...> &&t, std::index_sequence<Is...>) {
|
||||
return [this]<std::size_t... Is>(std::tuple<Ts...> && t, std::index_sequence<Is...>)
|
||||
{
|
||||
return (predicate(std::get<Is>(t), std::get<Is + 1>(t)) && ...);
|
||||
}(std::make_tuple(std::forward<Ts>(ts)...), std::make_index_sequence<num_comparisons>());
|
||||
}
|
||||
(std::make_tuple(std::forward<Ts>(ts)...), std::make_index_sequence<num_comparisons>());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -294,8 +316,9 @@ pairwise_all(T) -> pairwise_all<T>;
|
||||
|
||||
// Check whether a sequence of (possibly heterogeneously typed) values are strictly increasing.
|
||||
template<typename... Ts>
|
||||
requires (std::is_signed_v<Ts> && ...)
|
||||
constexpr bool strictly_increasing(Ts&&... ts) {
|
||||
requires(std::is_signed_v<Ts> && ...)
|
||||
constexpr bool strictly_increasing(Ts&&... ts)
|
||||
{
|
||||
return pairwise_all{std::less{}}(std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
@@ -308,14 +331,13 @@ inline constexpr bool all_bases_in_order = strictly_increasing(BPs.get_base()...
|
||||
template<BasePower auto... BPs>
|
||||
inline constexpr bool is_base_power_pack_valid = all_base_powers_valid<BPs...> && all_bases_in_order<BPs...>;
|
||||
|
||||
constexpr bool is_rational(BasePower auto bp) {
|
||||
constexpr bool is_rational(BasePower auto bp)
|
||||
{
|
||||
return std::is_integral_v<decltype(bp.get_base())> && (bp.power.den == 1) && (bp.power.exp >= 0);
|
||||
}
|
||||
|
||||
constexpr bool is_integral(BasePower auto bp) {
|
||||
return is_rational(bp) && bp.power.num > 0;
|
||||
}
|
||||
} // namespace detail
|
||||
constexpr bool is_integral(BasePower auto bp) { return is_rational(bp) && bp.power.num > 0; }
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief A representation for positive real numbers which optimizes taking products and rational powers.
|
||||
@@ -339,7 +361,7 @@ template<typename T>
|
||||
inline constexpr bool is_magnitude = false;
|
||||
template<BasePower auto... BPs>
|
||||
inline constexpr bool is_magnitude<magnitude<BPs...>> = true;
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Concept to detect whether T is a valid Magnitude.
|
||||
@@ -352,7 +374,8 @@ concept Magnitude = detail::is_magnitude<T>;
|
||||
*/
|
||||
template<typename T, BasePower auto... BPs>
|
||||
requires std::is_floating_point_v<T> || (std::is_integral_v<T> && is_integral(magnitude<BPs...>{}))
|
||||
constexpr T get_value(const magnitude<BPs...> &) {
|
||||
constexpr T get_value(const magnitude<BPs...>&)
|
||||
{
|
||||
// Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow.
|
||||
constexpr auto result = detail::checked_static_cast<T>((detail::compute_base_power<T>(BPs) * ...));
|
||||
|
||||
@@ -370,18 +393,26 @@ struct pi_base {
|
||||
// Magnitude equality implementation.
|
||||
|
||||
template<BasePower auto... LeftBPs, BasePower auto... RightBPs>
|
||||
constexpr bool operator==(magnitude<LeftBPs...>, magnitude<RightBPs...>) {
|
||||
if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) { return ((LeftBPs == RightBPs) && ...); }
|
||||
else { return false; }
|
||||
constexpr bool operator==(magnitude<LeftBPs...>, magnitude<RightBPs...>)
|
||||
{
|
||||
if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) {
|
||||
return ((LeftBPs == RightBPs) && ...);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Magnitude rational powers implementation.
|
||||
|
||||
template<ratio E, BasePower auto... BPs>
|
||||
constexpr auto pow(magnitude<BPs...>) {
|
||||
if constexpr (E == 0) { return magnitude<>{}; }
|
||||
else { return magnitude<pow(BPs, E)...>{}; }
|
||||
constexpr auto pow(magnitude<BPs...>)
|
||||
{
|
||||
if constexpr (E == 0) {
|
||||
return magnitude<>{};
|
||||
} else {
|
||||
return magnitude<pow(BPs, E)...>{};
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -394,9 +425,10 @@ constexpr auto operator*(Magnitude auto m, magnitude<>) { return m; }
|
||||
|
||||
// Recursive case for the product of any two non-identity Magnitudes.
|
||||
template<BasePower auto H1, BasePower auto... T1, BasePower auto H2, BasePower auto... T2>
|
||||
constexpr auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>) {
|
||||
constexpr auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
|
||||
{
|
||||
// Case for when H1 has the smaller base.
|
||||
if constexpr(H1.get_base() < H2.get_base()){
|
||||
if constexpr (H1.get_base() < H2.get_base()) {
|
||||
if constexpr (sizeof...(T1) == 0) {
|
||||
// Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases.
|
||||
return magnitude<H1, H2, T2...>{};
|
||||
@@ -406,7 +438,7 @@ constexpr auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>) {
|
||||
}
|
||||
|
||||
// Case for when H2 has the smaller base.
|
||||
if constexpr(H1.get_base() > H2.get_base()){
|
||||
if constexpr (H1.get_base() > H2.get_base()) {
|
||||
return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
|
||||
}
|
||||
|
||||
@@ -439,23 +471,25 @@ constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1
|
||||
namespace detail {
|
||||
// Helper to perform prime factorization at compile time.
|
||||
template<std::intmax_t N>
|
||||
requires (N > 0)
|
||||
requires(N > 0)
|
||||
struct prime_factorization {
|
||||
static constexpr std::intmax_t first_base = static_cast<std::intmax_t>(Factorizer::find_first_factor(N));
|
||||
static constexpr std::intmax_t first_power = multiplicity(first_base, N);
|
||||
static constexpr std::intmax_t remainder = remove_power(first_base, first_power, N);
|
||||
|
||||
static constexpr auto value = magnitude<base_power{first_base, first_power}>{}
|
||||
* prime_factorization<remainder>::value;
|
||||
static constexpr auto value =
|
||||
magnitude<base_power{first_base, first_power}>{} * prime_factorization<remainder>::value;
|
||||
};
|
||||
|
||||
// Specialization for the prime factorization of 1 (base case).
|
||||
template<>
|
||||
struct prime_factorization<1> { static constexpr magnitude<> value{}; };
|
||||
struct prime_factorization<1> {
|
||||
static constexpr magnitude<> value{};
|
||||
};
|
||||
|
||||
template<std::intmax_t N>
|
||||
inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Convert any positive integer to a Magnitude.
|
||||
@@ -464,11 +498,11 @@ inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
|
||||
* manually adding base powers.
|
||||
*/
|
||||
template<ratio R>
|
||||
requires (R.num > 0)
|
||||
constexpr Magnitude auto as_magnitude() {
|
||||
return pow<R.exp>(detail::prime_factorization_v<10>)
|
||||
* detail::prime_factorization_v<R.num>
|
||||
/ detail::prime_factorization_v<R.den>;
|
||||
requires(R.num > 0)
|
||||
constexpr Magnitude auto as_magnitude()
|
||||
{
|
||||
return pow<R.exp>(detail::prime_factorization_v<10>) * detail::prime_factorization_v<R.num> /
|
||||
detail::prime_factorization_v<R.den>;
|
||||
}
|
||||
|
||||
} // namespace units
|
||||
} // namespace units
|
||||
|
@@ -20,54 +20,64 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <units/magnitude.h>
|
||||
#include <units/ratio.h>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace units {
|
||||
|
||||
// A set of non-standard bases for testing purposes.
|
||||
struct noninteger_base { static constexpr long double value = 1.234L; };
|
||||
struct noncanonical_two_base { static constexpr long double value = 2.0L; };
|
||||
struct other_noncanonical_two_base { static constexpr long double value = 2.0L; };
|
||||
struct invalid_zero_base { static constexpr long double value = 0.0L; };
|
||||
struct invalid_negative_base { static constexpr long double value = -1.234L; };
|
||||
struct noninteger_base {
|
||||
static constexpr long double value = 1.234L;
|
||||
};
|
||||
struct noncanonical_two_base {
|
||||
static constexpr long double value = 2.0L;
|
||||
};
|
||||
struct other_noncanonical_two_base {
|
||||
static constexpr long double value = 2.0L;
|
||||
};
|
||||
struct invalid_zero_base {
|
||||
static constexpr long double value = 0.0L;
|
||||
};
|
||||
struct invalid_negative_base {
|
||||
static constexpr long double value = -1.234L;
|
||||
};
|
||||
|
||||
template<ratio Power>
|
||||
constexpr auto pi_to_the() { return magnitude<base_power<pi_base>{Power}>{}; }
|
||||
constexpr auto pi_to_the()
|
||||
{
|
||||
return magnitude<base_power<pi_base>{Power}>{};
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
void check_same_type_and_value(T actual, U expected) {
|
||||
void check_same_type_and_value(T actual, U expected)
|
||||
{
|
||||
CHECK(std::is_same_v<T, U>);
|
||||
CHECK(actual == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("base_power")
|
||||
{
|
||||
SECTION("base rep deducible for integral base")
|
||||
{
|
||||
SECTION ("base rep deducible for integral base") {
|
||||
CHECK(base_power{2} == base_power<std::intmax_t>{2, ratio{1}});
|
||||
CHECK(base_power{2, 3} == base_power<std::intmax_t>{2, ratio{3}});
|
||||
CHECK(base_power{2, ratio{3, 4}} == base_power<std::intmax_t>{2, ratio{3, 4}});
|
||||
}
|
||||
|
||||
SECTION("get_base retrieves base for integral base")
|
||||
{
|
||||
SECTION ("get_base retrieves base for integral base") {
|
||||
CHECK(base_power{2}.get_base() == 2);
|
||||
CHECK(base_power{3, 5}.get_base() == 3);
|
||||
CHECK(base_power{5, ratio{1, 3}}.get_base() == 5);
|
||||
}
|
||||
|
||||
SECTION("get_base retrieves member value for non-integer base")
|
||||
{
|
||||
SECTION ("get_base retrieves member value for non-integer base") {
|
||||
CHECK(base_power<noninteger_base>{}.get_base() == 1.234L);
|
||||
CHECK(base_power<noninteger_base>{2}.get_base() == 1.234L);
|
||||
CHECK(base_power<noninteger_base>{ratio{5, 8}}.get_base() == 1.234L);
|
||||
}
|
||||
|
||||
SECTION("same-base values not equal if types are different")
|
||||
{
|
||||
SECTION ("same-base values not equal if types are different") {
|
||||
const auto a = base_power<noncanonical_two_base>{};
|
||||
const auto b = base_power{2};
|
||||
const auto c = base_power<other_noncanonical_two_base>{};
|
||||
@@ -79,31 +89,25 @@ TEST_CASE("base_power")
|
||||
CHECK(a != c);
|
||||
}
|
||||
|
||||
SECTION("same-type values not equal if bases are different")
|
||||
{
|
||||
SECTION ("same-type values not equal if bases are different") {
|
||||
CHECK(base_power{2} != base_power{3});
|
||||
CHECK(base_power{2, ratio{5, 4}} != base_power{3, ratio{5, 4}});
|
||||
}
|
||||
|
||||
SECTION("same-type, same-base values not equal if powers are different")
|
||||
{
|
||||
SECTION ("same-type, same-base values not equal if powers are different") {
|
||||
CHECK(base_power{2} != base_power{2, 2});
|
||||
CHECK(base_power<pi_base>{} != base_power<pi_base>{ratio{1, 3}});
|
||||
}
|
||||
|
||||
SECTION("product with inverse equals identity")
|
||||
{
|
||||
auto check_product_with_inverse_is_identity = [] (auto x) {
|
||||
CHECK(x * pow<-1>(x) == as_magnitude<1>());
|
||||
};
|
||||
SECTION ("product with inverse equals identity") {
|
||||
auto check_product_with_inverse_is_identity = [](auto x) { CHECK(x * pow<-1>(x) == as_magnitude<1>()); };
|
||||
|
||||
check_product_with_inverse_is_identity(as_magnitude<3>());
|
||||
check_product_with_inverse_is_identity(as_magnitude<ratio{4, 17}>());
|
||||
check_product_with_inverse_is_identity(pi_to_the<ratio{-22, 7}>());
|
||||
}
|
||||
|
||||
SECTION("pow() multiplies exponent")
|
||||
{
|
||||
SECTION ("pow() multiplies exponent") {
|
||||
CHECK(pow(base_power{2}, 0) == base_power{2, 0});
|
||||
CHECK(pow(base_power{2, 3}, ratio{-1, 2}) == base_power{2, ratio{-3, 2}});
|
||||
CHECK(pow(base_power<pi_base>{ratio{3, 2}}, ratio{1, 3}) == base_power<pi_base>{ratio{1, 2}});
|
||||
@@ -112,8 +116,7 @@ TEST_CASE("base_power")
|
||||
|
||||
TEST_CASE("make_ratio performs prime factorization correctly")
|
||||
{
|
||||
SECTION("Performs prime factorization when denominator is 1")
|
||||
{
|
||||
SECTION ("Performs prime factorization when denominator is 1") {
|
||||
CHECK(as_magnitude<1>() == magnitude<>{});
|
||||
CHECK(as_magnitude<2>() == magnitude<base_power{2}>{});
|
||||
CHECK(as_magnitude<3>() == magnitude<base_power{3}>{});
|
||||
@@ -122,27 +125,23 @@ TEST_CASE("make_ratio performs prime factorization correctly")
|
||||
CHECK(as_magnitude<792>() == magnitude<base_power{2, 3}, base_power{3, 2}, base_power{11}>{});
|
||||
}
|
||||
|
||||
SECTION("Supports fractions")
|
||||
{
|
||||
SECTION ("Supports fractions") {
|
||||
CHECK(as_magnitude<ratio{5, 8}>() == magnitude<base_power{2, -3}, base_power{5}>{});
|
||||
}
|
||||
|
||||
SECTION("Supports nonzero exp")
|
||||
{
|
||||
SECTION ("Supports nonzero exp") {
|
||||
constexpr ratio r{3, 1, 2};
|
||||
REQUIRE(r.exp == 2);
|
||||
CHECK(as_magnitude<r>() == as_magnitude<300>());
|
||||
}
|
||||
|
||||
SECTION("Can handle prime factor which would be large enough to overflow int")
|
||||
{
|
||||
SECTION ("Can handle prime factor which would be large enough to overflow int") {
|
||||
// This was taken from a case which failed when we used `int` for our base to store prime numbers.
|
||||
// The failure was due to a prime factor which is larger than 2^31.
|
||||
as_magnitude<ratio(16'605'390'666'050, 10'000'000'000'000)>();
|
||||
}
|
||||
|
||||
SECTION("Can handle prime number which would exceed GCC iteration limit")
|
||||
{
|
||||
SECTION ("Can handle prime number which would exceed GCC iteration limit") {
|
||||
// GCC 10 has a constexpr loop iteration limit of 262144. A naive algorithm, which performs trial division on 2 and
|
||||
// all odd numbers up to sqrt(N), will exceed this limit for the following prime. Thus, for this test to pass, we
|
||||
// need to be using a more efficient algorithm. (We could increase the limit, but we don't want users to have to
|
||||
@@ -153,8 +152,7 @@ TEST_CASE("make_ratio performs prime factorization correctly")
|
||||
|
||||
TEST_CASE("magnitude converts to numerical value")
|
||||
{
|
||||
SECTION("Positive integer powers of integer bases give integer values")
|
||||
{
|
||||
SECTION ("Positive integer powers of integer bases give integer values") {
|
||||
constexpr auto mag_412 = as_magnitude<412>();
|
||||
check_same_type_and_value(get_value<int>(mag_412), 412);
|
||||
check_same_type_and_value(get_value<std::size_t>(mag_412), std::size_t{412});
|
||||
@@ -162,23 +160,20 @@ TEST_CASE("magnitude converts to numerical value")
|
||||
check_same_type_and_value(get_value<double>(mag_412), 412.0);
|
||||
}
|
||||
|
||||
SECTION("Negative integer powers of integer bases compute correct values")
|
||||
{
|
||||
SECTION ("Negative integer powers of integer bases compute correct values") {
|
||||
constexpr auto mag_0p125 = as_magnitude<ratio{1, 8}>();
|
||||
check_same_type_and_value(get_value<float>(mag_0p125), 0.125f);
|
||||
check_same_type_and_value(get_value<double>(mag_0p125), 0.125);
|
||||
}
|
||||
|
||||
SECTION("pi to the 1 supplies correct values")
|
||||
{
|
||||
SECTION ("pi to the 1 supplies correct values") {
|
||||
constexpr auto pi = pi_to_the<1>();
|
||||
check_same_type_and_value(get_value<float>(pi), std::numbers::pi_v<float>);
|
||||
check_same_type_and_value(get_value<double>(pi), std::numbers::pi_v<double>);
|
||||
check_same_type_and_value(get_value<long double>(pi), std::numbers::pi_v<long double>);
|
||||
}
|
||||
|
||||
SECTION("pi to arbitrary power performs computations in most accurate type at compile time")
|
||||
{
|
||||
SECTION ("pi to arbitrary power performs computations in most accurate type at compile time") {
|
||||
if constexpr (sizeof(float) < sizeof(long double)) {
|
||||
constexpr auto pi_cubed = pi_to_the<3>();
|
||||
|
||||
@@ -192,8 +187,7 @@ TEST_CASE("magnitude converts to numerical value")
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Impossible requests are prevented at compile time")
|
||||
{
|
||||
SECTION ("Impossible requests are prevented at compile time") {
|
||||
// Naturally, we cannot actually write a test to verify a compiler error. But any of these can
|
||||
// be uncommented if desired to verify that it breaks the build.
|
||||
|
||||
@@ -205,7 +199,7 @@ TEST_CASE("magnitude converts to numerical value")
|
||||
// Would work for pow<63>:
|
||||
// get_value<uint64_t>(pow<64>(as_magnitude<2>()));
|
||||
|
||||
get_value<double>(pow<308>(as_magnitude<10>())); // Compiles, correctly.
|
||||
get_value<double>(pow<308>(as_magnitude<10>())); // Compiles, correctly.
|
||||
// get_value<double>(pow<309>(as_magnitude<10>()));
|
||||
// get_value<double>(pow<3099>(as_magnitude<10>()));
|
||||
// get_value<double>(pow<3099999>(as_magnitude<10>()));
|
||||
@@ -218,21 +212,18 @@ TEST_CASE("magnitude converts to numerical value")
|
||||
|
||||
TEST_CASE("Equality works for magnitudes")
|
||||
{
|
||||
SECTION("Equivalent ratios are equal")
|
||||
{
|
||||
SECTION ("Equivalent ratios are equal") {
|
||||
CHECK(as_magnitude<1>() == as_magnitude<1>());
|
||||
CHECK(as_magnitude<3>() == as_magnitude<3>());
|
||||
CHECK(as_magnitude<ratio{3, 4}>() == as_magnitude<ratio{9, 12}>());
|
||||
}
|
||||
|
||||
SECTION("Different ratios are unequal")
|
||||
{
|
||||
SECTION ("Different ratios are unequal") {
|
||||
CHECK(as_magnitude<3>() != as_magnitude<5>());
|
||||
CHECK(as_magnitude<3>() != as_magnitude<ratio{3, 2}>());
|
||||
}
|
||||
|
||||
SECTION("Supports constexpr")
|
||||
{
|
||||
SECTION ("Supports constexpr") {
|
||||
constexpr auto eq = (as_magnitude<ratio{4, 5}>() == as_magnitude<ratio{4, 3}>());
|
||||
CHECK(!eq);
|
||||
}
|
||||
@@ -240,25 +231,20 @@ TEST_CASE("Equality works for magnitudes")
|
||||
|
||||
TEST_CASE("Multiplication works for magnitudes")
|
||||
{
|
||||
SECTION("Reciprocals reduce to null magnitude")
|
||||
{
|
||||
SECTION ("Reciprocals reduce to null magnitude") {
|
||||
CHECK(as_magnitude<ratio{3, 4}>() * as_magnitude<ratio{4, 3}>() == as_magnitude<1>());
|
||||
}
|
||||
|
||||
SECTION("Products work as expected")
|
||||
{
|
||||
SECTION ("Products work as expected") {
|
||||
CHECK(as_magnitude<ratio{4, 5}>() * as_magnitude<ratio{4, 3}>() == as_magnitude<ratio{16, 15}>());
|
||||
}
|
||||
|
||||
SECTION("Products handle pi correctly")
|
||||
{
|
||||
CHECK(
|
||||
pi_to_the<1>() * as_magnitude<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 ("Products handle pi correctly") {
|
||||
CHECK(pi_to_the<1>() * as_magnitude<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")
|
||||
{
|
||||
SECTION ("Supports constexpr") {
|
||||
constexpr auto p = as_magnitude<ratio{4, 5}>() * as_magnitude<ratio{4, 3}>();
|
||||
CHECK(p == as_magnitude<ratio{16, 15}>());
|
||||
}
|
||||
@@ -266,19 +252,16 @@ TEST_CASE("Multiplication works for magnitudes")
|
||||
|
||||
TEST_CASE("Division works for magnitudes")
|
||||
{
|
||||
SECTION("Dividing anything by itself reduces to null magnitude")
|
||||
{
|
||||
SECTION ("Dividing anything by itself reduces to null magnitude") {
|
||||
CHECK(as_magnitude<ratio{3, 4}>() / as_magnitude<ratio{3, 4}>() == as_magnitude<1>());
|
||||
CHECK(as_magnitude<15>() / as_magnitude<15>() == as_magnitude<1>());
|
||||
}
|
||||
|
||||
SECTION("Quotients work as expected")
|
||||
{
|
||||
SECTION ("Quotients work as expected") {
|
||||
CHECK(as_magnitude<ratio{4, 5}>() / as_magnitude<ratio{4, 3}>() == as_magnitude<ratio{3, 5}>());
|
||||
}
|
||||
|
||||
SECTION("Supports constexpr")
|
||||
{
|
||||
SECTION ("Supports constexpr") {
|
||||
constexpr auto q = as_magnitude<ratio{4, 5}>() / as_magnitude<ratio{4, 3}>();
|
||||
CHECK(q == as_magnitude<ratio{3, 5}>());
|
||||
}
|
||||
@@ -286,29 +269,28 @@ TEST_CASE("Division works for magnitudes")
|
||||
|
||||
TEST_CASE("Can raise Magnitudes to rational powers")
|
||||
{
|
||||
SECTION("Anything to the 0 is 1") {
|
||||
SECTION ("Anything to the 0 is 1") {
|
||||
CHECK(pow<0>(as_magnitude<1>()) == as_magnitude<1>());
|
||||
CHECK(pow<0>(as_magnitude<123>()) == as_magnitude<1>());
|
||||
CHECK(pow<0>(as_magnitude<ratio{3, 4}>()) == as_magnitude<1>());
|
||||
CHECK(pow<0>(pi_to_the<ratio{-1, 2}>()) == as_magnitude<1>());
|
||||
}
|
||||
|
||||
SECTION("Anything to the 1 is itself") {
|
||||
SECTION ("Anything to the 1 is itself") {
|
||||
CHECK(pow<1>(as_magnitude<1>()) == as_magnitude<1>());
|
||||
CHECK(pow<1>(as_magnitude<123>()) == as_magnitude<123>());
|
||||
CHECK(pow<1>(as_magnitude<ratio{3, 4}>()) == as_magnitude<ratio{3, 4}>());
|
||||
CHECK(pow<1>(pi_to_the<ratio{-1, 2}>()) == pi_to_the<ratio{-1, 2}>());
|
||||
}
|
||||
|
||||
SECTION("Can raise to arbitrary rational power") {
|
||||
SECTION ("Can raise to arbitrary rational power") {
|
||||
CHECK(pow<ratio{-8, 3}>(pi_to_the<ratio{-1, 2}>()) == pi_to_the<ratio{4, 3}>());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("can distinguish integral, rational, and irrational magnitudes")
|
||||
{
|
||||
SECTION("Integer magnitudes are integral and rational")
|
||||
{
|
||||
SECTION ("Integer magnitudes are integral and rational") {
|
||||
auto check_rational_and_integral = [](Magnitude auto m) {
|
||||
CHECK(is_integral(m));
|
||||
CHECK(is_rational(m));
|
||||
@@ -321,8 +303,7 @@ TEST_CASE("can distinguish integral, rational, and irrational magnitudes")
|
||||
check_rational_and_integral(as_magnitude<ratio{1, 1}>());
|
||||
}
|
||||
|
||||
SECTION("Fractional magnitudes are rational, but not integral")
|
||||
{
|
||||
SECTION ("Fractional magnitudes are rational, but not integral") {
|
||||
auto check_rational_but_not_integral = [](Magnitude auto m) {
|
||||
CHECK(!is_integral(m));
|
||||
CHECK(is_rational(m));
|
||||
@@ -334,8 +315,9 @@ TEST_CASE("can distinguish integral, rational, and irrational magnitudes")
|
||||
|
||||
namespace detail {
|
||||
|
||||
TEST_CASE("int_power computes integer powers") {
|
||||
SECTION("handles floating point") {
|
||||
TEST_CASE("int_power computes integer powers")
|
||||
{
|
||||
SECTION ("handles floating point") {
|
||||
check_same_type_and_value(int_power(0.123L, 0), 1.0L);
|
||||
check_same_type_and_value(int_power(0.246f, 1), 0.246f);
|
||||
check_same_type_and_value(int_power(0.5f, 3), 0.125f);
|
||||
@@ -344,7 +326,7 @@ TEST_CASE("int_power computes integer powers") {
|
||||
CHECK(std::is_same_v<long double, decltype(compute_base_power<double>(base_power{10, 20}))>);
|
||||
}
|
||||
|
||||
SECTION("handles integral") {
|
||||
SECTION ("handles integral") {
|
||||
check_same_type_and_value(int_power(8, 0), 1);
|
||||
check_same_type_and_value(int_power(9L, 1), 9L);
|
||||
check_same_type_and_value(int_power(2, 10), 1024);
|
||||
@@ -353,13 +335,13 @@ TEST_CASE("int_power computes integer powers") {
|
||||
|
||||
TEST_CASE("Prime helper functions")
|
||||
{
|
||||
SECTION("multiplicity") {
|
||||
SECTION ("multiplicity") {
|
||||
CHECK(multiplicity(2, 8) == 3);
|
||||
CHECK(multiplicity(2, 1024) == 10);
|
||||
CHECK(multiplicity(11, 6655) == 3);
|
||||
}
|
||||
|
||||
SECTION("remove_power()") {
|
||||
SECTION ("remove_power()") {
|
||||
CHECK(remove_power(17, 0, 5) == 5);
|
||||
CHECK(remove_power(2, 3, 24) == 3);
|
||||
CHECK(remove_power(11, 3, 6655) == 5);
|
||||
@@ -368,13 +350,11 @@ TEST_CASE("Prime helper functions")
|
||||
|
||||
TEST_CASE("Prime factorization")
|
||||
{
|
||||
SECTION("1 factors into the null magnitude")
|
||||
{
|
||||
SECTION ("1 factors into the null magnitude") {
|
||||
CHECK(prime_factorization_v<1> == magnitude<>{});
|
||||
}
|
||||
|
||||
SECTION("Prime numbers factor into themselves")
|
||||
{
|
||||
SECTION ("Prime numbers factor into themselves") {
|
||||
CHECK(prime_factorization_v<2> == magnitude<base_power{2}>{});
|
||||
CHECK(prime_factorization_v<3> == magnitude<base_power{3}>{});
|
||||
CHECK(prime_factorization_v<5> == magnitude<base_power{5}>{});
|
||||
@@ -384,29 +364,24 @@ TEST_CASE("Prime factorization")
|
||||
CHECK(prime_factorization_v<41> == magnitude<base_power{41}>{});
|
||||
}
|
||||
|
||||
SECTION("Prime factorization finds factors and multiplicities")
|
||||
{
|
||||
CHECK(prime_factorization_v<792> ==
|
||||
magnitude<base_power{2, 3}, base_power{3, 2}, base_power{11}>{});
|
||||
SECTION ("Prime factorization finds factors and multiplicities") {
|
||||
CHECK(prime_factorization_v<792> == magnitude<base_power{2, 3}, base_power{3, 2}, base_power{11}>{});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("is_prime detects primes")
|
||||
{
|
||||
SECTION("Non-positive numbers are not prime")
|
||||
{
|
||||
SECTION ("Non-positive numbers are not prime") {
|
||||
CHECK(!is_prime(-1328));
|
||||
CHECK(!is_prime(-1));
|
||||
CHECK(!is_prime(0));
|
||||
}
|
||||
|
||||
SECTION("1 is not prime")
|
||||
{
|
||||
SECTION ("1 is not prime") {
|
||||
CHECK(!is_prime(1));
|
||||
}
|
||||
|
||||
SECTION("Discriminates between primes and non-primes")
|
||||
{
|
||||
SECTION ("Discriminates between primes and non-primes") {
|
||||
CHECK(is_prime(2));
|
||||
CHECK(is_prime(3));
|
||||
CHECK(!is_prime(4));
|
||||
@@ -422,7 +397,7 @@ TEST_CASE("is_prime detects primes")
|
||||
|
||||
TEST_CASE("is_valid_base_power")
|
||||
{
|
||||
SECTION("0 power is invalid") {
|
||||
SECTION ("0 power is invalid") {
|
||||
REQUIRE(is_valid_base_power(base_power{2}));
|
||||
CHECK(!is_valid_base_power(base_power{2, 0}));
|
||||
|
||||
@@ -433,7 +408,7 @@ TEST_CASE("is_valid_base_power")
|
||||
CHECK(!is_valid_base_power(base_power<pi_base>{0}));
|
||||
}
|
||||
|
||||
SECTION("non-prime integers are invalid") {
|
||||
SECTION ("non-prime integers are invalid") {
|
||||
CHECK(!is_valid_base_power(base_power{-8}));
|
||||
CHECK(!is_valid_base_power(base_power{0}));
|
||||
CHECK(!is_valid_base_power(base_power{1}));
|
||||
@@ -444,7 +419,7 @@ TEST_CASE("is_valid_base_power")
|
||||
CHECK(!is_valid_base_power(base_power{4}));
|
||||
}
|
||||
|
||||
SECTION("non-positive floating point bases are invalid") {
|
||||
SECTION ("non-positive floating point bases are invalid") {
|
||||
CHECK(!is_valid_base_power(base_power<invalid_zero_base>{}));
|
||||
CHECK(!is_valid_base_power(base_power<invalid_negative_base>{}));
|
||||
}
|
||||
@@ -452,25 +427,22 @@ TEST_CASE("is_valid_base_power")
|
||||
|
||||
TEST_CASE("pairwise_all evaluates all pairs")
|
||||
{
|
||||
const auto all_pairs_return_true = pairwise_all{[](auto, auto){ return true; }};
|
||||
const auto all_pairs_return_false = pairwise_all{[](auto, auto){ return false; }};
|
||||
const auto all_pairs_return_true = pairwise_all{[](auto, auto) { return true; }};
|
||||
const auto all_pairs_return_false = pairwise_all{[](auto, auto) { return false; }};
|
||||
const auto all_increasing = pairwise_all{std::less{}};
|
||||
|
||||
SECTION("always true for empty tuples")
|
||||
{
|
||||
SECTION ("always true for empty tuples") {
|
||||
CHECK(all_pairs_return_true());
|
||||
CHECK(all_pairs_return_false());
|
||||
}
|
||||
|
||||
SECTION("always true for single-element tuples")
|
||||
{
|
||||
SECTION ("always true for single-element tuples") {
|
||||
CHECK(all_pairs_return_true(1));
|
||||
CHECK(all_pairs_return_false(3.14));
|
||||
CHECK(all_pairs_return_true('x'));
|
||||
}
|
||||
|
||||
SECTION("true for longer tuples iff true for all neighbouring pairs")
|
||||
{
|
||||
SECTION ("true for longer tuples iff true for all neighbouring pairs") {
|
||||
CHECK(all_increasing(1, 1.5));
|
||||
CHECK(all_increasing(1, 1.5, 2));
|
||||
|
||||
@@ -484,26 +456,23 @@ TEST_CASE("pairwise_all evaluates all pairs")
|
||||
|
||||
TEST_CASE("strictly_increasing")
|
||||
{
|
||||
SECTION("Empty input is sorted")
|
||||
{
|
||||
SECTION ("Empty input is sorted") {
|
||||
CHECK(strictly_increasing());
|
||||
}
|
||||
|
||||
SECTION("Single-element input is sorted")
|
||||
{
|
||||
SECTION ("Single-element input is sorted") {
|
||||
CHECK(strictly_increasing(3));
|
||||
CHECK(strictly_increasing(15.42));
|
||||
CHECK(strictly_increasing('c'));
|
||||
}
|
||||
|
||||
SECTION("Multi-value inputs compare correctly")
|
||||
{
|
||||
SECTION ("Multi-value inputs compare correctly") {
|
||||
CHECK(strictly_increasing(3, 3.14));
|
||||
CHECK(!strictly_increasing(3, 3.0));
|
||||
CHECK(!strictly_increasing(4, 3.0));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
} // namespace units
|
||||
} // namespace units
|
||||
|
@@ -21,15 +21,14 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include <units/bits/prime.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace units::detail
|
||||
{
|
||||
namespace units::detail {
|
||||
|
||||
template<std::size_t BasisSize, std::size_t... Is>
|
||||
constexpr bool check_primes(std::index_sequence<Is...>) {
|
||||
constexpr bool check_primes(std::index_sequence<Is...>)
|
||||
{
|
||||
return ((Is < 2 || WheelFactorizer<BasisSize>::is_prime(Is) == is_prime_by_trial_division(Is)) && ...);
|
||||
}
|
||||
|
||||
@@ -72,4 +71,4 @@ static_assert(!WheelFactorizer<3>::is_prime(0));
|
||||
static_assert(!WheelFactorizer<3>::is_prime(1));
|
||||
static_assert(WheelFactorizer<3>::is_prime(2));
|
||||
|
||||
} // namespace units::detail
|
||||
} // namespace units::detail
|
||||
|
Reference in New Issue
Block a user