refactor: magnitudes support refactored to improve compile-times

This commit is contained in:
Mateusz Pusz
2024-06-06 20:58:15 +02:00
parent 3c97f2e611
commit e38c7c4460
6 changed files with 97 additions and 87 deletions

View File

@@ -157,22 +157,30 @@ struct wheel_factorizer {
static constexpr auto coprimes_in_first_wheel =
coprimes_up_to<num_coprimes_up_to(wheel_size, basis)>(wheel_size, basis);
[[nodiscard]] static consteval std::uintmax_t find_first_factor(std::uintmax_t n)
template<std::size_t N>
[[nodiscard]] static consteval auto find_first_factor()
{
if (const auto k = detail::get_first_of(basis, [&](auto p) { return first_factor_maybe(n, p); })) return *k;
constexpr std::uintmax_t res = [] {
if (const auto k = detail::get_first_of(basis, [&](auto p) { return first_factor_maybe(N, p); })) return *k;
if (const auto k = detail::get_first_of(std::next(begin(coprimes_in_first_wheel)), end(coprimes_in_first_wheel),
[&](auto p) { return first_factor_maybe(n, p); }))
return *k;
for (std::size_t wheel = wheel_size; wheel < n; wheel += wheel_size)
if (const auto k =
detail::get_first_of(coprimes_in_first_wheel, [&](auto p) { return first_factor_maybe(n, wheel + p); }))
if (const auto k = detail::get_first_of(std::next(begin(coprimes_in_first_wheel)), end(coprimes_in_first_wheel),
[&](auto p) { return first_factor_maybe(N, p); }))
return *k;
return n;
for (std::size_t wheel = wheel_size; wheel < N; wheel += wheel_size)
if (const auto k =
detail::get_first_of(coprimes_in_first_wheel, [&](auto p) { return first_factor_maybe(N, wheel + p); }))
return *k;
return N;
}();
return std::integral_constant<std::uintmax_t, res>{};
}
[[nodiscard]] static consteval bool is_prime(std::size_t n) { return (n > 1) && find_first_factor(n) == n; }
template<std::size_t N>
[[nodiscard]] static consteval auto is_prime()
{
return std::bool_constant<(N > 1 && decltype(find_first_factor<N>())::value == N)>{};
}
};
} // namespace mp_units::detail

View File

