Merge pull request #374 from chiphogg/chiphogg/exp0

Remove `exp` member from `ratio`
This commit is contained in:
Mateusz Pusz
2022-07-28 10:39:44 +02:00
committed by GitHub
18 changed files with 75 additions and 304 deletions

View File

@@ -54,7 +54,7 @@ using length = quantity<dim_length, U, Rep>;
namespace fps {
struct foot : named_scaled_unit<foot, "ft", as_magnitude<ratio(3'048, 1'000, -1)>(), metre> {};
struct foot : named_scaled_unit<foot, "ft", as_magnitude<ratio{3'048, 10'000}>(), metre> {};
struct yard : named_scaled_unit<yard, "yd", as_magnitude<3>(), foot> {};
struct dim_length : base_dimension<"L", foot> {};
@@ -94,10 +94,7 @@ void unknown_dimensions()
std::cout << si_fps_area << "\n";
}
std::ostream& operator<<(std::ostream& os, const ratio& r)
{
return os << "ratio{" << r.num << ", " << r.den << ", " << r.exp << "}";
}
std::ostream& operator<<(std::ostream& os, const ratio& r) { return os << "ratio{" << r.num << ", " << r.den << "}"; }
template<Unit U>
std::ostream& operator<<(std::ostream& os, const U& u)

View File

@@ -39,108 +39,20 @@ template<typename T>
return v < 0 ? -v : v;
}
// the following functions enable gcd and related computations on ratios
// with exponents. They avoid overflow. Further information here:
// https://github.com/mpusz/units/issues/62#issuecomment-588152833
// Computes (a * b) mod m relies on unsigned integer arithmetic, should not
// overflow
[[nodiscard]] constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m)
{
std::uint64_t res = 0;
if (b >= m) {
if (m > UINT64_MAX / 2u) {
b -= m;
} else {
b %= m;
}
}
while (a != 0) {
if (a & 1) {
if (b >= m - res) {
res -= m;
}
res += b;
}
a >>= 1;
std::uint64_t temp_b = b;
if (b >= m - b) {
temp_b -= m;
}
b += temp_b;
}
return res;
}
// Calculates (a ^ e) mod m , should not overflow.
[[nodiscard]] constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m)
{
a %= m;
std::uint64_t result = 1;
while (e > 0) {
if (e & 1) {
result = mulmod(result, a, m);
}
a = mulmod(a, a, m);
e >>= 1;
}
return result;
}
// gcd(a * 10 ^ e, b), should not overflow
[[nodiscard]] constexpr std::intmax_t gcdpow(std::intmax_t a, std::intmax_t e, std::intmax_t b) noexcept
{
assert(a > 0);
assert(e >= 0);
assert(b > 0);
// gcd(i, j) = gcd(j, i mod j) for j != 0 Euclid;
//
// gcd(a 10^e, b) = gcd(b, a 10^e mod b)
//
// (a 10^e) mod b -> [ (a mod b) (10^e mod b) ] mod b
return std::gcd(
b, static_cast<std::intmax_t>(mulmod(static_cast<std::uint64_t>(a % b),
modpow(10, static_cast<std::uint64_t>(e), static_cast<std::uint64_t>(b)),
static_cast<std::uint64_t>(b))));
}
constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs)
{
std::intmax_t tmp = lhs;
lhs = rhs;
rhs = tmp;
}
// Computes the rational gcd of n1/d1 x 10^e1 and n2/d2 x 10^e2
[[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, std::intmax_t n2,
std::intmax_t d2, std::intmax_t e2) noexcept
// Computes the rational gcd of n1/d1 and n2/d2
[[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t n2, std::intmax_t d2) noexcept
{
// Short cut for equal ratios
if (n1 == n2 && d1 == d2 && e1 == e2) {
return std::array{n1, d1, e1};
if (n1 == n2 && d1 == d2) {
return std::array{n1, d1};
}
if (e2 > e1) {
detail::cwap(n1, n2);
detail::cwap(d1, d2);
detail::cwap(e1, e2);
}
std::intmax_t exp = e2; // minimum
// gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d
assert(std::numeric_limits<std::intmax_t>::max() / n1 > d2);
assert(std::numeric_limits<std::intmax_t>::max() / n2 > d1);
std::intmax_t num = detail::gcdpow(n1 * d2, e1 - e2, n2 * d1);
std::intmax_t num = std::gcd(n1 * d2, n2 * d1);
assert(std::numeric_limits<std::intmax_t>::max() / d1 > d2);
@@ -148,29 +60,19 @@ constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs)
std::intmax_t gcd = std::gcd(num, den);
return std::array{num / gcd, den / gcd, exp};
return std::array{num / gcd, den / gcd};
}
constexpr void normalize(std::intmax_t& num, std::intmax_t& den, std::intmax_t& exp)
constexpr void normalize(std::intmax_t& num, std::intmax_t& den)
{
if (num == 0) {
den = 1;
exp = 0;
return;
}
std::intmax_t gcd = std::gcd(num, den);
num = num * (den < 0 ? -1 : 1) / gcd;
den = detail::abs(den) / gcd;
while (num % 10 == 0) {
num /= 10;
++exp;
}
while (den % 10 == 0) {
den /= 10;
--exp;
}
}
[[nodiscard]] constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs)

View File

@@ -75,12 +75,7 @@ constexpr std::intmax_t pow_10(std::intmax_t v)
template<ratio R>
constexpr auto to_std_ratio_impl()
{
if constexpr (R.exp == 0)
return std::ratio<R.num, R.den>{};
else if constexpr (R.exp > 0)
return std::ratio<R.num * pow_10(R.exp), R.den>{};
else
return std::ratio<R.num, R.den * pow_10(-R.exp)>{};
}
} // namespace detail