@@ -476,16 +476,16 @@ namespace detail {
template<MagnitudeSpec auto... Ms>
// requires detail::is_element_pack_valid<Ms...>
struct magnitude {
[[nodiscard]] friend consteval bool is_integral(const magnitude&)
[[nodiscard]] friend consteval auto is_integral(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_integral(Ms) && ...);
return std::bool_constant<(is_integral(Ms) && ...)>{};
}
[[nodiscard]] friend consteval bool is_rational(const magnitude&)
[[nodiscard]] friend consteval auto is_rational(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_rational(Ms) && ...);
return std::bool_constant<(is_rational(Ms) && ...)>{};
}
};
@@ -508,22 +508,20 @@ inline constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
} // namespace detail
MP_UNITS_EXPORT_BEGIN
/**
* @brief The value of a Magnitude in a desired type T.
*/
template<typename T, auto... Ms>
requires(is_integral(magnitude<Ms...>{})) || treat_as_floating_point<T>
constexpr T get_value(const magnitude<Ms...>&)
requires(decltype(is_integral(magnitude<Ms...>{}))::value) || treat_as_floating_point<T>
constexpr auto get_value(const magnitude<Ms...>&)
{
// 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>(Ms) * ... * T{1}));
return result;
return std::integral_constant<T, result>{};
}
MP_UNITS_EXPORT_BEGIN
/**
* @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number.
*/
@@ -558,21 +556,21 @@ template<std::intmax_t Num, std::intmax_t Den = 1, auto... Ms>
if constexpr (Num == 0) {
return magnitude<>{};
} else {
return magnitude<
detail::power_v_or_T<detail::get_base(Ms), detail::get_exponent(Ms) * detail::ratio{Num, Den}>()...>{};
return decltype(magnitude<detail::power_v_or_T<detail::get_base(Ms),
detail::get_exponent(Ms) * detail::ratio{Num, Den}>()...>{}){};
}
}
template<auto... Ms>
[[nodiscard]] consteval auto sqrt(magnitude<Ms...> m)
{
return pow<1, 2>(m);
return decltype(pow<1, 2>(m)){};
}
template<auto... Ms>
[[nodiscard]] consteval auto cbrt(magnitude<Ms...> m)
{
return pow<1, 3>(m);
return decltype(pow<1, 3>(m)){};
}
MP_UNITS_EXPORT_END
@@ -615,13 +613,13 @@ template<auto H1, auto... T1, auto H2, auto... T2>
// Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases.
return magnitude<H1, H2, T2...>{};
} else {
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
return decltype(magnitude<H1>{} * (decltype(magnitude<T1...>{} * magnitude<H2, T2...>{}){})){};
}
} else if constexpr (less(H2, H1)) {
return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
return decltype(magnitude<H2>{} * (decltype(magnitude<H1, T1...>{} * magnitude<T2...>{}){})){};
} else {
if constexpr (is_same_v<decltype(get_base(H1)), decltype(get_base(H2))>) {
constexpr auto partial_product = magnitude<T1...>{} * magnitude<T2...>{};
constexpr auto partial_product = decltype(magnitude<T1...>{} * magnitude<T2...>{}){};
if constexpr (get_exponent(H1) + get_exponent(H2) == 0) {
return partial_product;
} else {
@@ -631,13 +629,13 @@ template<auto H1, auto... T1, auto H2, auto... T2>
if constexpr (get_exponent(new_head) == 0) {
return partial_product;
} else {
return magnitude<new_head>{} * partial_product;
return decltype(magnitude<new_head>{} * partial_product){};
}
}
} else if constexpr (is_named_magnitude<decltype(get_base(H1))>) {
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
return decltype(magnitude<H1>{} * decltype((magnitude<T1...>{} * magnitude<H2, T2...>{})){}){};
} else {
return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
return decltype(magnitude<H2>{} * decltype((magnitude<H1, T1...>{} * magnitude<T2...>{})){}){};
}
}
}
@@ -645,7 +643,7 @@ template<auto H1, auto... T1, auto H2, auto... T2>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude quotient implementation.
[[nodiscard]] consteval auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); }
[[nodiscard]] consteval auto operator/(Magnitude auto l, Magnitude auto r) { return decltype(l * pow<-1>(r)){}; }
MP_UNITS_EXPORT_END
@@ -672,20 +670,10 @@ template<auto M>
template<auto... Ms>
[[nodiscard]] consteval auto numerator(magnitude<Ms...>)
{
return (detail::integer_part(magnitude<Ms>{}) * ... * magnitude<>{});
return decltype((decltype(detail::integer_part(magnitude<Ms>{})){} * ... * magnitude<>{})){};
}
[[nodiscard]] consteval auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); }
// TODO This probably should not be exported but is used in chrono.h
MP_UNITS_EXPORT constexpr ratio as_ratio(Magnitude auto m)
requires(is_rational(decltype(m){}))
{
return ratio{
get_value<std::intmax_t>(numerator(m)),
get_value<std::intmax_t>(denominator(m)),
};
}
[[nodiscard]] consteval auto denominator(Magnitude auto m) { return decltype(numerator(pow<-1>(m))){}; }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Common Magnitude.
@@ -725,11 +713,11 @@ template<auto... Ms>
[[nodiscard]] consteval auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; }
[[nodiscard]] consteval auto common_magnitude(magnitude<>, Magnitude auto m)
{
return detail::remove_positive_powers(m);
return decltype(detail::remove_positive_powers(m)){};
}
[[nodiscard]] consteval auto common_magnitude(Magnitude auto m, magnitude<>)
{
return detail::remove_positive_powers(m);
return decltype(detail::remove_positive_powers(m)){};
}
// Recursive case for the common Magnitude of any two non-identity Magnitudes.
@@ -740,17 +728,19 @@ template<auto H1, auto... T1, auto H2, auto... T2>
if constexpr (detail::get_base_value(H1) < detail::get_base_value(H2)) {
// When H1 has the smaller base, prepend to result from recursion.
return remove_positive_power(magnitude<H1>{}) * common_magnitude(magnitude<T1...>{}, magnitude<H2, T2...>{});
return decltype(decltype(remove_positive_power(magnitude<H1>{})){} *
decltype(common_magnitude(magnitude<T1...>{}, magnitude<H2, T2...>{})){}){};
} else if constexpr (detail::get_base_value(H2) < detail::get_base_value(H1)) {
// When H2 has the smaller base, prepend to result from recursion.
return remove_positive_power(magnitude<H2>{}) * common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{});
return decltype(decltype(remove_positive_power(magnitude<H2>{})){} *
decltype(common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{})){}){};
} else {
// When the bases are equal, pick whichever has the lower power.
constexpr auto common_tail = common_magnitude(magnitude<T1...>{}, magnitude<T2...>{});
constexpr auto common_tail = decltype(common_magnitude(magnitude<T1...>{}, magnitude<T2...>{})){};
if constexpr (detail::get_exponent(H1) < detail::get_exponent(H2)) {
return magnitude<H1>{} * common_tail;
return decltype(magnitude<H1>{} * common_tail){};
} else {
return magnitude<H2>{} * common_tail;
return decltype(magnitude<H2>{} * common_tail){};
}
}
}
@@ -758,7 +748,7 @@ template<auto H1, auto... T1, auto H2, auto... T2>
template<auto... Ms>
[[nodiscard]] consteval auto common_magnitude_type_impl(magnitude<Ms...>)
{
return (... * decltype(get_base_value(Ms)){}) * std::intmax_t{};
return (std::intmax_t{} * ... * decltype(get_base_value(Ms)){});
}
// Returns the most precise type to express the magnitude factor
@@ -791,7 +781,7 @@ struct prime_factorization {
if constexpr (opt.has_value()) {
return opt.value(); // NOLINT(bugprone-unchecked-optional-access)
} else {
return static_cast<std::intmax_t>(factorizer::find_first_factor(N));
return static_cast<std::intmax_t>(decltype(factorizer::find_first_factor<N>())::value);
}
}
@@ -826,14 +816,15 @@ inline constexpr Magnitude auto mag = detail::prime_factorization_v<V>;
MP_UNITS_EXPORT template<std::intmax_t N, std::intmax_t D>
requires detail::gt_zero<N>
inline constexpr Magnitude auto mag_ratio = detail::prime_factorization_v<N> / detail::prime_factorization_v<D>;
inline constexpr Magnitude auto mag_ratio =
decltype(detail::prime_factorization_v<N> / detail::prime_factorization_v<D>){};
/**
* @brief Create a Magnitude which is some rational number raised to a rational power.
*/
MP_UNITS_EXPORT template<std::intmax_t Base, std::intmax_t Pow>
requires detail::gt_zero<Base>
inline constexpr Magnitude auto mag_power = pow<Pow>(mag<Base>);
inline constexpr Magnitude auto mag_power = decltype(pow<Pow>(mag<Base>)){};
namespace detail {
@@ -862,20 +853,20 @@ template<Magnitude auto M>
{
constexpr auto exp10 = extract_power_of_10(M);
constexpr Magnitude auto base = M / mag_power<10, exp10>;
constexpr Magnitude auto num = numerator(base);
constexpr Magnitude auto den = denominator(base);
using base = decltype(M / mag_power<10, exp10>);
using num = decltype(numerator(base{}));
using den = decltype(denominator(base{}));
// TODO address the below
static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported");
static_assert(base{} == num{} / den{}, "Printing rational powers, or irrational bases, not yet supported");
constexpr auto num_value = get_value<std::intmax_t>(num);
constexpr auto den_value = get_value<std::intmax_t>(den);
using num_value = decltype(get_value<std::intmax_t>(num{}));
using den_value = decltype(get_value<std::intmax_t>(den{}));
if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) {
if constexpr (num_value{} == 1 && den_value{} == 1 && exp10 != 0) {
return base_multiplier + superscript<exp10>();
} else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) {
auto txt = symbol_text("[") + regular<num_value>();
if constexpr (den_value == 1) {
} else if constexpr (num_value{} != 1 || den_value{} != 1 || exp10 != 0) {
auto txt = symbol_text("[") + regular<num_value{}>();
if constexpr (den_value{} == 1) {
if constexpr (exp10 == 0) {
return txt + symbol_text("]");
} else {
@@ -883,9 +874,9 @@ template<Magnitude auto M>
}
} else {
if constexpr (exp10 == 0) {
return txt + symbol_text("/") + regular<den_value>() + symbol_text("]");
return txt + symbol_text("/") + regular<den_value{}>() + symbol_text("]");
} else {
return txt + symbol_text("/") + regular<den_value>() + symbol_text(" ") + base_multiplier +
return txt + symbol_text("/") + regular<den_value{}>() + symbol_text(" ") + base_multiplier +
superscript<exp10>() + symbol_text("]");
}
}

View File

@@ -46,9 +46,9 @@ namespace mp_units {
namespace detail {
template<auto UFrom, auto UTo>
concept IntegralConversionFactor =
Unit<decltype(UFrom)> && Unit<decltype(UTo)> &&
is_integral(decltype(decltype(get_canonical_unit(UFrom))::mag / decltype(get_canonical_unit(UTo))::mag){});
concept IntegralConversionFactor = Unit<decltype(UFrom)> && Unit<decltype(UTo)> &&
decltype(is_integral(decltype(decltype(get_canonical_unit(UFrom))::mag /
decltype(get_canonical_unit(UTo))::mag){}))::value;
template<typename QFrom, typename QTo>
concept QuantityConvertibleTo =

View File

@@ -627,9 +627,9 @@ template<Unit U1, Unit U2>
using canonical_lhs = decltype(get_canonical_unit(U1{}));
using canonical_rhs = decltype(get_canonical_unit(U2{}));
if constexpr (is_integral(decltype(canonical_lhs::mag / canonical_rhs::mag){}))
if constexpr (decltype(is_integral(decltype(canonical_lhs::mag / canonical_rhs::mag){}))::value)
return u2;
else if constexpr (is_integral(decltype(canonical_rhs::mag / canonical_lhs::mag){}))
else if constexpr (decltype(is_integral(decltype(canonical_rhs::mag / canonical_lhs::mag){}))::value)
return u1;
else {
constexpr auto cm = decltype(detail::common_magnitude(canonical_lhs::mag, canonical_rhs::mag)){};

View File

@@ -113,11 +113,22 @@ struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::durati
}
};
namespace detail {
constexpr auto as_ratio(Magnitude auto m)
requires(decltype(is_rational(decltype(m){}))::value)
{
return std::ratio<decltype(get_value<std::intmax_t>(numerator(m))){},
decltype(get_value<std::intmax_t>(denominator(m))){}>{};
}
} // namespace detail
template<QuantityOf<isq::time> Q>
[[nodiscard]] constexpr auto to_chrono_duration(const Q& q)
{
constexpr detail::ratio r = detail::as_ratio(decltype(get_canonical_unit(Q::unit))::mag);
return std::chrono::duration<typename Q::rep, std::ratio<r.num, r.den>>{q};
return std::chrono::duration<typename Q::rep, decltype(detail::as_ratio(decltype(get_canonical_unit(Q::unit))::mag))>{
q};
}
template<QuantityPointOf<isq::time> QP>
@@ -126,8 +137,8 @@ template<QuantityPointOf<isq::time> QP>
{
using clock = MP_UNITS_TYPENAME decltype(QP::absolute_point_origin)::clock;
using rep = MP_UNITS_TYPENAME QP::rep;
constexpr detail::ratio r = detail::as_ratio(decltype(get_canonical_unit(QP::unit))::mag);
using ret_type = std::chrono::time_point<clock, std::chrono::duration<rep, std::ratio<r.num, r.den>>>;
using ret_type = std::chrono::time_point<
clock, std::chrono::duration<rep, decltype(detail::as_ratio(decltype(get_canonical_unit(QP::unit))::mag))>>;
return ret_type(to_chrono_duration(qp - qp.absolute_point_origin));
}

View File

@@ -32,7 +32,7 @@ namespace {
template<std::size_t BasisSize, std::size_t... Is>
constexpr bool check_primes(std::index_sequence<Is...>)
{
return ((Is < 2 || wheel_factorizer<BasisSize>::is_prime(Is) == is_prime_by_trial_division(Is)) && ...);
return ((Is < 2 || wheel_factorizer<BasisSize>::template is_prime<Is>() == is_prime_by_trial_division(Is)) && ...);
}
static_assert(check_primes<2>(std::make_index_sequence<122>{}));
@@ -45,7 +45,7 @@ static_assert(check_primes<2>(std::make_index_sequence<122>{}));
// using numbers of the form (210 * n + 121) as trial divisors, which is a problem if any are prime. For n = 1, we have
// a divisor of (210 + 121 = 331), which happens to be prime but will not be used. Thus, (331 * 331 = 109561) is a
// composite number which could wrongly appear prime if we skip over 331.
static_assert(wheel_factorizer<4>::is_prime(109'561) == is_prime_by_trial_division(109'561));
static_assert(wheel_factorizer<4>::is_prime<109'561>() == is_prime_by_trial_division(109'561));
static_assert(wheel_factorizer<1>::coprimes_in_first_wheel.size() == 1);
static_assert(wheel_factorizer<2>::coprimes_in_first_wheel.size() == 2);
@@ -62,16 +62,16 @@ static_assert(wheel_factorizer<3>::coprimes_in_first_wheel[5] == 19);
static_assert(wheel_factorizer<3>::coprimes_in_first_wheel[6] == 23);
static_assert(wheel_factorizer<3>::coprimes_in_first_wheel[7] == 29);
static_assert(!wheel_factorizer<1>::is_prime(0));
static_assert(!wheel_factorizer<1>::is_prime(1));
static_assert(wheel_factorizer<1>::is_prime(2));
static_assert(!wheel_factorizer<1>::is_prime<0>());
static_assert(!wheel_factorizer<1>::is_prime<1>());
static_assert(wheel_factorizer<1>::is_prime<2>());
static_assert(!wheel_factorizer<2>::is_prime(0));
static_assert(!wheel_factorizer<2>::is_prime(1));
static_assert(wheel_factorizer<2>::is_prime(2));
static_assert(!wheel_factorizer<2>::is_prime<0>());
static_assert(!wheel_factorizer<2>::is_prime<1>());
static_assert(wheel_factorizer<2>::is_prime<2>());
static_assert(!wheel_factorizer<3>::is_prime(0));
static_assert(!wheel_factorizer<3>::is_prime(1));
static_assert(wheel_factorizer<3>::is_prime(2));
static_assert(!wheel_factorizer<3>::is_prime<0>());
static_assert(!wheel_factorizer<3>::is_prime<1>());
static_assert(wheel_factorizer<3>::is_prime<2>());
} // namespace