View File

@@ -189,9 +189,6 @@ constexpr widen_t<T> compute_base_power(BasePower auto bp)
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>) {
@@ -201,7 +198,7 @@ constexpr widen_t<T> compute_base_power(BasePower auto bp)
}
}
auto power = numerator(bp.power);
auto power = bp.power.num;
return int_power(static_cast<widen_t<T>>(bp.get_base()), power);
}
@@ -344,7 +341,7 @@ inline constexpr bool is_base_power_pack_valid = all_base_powers_valid<BPs...> &
constexpr bool is_rational(BasePower auto bp)
{
return std::is_integral_v<decltype(bp.get_base())> && (bp.power.den == 1) && (bp.power.exp >= 0);
return std::is_integral_v<decltype(bp.get_base())> && (bp.power.den == 1);
}
constexpr bool is_integral(BasePower auto bp) { return is_rational(bp) && bp.power.num > 0; }
@@ -498,8 +495,8 @@ namespace detail {
template<auto BP>
constexpr auto integer_part(magnitude<BP>)
{
constexpr auto power_num = numerator(BP.power);
constexpr auto power_den = denominator(BP.power);
constexpr auto power_num = BP.power.num;
constexpr auto power_den = BP.power.den;
if constexpr (std::is_integral_v<decltype(BP.get_base())> && (power_num >= power_den)) {
constexpr auto largest_integer_power = [=](BasePower auto bp) {
@@ -556,7 +553,7 @@ namespace detail {
template<auto BP>
constexpr auto remove_positive_power(magnitude<BP> m)
{
if constexpr (numerator(BP.power) < 0) {
if constexpr (BP.power.num < 0) {
return m;
} else {
return magnitude<>{};
@@ -652,8 +649,17 @@ template<ratio R>
requires(R.num > 0)
constexpr Magnitude auto as_magnitude()
{
return pow<ratio{R.exp}>(detail::prime_factorization_v<10>) * detail::prime_factorization_v<R.num> /
detail::prime_factorization_v<R.den>;
return detail::prime_factorization_v<R.num> / detail::prime_factorization_v<R.den>;
}
/**
* @brief Create a Magnitude which is some rational number raised to a rational power.
*/
template<ratio Base, ratio Pow>
requires(Base.num > 0)
constexpr Magnitude auto mag_power()
{
return pow<Pow>(as_magnitude<Base>());
}
namespace detail {
@@ -663,7 +669,7 @@ constexpr ratio get_power(T base, magnitude<BPs...>)
return ((BPs.get_base() == base ? BPs.power : ratio{0}) + ... + ratio{0});
}
constexpr std::intmax_t integer_part(ratio r) { return numerator(r) / denominator(r); }
constexpr std::intmax_t integer_part(ratio r) { return r.num / r.den; }
constexpr std::intmax_t extract_power_of_10(Magnitude auto m)
{

View File

@@ -43,42 +43,28 @@ constexpr ratio inverse(const ratio& r);
/**
* @brief Provides compile-time rational arithmetic support.
*
* This class is really similar to @c std::ratio but gets an additional `Exp`
* template parameter that defines the exponent of the ratio. Another important
* difference is the fact that the objects of that class are used as class NTTPs
* rather then a type template parameter kind.
* This class is really similar to @c std::ratio. An important difference is the fact that the objects of that class
* are used as class NTTPs rather then a type template parameter kind.
*/
struct ratio {
std::intmax_t num;
std::intmax_t den;
std::intmax_t exp;
constexpr explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1, std::intmax_t e = 0) : num(n), den(d), exp(e)
constexpr explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1) : num(n), den(d)
{
gsl_Expects(den != 0);
detail::normalize(num, den, exp);
detail::normalize(num, den);
}
[[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default;
[[nodiscard]] friend constexpr auto operator<=>(const ratio& lhs, const ratio& rhs) { return (lhs - rhs).num <=> 0; }
[[nodiscard]] friend constexpr ratio operator-(const ratio& r) { return ratio(-r.num, r.den, r.exp); }
[[nodiscard]] friend constexpr ratio operator-(const ratio& r) { return ratio(-r.num, r.den); }
[[nodiscard]] friend constexpr ratio operator+(ratio lhs, ratio rhs)
{
// First, get the inputs into a common exponent.
const auto common_exp = std::min(lhs.exp, rhs.exp);
auto commonify = [common_exp](ratio& r) {
while (r.exp > common_exp) {
r.num *= 10;
--r.exp;
}
};
commonify(lhs);
commonify(rhs);
return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den, common_exp};
return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den};
}
[[nodiscard]] friend constexpr ratio operator-(const ratio& lhs, const ratio& rhs) { return lhs + (-rhs); }
@@ -88,96 +74,27 @@ struct ratio {
const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den);
const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den);
return ratio(detail::safe_multiply(lhs.num / gcd1, rhs.num / gcd2),
detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1), lhs.exp + rhs.exp);
detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1));
}
[[nodiscard]] friend constexpr ratio operator/(const ratio& lhs, const ratio& rhs) { return lhs * inverse(rhs); }
[[nodiscard]] friend constexpr std::intmax_t numerator(const ratio& r)
{
std::intmax_t true_num = r.num;
for (auto i = r.exp; i > 0; --i) {
true_num *= 10;
}
return true_num;
}
[[nodiscard]] friend constexpr std::intmax_t denominator(const ratio& r)
{
std::intmax_t true_den = r.den;
for (auto i = r.exp; i < 0; ++i) {
true_den *= 10;
}
return true_den;
}
};
[[nodiscard]] constexpr ratio inverse(const ratio& r) { return ratio(r.den, r.num, -r.exp); }
[[nodiscard]] constexpr ratio inverse(const ratio& r) { return ratio(r.den, r.num); }
[[nodiscard]] constexpr bool is_integral(const ratio& r)
{
if (r.exp < 0) {
return false;
} else {
return detail::gcdpow(r.num, r.exp, r.den) == r.den;
}
}
[[nodiscard]] constexpr bool is_integral(const ratio& r) { return r.num % r.den == 0; }
namespace detail {
[[nodiscard]] constexpr auto make_exp_align(const ratio& r, std::intmax_t alignment)
{
gsl_Expects(alignment > 0);
const std::intmax_t rem = r.exp % alignment;
if (rem == 0) { // already aligned
return std::array{r.num, r.den, r.exp};
}
if (r.exp > 0) { // remainder is positive
return std::array{r.num * ipow10(rem), r.den, r.exp - rem};
}
// remainder is negative
return std::array{r.num, r.den * ipow10(-rem), r.exp - rem};
}
template<std::intmax_t N>
requires gt_zero<N>
[[nodiscard]] constexpr ratio root(const ratio& r)
{
if constexpr (N == 1) {
return r;
} else {
if (r.num == 0) {
return ratio(0);
}
const auto aligned = make_exp_align(r, N);
return ratio(iroot<N>(aligned[0]), iroot<N>(aligned[1]), aligned[2] / N);
}
}
} // namespace detail
template<std::intmax_t Num, std::intmax_t Den = 1>
requires detail::non_zero<Den>
template<std::intmax_t Num>
[[nodiscard]] constexpr ratio pow(const ratio& r)
{
if constexpr (Num == 0) {
return ratio(1);
} else if constexpr (Num == Den) {
} else if constexpr (Num == 1) {
return r;
} else {
// simplify factors first and compute power for positive exponent
constexpr std::intmax_t gcd = std::gcd(Num, Den);
constexpr std::intmax_t num = detail::abs(Num / gcd);
constexpr std::intmax_t den = detail::abs(Den / gcd);
const ratio result = detail::pow_impl<Num>(r);
// integer root loses precision so do pow first
const ratio result = detail::root<den>(detail::pow_impl<num>(r));
if constexpr (Num * Den < 0) { // account for negative exponent
if constexpr (Num < 0) { // account for negative exponent
return inverse(result);
} else {
return result;
@@ -185,15 +102,11 @@ template<std::intmax_t Num, std::intmax_t Den = 1>
}
}
[[nodiscard]] constexpr ratio sqrt(const ratio& r) { return pow<1, 2>(r); }
[[nodiscard]] constexpr ratio cbrt(const ratio& r) { return pow<1, 3>(r); }
// common_ratio
[[nodiscard]] constexpr ratio common_ratio(const ratio& r1, const ratio& r2)
{
const auto res = detail::gcd_frac(r1.num, r1.den, r1.exp, r2.num, r2.den, r2.exp);
return ratio(res[0], res[1], res[2]);
const auto res = detail::gcd_frac(r1.num, r1.den, r2.num, r2.den);
return ratio(res[0], res[1]);
}
} // namespace units

View File

@@ -37,7 +37,7 @@ namespace units::isq::si::hep {
// effective cross-sectional area according to EU council directive 80/181/EEC
// https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:01980L0181-20090527#page=10
// https://www.fedlex.admin.ch/eli/cc/1994/3109_3109_3109/de
struct barn : named_scaled_unit<barn, "b", as_magnitude<ratio(1, 1, -28)>(), square_metre> {};
struct barn : named_scaled_unit<barn, "b", mag_power<10, -28>(), square_metre> {};
struct yocto_barn : prefixed_unit<yocto_barn, yocto, barn> {};
struct zepto_barn : prefixed_unit<zepto_barn, zepto, barn> {};
struct atto_barn : prefixed_unit<atto_barn, atto, barn> {};

View File

@@ -44,7 +44,7 @@ namespace units::isq::si::hep {
struct eV_per_c2 :
named_scaled_unit<eV_per_c2, basic_symbol_text{"eV/c²", "eV/c^2"},
as_magnitude<ratio(17'826'619'216'279, 1'000'000'000'000, -35)>(), kilogram> {};
as_magnitude<ratio(17'826'619'216'279, 1'000'000'000'000)>() * mag_power<10, -35>(), kilogram> {};
struct feV_per_c2 : prefixed_unit<feV_per_c2, femto, eV_per_c2> {};
struct peV_per_c2 : prefixed_unit<peV_per_c2, pico, eV_per_c2> {};
struct neV_per_c2 : prefixed_unit<neV_per_c2, nano, eV_per_c2> {};
@@ -60,11 +60,14 @@ struct PeV_per_c2 : prefixed_unit<PeV_per_c2, peta, eV_per_c2> {};
struct EeV_per_c2 : prefixed_unit<EeV_per_c2, exa, eV_per_c2> {};
struct YeV_per_c2 : prefixed_unit<YeV_per_c2, yotta, eV_per_c2> {};
struct electron_mass :
named_scaled_unit<eV_per_c2, "m_e", as_magnitude<ratio(9'109'383'701'528, 1'000'000'000'000, -31)>(), kilogram> {};
named_scaled_unit<eV_per_c2, "m_e",
as_magnitude<ratio(9'109'383'701'528, 1'000'000'000'000)>() * mag_power<10, -31>(), kilogram> {};
struct proton_mass :
named_scaled_unit<eV_per_c2, "m_p", as_magnitude<ratio(1'672'621'923'695, 1'000'000'000'000, -27)>(), kilogram> {};
named_scaled_unit<eV_per_c2, "m_p",
as_magnitude<ratio(1'672'621'923'695, 1'000'000'000'000)>() * mag_power<10, -27>(), kilogram> {};
struct neutron_mass :
named_scaled_unit<eV_per_c2, "m_n", as_magnitude<ratio(1'674'927'498'049, 1'000'000'000'000, -27)>(), kilogram> {};
named_scaled_unit<eV_per_c2, "m_n",
as_magnitude<ratio(1'674'927'498'049, 1'000'000'000'000)>() * mag_power<10, -27>(), kilogram> {};
struct dim_mass : isq::dim_mass<eV_per_c2> {};

View File

@@ -41,7 +41,8 @@ namespace units::isq::si::hep {
struct kilogram_metre_per_second : derived_unit<kilogram_metre_per_second> {};
struct eV_per_c :
named_scaled_unit<eV_per_c, "eV/c", as_magnitude<ratio(5'344'285'992'678, 1'000'000'000'000, -35)>(),
named_scaled_unit<eV_per_c, "eV/c",
as_magnitude<ratio(5'344'285'992'678, 1'000'000'000'000)>() * mag_power<10, -35>(),
kilogram_metre_per_second> {};
struct feV_per_c : prefixed_unit<feV_per_c, femto, eV_per_c> {};
struct peV_per_c : prefixed_unit<peV_per_c, pico, eV_per_c> {};

View File

@@ -42,7 +42,7 @@ struct light_year : named_scaled_unit<light_year, "ly", as_magnitude<94607304725
struct parsec : named_scaled_unit<parsec, "pc", as_magnitude<30'856'775'814'913'673>(), si::metre> {};
// https://en.wikipedia.org/wiki/Angstrom
struct angstrom : named_scaled_unit<angstrom, "angstrom", as_magnitude<ratio(1, 1, -10)>(), si::metre> {};
struct angstrom : named_scaled_unit<angstrom, "angstrom", mag_power<10, -10>(), si::metre> {};
#ifndef UNITS_NO_LITERALS

View File

@@ -37,7 +37,7 @@ namespace units::isq::si::international {
// si::international yard
// https://en.wikipedia.org/wiki/International_yard_and_pound
struct yard : named_scaled_unit<yard, "yd", as_magnitude<ratio(9'144, 1'000, -1)>(), si::metre> {};
struct yard : named_scaled_unit<yard, "yd", as_magnitude<ratio{9'144, 10'000}>(), si::metre> {};
// si::international foot
// https://en.wikipedia.org/wiki/Foot_(unit)#International_foot

View File

@@ -38,11 +38,16 @@ namespace units::isq::si::typographic {
// TODO Conflicts with (https://en.wikipedia.org/wiki/Pica_(typography)), verify correctness of below conversion factors
// and provide hyperlinks to definitions
struct pica_comp :
named_scaled_unit<pica_comp, "pica(comp)", as_magnitude<ratio(4233333, 1000000, -3)>(), si::metre> {};
struct pica_prn : named_scaled_unit<pica_prn, "pica(prn)", as_magnitude<ratio(2108759, 500000, -3)>(), si::metre> {};
named_scaled_unit<pica_comp, "pica(comp)", as_magnitude<4'233'333>() * mag_power<10, -9>(), si::metre> {};
struct pica_prn :
named_scaled_unit<pica_prn, "pica(prn)", as_magnitude<ratio(2108759, 500000)>() * mag_power<10, -3>(), si::metre> {
};
struct point_comp :
named_scaled_unit<point_comp, "point(comp)", as_magnitude<ratio(1763889, 500000, -4)>(), si::metre> {};
struct point_prn : named_scaled_unit<point_prn, "point(prn)", as_magnitude<ratio(1757299, 500000, -4)>(), si::metre> {};
named_scaled_unit<point_comp, "point(comp)", as_magnitude<ratio(1763889, 500000)>() * mag_power<10, -4>(),
si::metre> {};
struct point_prn :
named_scaled_unit<point_prn, "point(prn)", as_magnitude<ratio(1757299, 500000)>() * mag_power<10, -4>(),
si::metre> {};
#ifndef UNITS_NO_LITERALS

View File

@@ -58,7 +58,7 @@ struct exakatal : prefixed_unit<exakatal, exa, katal> {};
struct zettakatal : prefixed_unit<zettakatal, zetta, katal> {};
struct yottakatal : prefixed_unit<yottakatal, yotta, katal> {};
struct enzyme_unit : named_scaled_unit<enzyme_unit, "U", as_magnitude<ratio(1, 60, -6)>(), katal> {};
struct enzyme_unit : named_scaled_unit<enzyme_unit, "U", as_magnitude<ratio(1, 60)>() * mag_power<10, -6>(), katal> {};
struct dim_catalytic_activity :
isq::dim_catalytic_activity<dim_catalytic_activity, katal, dim_time, dim_amount_of_substance> {};

View File

@@ -56,7 +56,8 @@ struct yottajoule : prefixed_unit<yottajoule, yotta, joule> {};
// N.B. electron charge (and eV) is an exact constant:
// https://www.bipm.org/documents/20126/41483022/SI-Brochure-9.pdf#page=147
struct electronvolt :
named_scaled_unit<electronvolt, "eV", as_magnitude<ratio(1'602'176'634, 1'000'000'000, -19)>(), joule> {};
named_scaled_unit<electronvolt, "eV", as_magnitude<ratio(1'602'176'634, 1'000'000'000)>() * mag_power<10, -19>(),
joule> {};
struct gigaelectronvolt : prefixed_unit<gigaelectronvolt, giga, electronvolt> {};
struct dim_energy : isq::dim_energy<dim_energy, joule, dim_force, dim_length> {};

View File

@@ -79,7 +79,9 @@ struct zettatonne : prefixed_unit<zettatonne, zetta, tonne> {};
struct yottatonne : prefixed_unit<yottatonne, yotta, tonne> {};
struct dalton :
named_scaled_unit<dalton, "Da", as_magnitude<ratio(16'605'390'666'050, 10'000'000'000'000, -27)>(), kilogram> {};
named_scaled_unit<dalton, "Da",
as_magnitude<ratio(16'605'390'666'050, 10'000'000'000'000)>() * mag_power<10, -27>(), kilogram> {
};
struct dim_mass : isq::dim_mass<kilogram> {};

View File

@@ -154,13 +154,6 @@ TEST_CASE("make_ratio performs prime factorization correctly")
SECTION("Supports fractions") { CHECK(as_magnitude<ratio{5, 8}>() == magnitude<base_power{2, -3}, base_power{5}>{}); }
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")
{
// This was taken from a case which failed when we used `int` for our base to store prime numbers.

View File

@@ -579,8 +579,7 @@ static_assert(is_same_v<decltype(1_q_km % 1_q_m), length<kilometre, std::int64_t
// different dimensions
static_assert(compare<decltype(1_q_m_per_s * 1_q_s), length<metre, std::int64_t>>);
static_assert(
compare<decltype(1_q_m_per_s * 1_q_h), length<scaled_unit<as_magnitude<ratio(36, 1, 2)>(), metre>, std::int64_t>>);
static_assert(compare<decltype(1_q_m_per_s * 1_q_h), length<scaled_unit<as_magnitude<3600>(), metre>, std::int64_t>>);
static_assert(
compare<decltype(1_q_m * 1_q_min), quantity<unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, 1>>,
scaled_unit<as_magnitude<60>(), unknown_coherent_unit>, std::int64_t>>);
@@ -590,7 +589,7 @@ static_assert(
static_assert(compare<decltype(1 / 1_q_Hz), isq::si::time<second, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_km),
quantity<unknown_dimension<exponent<dim_length, -1>>,
scaled_unit<as_magnitude<ratio(1, 1, -3)>(), unknown_coherent_unit>, std::int64_t>>);
scaled_unit<as_magnitude<ratio(1, 1000)>(), unknown_coherent_unit>, std::int64_t>>);
static_assert(compare<decltype(1_q_km / 1_q_m), dimensionless<scaled_unit<as_magnitude<1000>(), one>, std::int64_t>>);
static_assert(compare<decltype(1_q_m / 1_q_s), speed<metre_per_second, std::int64_t>>);
static_assert(
@@ -889,7 +888,7 @@ static_assert(is_same_v<decltype(10_q_m / 5_q_s),
quantity<unknown_dimension<units::exponent<dim_length, 1>, units::exponent<dim_time, -1>>,
scaled_unit<as_magnitude<1>(), unknown_coherent_unit>, std::int64_t>>);
static_assert(
is_same_v<decltype(1_q_mm + 1_q_km), length<scaled_unit<as_magnitude<ratio(1, 1, -3)>(), metre>, std::int64_t>>);
is_same_v<decltype(1_q_mm + 1_q_km), length<scaled_unit<as_magnitude<ratio(1, 1000)>(), metre>, std::int64_t>>);
#else

View File

@@ -28,11 +28,6 @@ using namespace units;
static_assert(ratio(2, 4) == ratio(1, 2));
// basic exponents tests
static_assert(ratio(2, 40, 1) == ratio(1, 20, 1));
static_assert(ratio(20, 4, -1) == ratio(10, 2, -1));
static_assert(ratio(200, 5) == ratio(20'000, 50, -1));
static_assert(ratio(1) * ratio(3, 8) == ratio(3, 8));
static_assert(ratio(3, 8) * ratio(1) == ratio(3, 8));
static_assert(ratio(4) * ratio(1, 8) == ratio(1, 2));
@@ -45,21 +40,12 @@ static_assert(-ratio(3, 8) == ratio(-3, 8));
// ratio addition
static_assert(ratio(1, 2) + ratio(1, 3) == ratio(5, 6));
static_assert(ratio(1, 3, 2) + ratio(11, 6) == ratio(211, 6)); // 100/3 + 11/6
// multiply with exponents
static_assert(ratio(1, 8, 2) * ratio(2, 1, 4) == ratio(1, 4, 6));
static_assert(ratio(1, 2, -4) * ratio(8, 1, 3) == ratio(4, 1, -1));
static_assert(ratio(4) / ratio(2) == ratio(2));
static_assert(ratio(2) / ratio(8) == ratio(1, 4));
static_assert(ratio(1, 8) / ratio(2) == ratio(1, 16));
static_assert(ratio(6) / ratio(3) == ratio(2));
// divide with exponents
static_assert(ratio(1, 8, -6) / ratio(2, 1, -8) == ratio(1, 16, 2));
static_assert(ratio(6, 1, 4) / ratio(3) == ratio(2, 1, 4));
static_assert(pow<0>(ratio(2)) == ratio(1));
static_assert(pow<1>(ratio(2)) == ratio(2));
static_assert(pow<2>(ratio(2)) == ratio(4));
@@ -69,27 +55,6 @@ static_assert(pow<1>(ratio(1, 2)) == ratio(1, 2));
static_assert(pow<2>(ratio(1, 2)) == ratio(1, 4));
static_assert(pow<3>(ratio(1, 2)) == ratio(1, 8));
// pow with exponents
static_assert(pow<2>(ratio(1, 2, 3)) == ratio(1, 4, 6));
static_assert(pow<4, 2>(ratio(1, 2, 3)) == ratio(1, 4, 6));
static_assert(pow<3>(ratio(1, 2, -6)) == ratio(1, 8, -18));
static_assert(sqrt(ratio(9)) == ratio(3));
static_assert(cbrt(ratio(27)) == ratio(3));
static_assert(sqrt(ratio(4)) == ratio(2));
static_assert(cbrt(ratio(8)) == ratio(2));
static_assert(sqrt(ratio(1)) == ratio(1));
static_assert(cbrt(ratio(1)) == ratio(1));
static_assert(sqrt(ratio(0)) == ratio(0));
static_assert(cbrt(ratio(0)) == ratio(0));
static_assert(sqrt(ratio(1, 4)) == ratio(1, 2));
static_assert(cbrt(ratio(1, 8)) == ratio(1, 2));
// sqrt with exponents
static_assert(sqrt(ratio(9, 1, 2)) == ratio(3, 1, 1));
static_assert(cbrt(ratio(27, 1, 3)) == ratio(3, 1, 1));
static_assert(cbrt(ratio(27, 1, 2)) == ratio(13, 1, 0));
// common_ratio
static_assert(common_ratio(ratio(1), ratio(1000)) == ratio(1));
static_assert(common_ratio(ratio(1000), ratio(1)) == ratio(1));
@@ -98,20 +63,9 @@ static_assert(common_ratio(ratio(1, 1000), ratio(1)) == ratio(1, 1000));
static_assert(common_ratio(ratio(100, 1), ratio(10, 1)) == ratio(10, 1));
static_assert(common_ratio(ratio(100, 1), ratio(1, 10)) == ratio(1, 10));
// common ratio with exponents
static_assert(common_ratio(ratio(1), ratio(1, 1, 3)) == ratio(1));
static_assert(common_ratio(ratio(10, 1, -1), ratio(1, 1, -3)) == ratio(1, 1, -3));
// numerator and denominator
static_assert(numerator(ratio(3, 4)) == 3);
static_assert(numerator(ratio(3, 7, 2)) == 300);
static_assert(denominator(ratio(3, 4)) == 4);
static_assert(denominator(ratio(3, 7, -2)) == 700);
// comparison
static_assert((ratio(3, 4) <=> ratio(6, 8)) == (0 <=> 0));
static_assert((ratio(3, 4) <=> ratio(-3, 4)) == (0 <=> -1));
static_assert((ratio(-3, 4) <=> ratio(3, -4)) == (0 <=> 0));
static_assert((ratio(1, 1, 1) <=> ratio(10)) == (0 <=> 0));
} // namespace

View File

@@ -36,12 +36,12 @@ using namespace units::isq;
struct metre : named_unit<metre, "m"> {};
struct centimetre : prefixed_unit<centimetre, si::centi, metre> {};
struct kilometre : prefixed_unit<kilometre, si::kilo, metre> {};
struct yard : named_scaled_unit<yard, "yd", as_magnitude<ratio(9'144, 1, -4)>(), metre> {};
struct yard : named_scaled_unit<yard, "yd", as_magnitude<ratio{9'144, 10'000}>(), metre> {};
struct foot : named_scaled_unit<foot, "ft", as_magnitude<ratio(1, 3)>(), yard> {};
struct dim_length : base_dimension<"length", metre> {};
struct second : named_unit<second, "s"> {};
struct hour : named_scaled_unit<hour, "h", as_magnitude<ratio(36, 1, 2)>(), second> {};
struct hour : named_scaled_unit<hour, "h", as_magnitude<3600>(), second> {};
struct dim_time : base_dimension<"time", second> {};
struct kelvin : named_unit<kelvin, "K"> {};
@@ -60,7 +60,7 @@ struct kilometre_per_hour : derived_scaled_unit<kilometre_per_hour, dim_speed, k
static_assert(equivalent<metre::named_unit, metre>);
static_assert(equivalent<metre::scaled_unit, metre>);
static_assert(compare<downcast<scaled_unit<as_magnitude<1>(), metre>>, metre>);
static_assert(compare<downcast<scaled_unit<as_magnitude<ratio(1, 1, -2)>(), metre>>, centimetre>);
static_assert(compare<downcast<scaled_unit<as_magnitude<ratio(1, 100)>(), metre>>, centimetre>);
static_assert(compare<downcast<scaled_unit<yard::mag, metre>>, yard>);
static_assert(compare<downcast<scaled_unit<yard::mag / as_magnitude<3>(), metre>>, foot>);
static_assert(compare<downcast<scaled_unit<kilometre::mag / hour::mag, metre_per_second>>, kilometre_per_hour>);