forked from mpusz/mp-units
NTTP ratio support added (resolves #49)
This commit is contained in:
@@ -30,31 +30,22 @@ namespace units::detail {
|
||||
|
||||
template<Exponent E>
|
||||
requires (E::den == 1 || E::den == 2) // TODO provide support for any den
|
||||
struct exp_ratio {
|
||||
using base_ratio = E::dimension::base_unit::ratio;
|
||||
using positive_ratio = conditional<E::num * E::den < 0, ratio<base_ratio::den, base_ratio::num, -base_ratio::exp>, base_ratio>;
|
||||
static constexpr std::intmax_t N = E::num * E::den < 0 ? -E::num : E::num;
|
||||
using pow = ratio_pow<positive_ratio, N>;
|
||||
using type = conditional<E::den == 2, ratio_sqrt<pow>, pow>;
|
||||
};
|
||||
|
||||
template<typename ExpList>
|
||||
struct base_units_ratio_impl;
|
||||
|
||||
template<typename E, typename... Es>
|
||||
struct base_units_ratio_impl<exp_list<E, Es...>> {
|
||||
using type = ratio_multiply<typename exp_ratio<E>::type, typename base_units_ratio_impl<exp_list<Es...>>::type>;
|
||||
};
|
||||
|
||||
template<typename E>
|
||||
struct base_units_ratio_impl<exp_list<E>> {
|
||||
using type = exp_ratio<E>::type;
|
||||
};
|
||||
constexpr ratio exp_ratio()
|
||||
{
|
||||
const ratio base_ratio = E::dimension::base_unit::ratio;
|
||||
const ratio positive_ratio = E::num * E::den < 0 ? ratio(base_ratio.den, base_ratio.num, -base_ratio.exp) : base_ratio;
|
||||
const std::intmax_t N = E::num * E::den < 0 ? -E::num : E::num;
|
||||
const ratio ratio_pow = pow<N>(positive_ratio);
|
||||
return E::den == 2 ? sqrt(ratio_pow) : ratio_pow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the common ratio of all the references of base units in the derived dimension
|
||||
*/
|
||||
template<typename D>
|
||||
using base_units_ratio = base_units_ratio_impl<typename D::exponents>::type;
|
||||
template<typename... Es>
|
||||
constexpr ratio base_units_ratio(exp_list<Es...>)
|
||||
{
|
||||
return (exp_ratio<Es>() * ...);
|
||||
}
|
||||
|
||||
} // namespace units::detail
|
||||
|
||||
@@ -44,20 +44,20 @@ struct common_quantity_impl<quantity<D, U, Rep1>, quantity<D, U, Rep2>, Rep> {
|
||||
|
||||
template<typename D, typename U1, typename Rep1, typename U2, typename Rep2, typename Rep>
|
||||
struct common_quantity_impl<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, Rep> {
|
||||
using type = quantity<D, downcast_unit<D, common_ratio<typename U1::ratio, typename U2::ratio>>, Rep>;
|
||||
using type = quantity<D, downcast_unit<D, common_ratio(U1::ratio, U2::ratio)>, Rep>;
|
||||
};
|
||||
|
||||
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep>
|
||||
requires same_unit_reference<dimension_unit<D1>, dimension_unit<D2>>::value
|
||||
struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> {
|
||||
using type = quantity<D1, downcast_unit<D1, common_ratio<typename U1::ratio, typename U2::ratio>>, Rep>;
|
||||
using type = quantity<D1, downcast_unit<D1, common_ratio(U1::ratio, U2::ratio)>, Rep>;
|
||||
};
|
||||
|
||||
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep>
|
||||
struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> {
|
||||
using ratio1 = ratio_multiply<typename D1::base_units_ratio, typename U1::ratio>;
|
||||
using ratio2 = ratio_multiply<typename D2::base_units_ratio, typename U2::ratio>;
|
||||
using type = quantity<D1, downcast_unit<D1, common_ratio<ratio1, ratio2>>, Rep>;
|
||||
static constexpr ratio r1 = D1::base_units_ratio * U1::ratio;
|
||||
static constexpr ratio r2 = D2::base_units_ratio * U2::ratio;
|
||||
using type = quantity<D1, downcast_unit<D1, common_ratio(r1, r2)>, Rep>;
|
||||
};
|
||||
|
||||
template<typename D, typename U, typename Rep>
|
||||
|
||||
@@ -34,39 +34,23 @@ template<typename... Es, Unit... Us>
|
||||
inline constexpr bool same_scaled_units<exp_list<Es...>, Us...> = (UnitOf<Us, typename Es::dimension> && ...);
|
||||
|
||||
// deduced_unit
|
||||
template<typename Result, int UnitExpNum, int UnitExpDen, typename UnitRatio>
|
||||
struct ratio_op;
|
||||
|
||||
template<typename Result, int UnitExpDen, typename UnitRatio>
|
||||
struct ratio_op<Result, 0, UnitExpDen, UnitRatio> {
|
||||
using ratio = Result;
|
||||
};
|
||||
template<Exponent E>
|
||||
constexpr ratio inverse_if_negative(const ratio& r)
|
||||
{
|
||||
if constexpr(E::num * E::den > 0)
|
||||
return r;
|
||||
else
|
||||
return inverse(r);
|
||||
}
|
||||
|
||||
template<typename Result, int UnitExpNum, int UnitExpDen, typename UnitRatio>
|
||||
struct ratio_op {
|
||||
using calc_ratio =
|
||||
conditional<(UnitExpNum * UnitExpDen > 0), ratio_multiply<Result, UnitRatio>, ratio_divide<Result, UnitRatio>>;
|
||||
static constexpr int value = (UnitExpNum * UnitExpDen > 0) ? (UnitExpNum - UnitExpDen) : (UnitExpNum + UnitExpDen);
|
||||
using ratio = ratio_op<calc_ratio, value, UnitExpDen, UnitRatio>::ratio;
|
||||
};
|
||||
|
||||
template<typename ExpList, Unit... Us>
|
||||
struct derived_ratio;
|
||||
|
||||
template<Unit... Us>
|
||||
struct derived_ratio<exp_list<>, Us...> {
|
||||
using ratio = ::units::ratio<1>;
|
||||
};
|
||||
|
||||
template<typename E, typename... ERest, Unit U, Unit... URest>
|
||||
struct derived_ratio<exp_list<E, ERest...>, U, URest...> {
|
||||
using rest_ratio = derived_ratio<exp_list<ERest...>, URest...>::ratio;
|
||||
using unit_ratio = ratio_op<rest_ratio, E::num, E::den, typename U::ratio>::ratio;
|
||||
using ratio = ratio_divide<unit_ratio, typename dimension_unit<typename E::dimension>::ratio>;
|
||||
};
|
||||
template<Unit... Us, typename... Es>
|
||||
constexpr ratio derived_ratio(exp_list<Es...>)
|
||||
{
|
||||
return (... * inverse_if_negative<Es>(pow<detail::abs(Es::num)>(Us::ratio) / dimension_unit<typename Es::dimension>::ratio));
|
||||
}
|
||||
|
||||
template<DerivedDimension D, Unit... Us>
|
||||
using deduced_unit =
|
||||
scaled_unit<typename detail::derived_ratio<typename D::recipe, Us...>::ratio, typename D::coherent_unit::reference>;
|
||||
using deduced_unit = scaled_unit<derived_ratio<Us...>(typename D::recipe()), typename D::coherent_unit::reference>;
|
||||
|
||||
} // namespace units::detail
|
||||
|
||||
@@ -66,14 +66,14 @@ inline constexpr bool equivalent_dim = detail::equivalent_dim_impl<D1, D2>::valu
|
||||
*
|
||||
* Sometimes a temporary partial result of a complex calculation may not result in a predefined
|
||||
* dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit`
|
||||
* and ratio<1>.
|
||||
* and ratio(1).
|
||||
*
|
||||
* @tparam E the list of exponents of ingredient dimensions
|
||||
* @tparam ERest the list of exponents of ingredient dimensions
|
||||
*/
|
||||
template<Exponent E, Exponent... ERest>
|
||||
struct unknown_dimension : derived_dimension<unknown_dimension<E, ERest...>, scaled_unit<ratio<1>, unknown_coherent_unit>, E, ERest...> {
|
||||
using coherent_unit = scaled_unit<ratio<1>, unknown_coherent_unit>;
|
||||
struct unknown_dimension : derived_dimension<unknown_dimension<E, ERest...>, scaled_unit<ratio(1), unknown_coherent_unit>, E, ERest...> {
|
||||
using coherent_unit = scaled_unit<ratio(1), unknown_coherent_unit>;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/external/hacks.h>
|
||||
#include <units/concepts.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
@@ -46,7 +45,7 @@ template<typename T>
|
||||
|
||||
// Computes (a * b) mod m relies on unsigned integer arithmetic, should not
|
||||
// overflow
|
||||
constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m)
|
||||
[[nodiscard]] constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m)
|
||||
{
|
||||
std::uint64_t res = 0;
|
||||
|
||||
@@ -78,7 +77,7 @@ constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m
|
||||
}
|
||||
|
||||
// Calculates (a ^ e) mod m , should not overflow.
|
||||
constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m)
|
||||
[[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;
|
||||
@@ -94,7 +93,7 @@ constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m
|
||||
}
|
||||
|
||||
// gcd(a * 10 ^ e, b), should not overflow
|
||||
constexpr std::intmax_t gcdpow(std::intmax_t a, std::intmax_t e, std::intmax_t b) noexcept
|
||||
[[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);
|
||||
@@ -120,8 +119,8 @@ constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs)
|
||||
}
|
||||
|
||||
// Computes the rational gcd of n1/d1 x 10^e1 and n2/d2 x 10^e2
|
||||
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
|
||||
[[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
|
||||
{
|
||||
// Short cut for equal ratios
|
||||
if (n1 == n2 && d1 == d2 && e1 == e2) {
|
||||
@@ -152,8 +151,14 @@ constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, st
|
||||
return std::array{num / gcd, den / gcd, exp};
|
||||
}
|
||||
|
||||
constexpr auto normalize(std::intmax_t num, std::intmax_t den, std::intmax_t exp)
|
||||
constexpr void normalize(std::intmax_t& num, std::intmax_t& den, std::intmax_t& exp)
|
||||
{
|
||||
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;
|
||||
@@ -166,8 +171,23 @@ constexpr auto normalize(std::intmax_t num, std::intmax_t den, std::intmax_t exp
|
||||
den /= 10;
|
||||
--exp;
|
||||
}
|
||||
}
|
||||
|
||||
return std::array{num, den, exp};
|
||||
[[nodiscard]] static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs)
|
||||
{
|
||||
constexpr std::intmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4);
|
||||
|
||||
const std::intmax_t a0 = detail::abs(lhs) % c;
|
||||
const std::intmax_t a1 = detail::abs(lhs) / c;
|
||||
const std::intmax_t b0 = detail::abs(rhs) % c;
|
||||
const std::intmax_t b1 = detail::abs(rhs) / c;
|
||||
|
||||
Expects(a1 == 0 || b1 == 0); // overflow in multiplication
|
||||
Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication
|
||||
Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication
|
||||
Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication
|
||||
|
||||
return lhs * rhs;
|
||||
}
|
||||
|
||||
} // namespace units::detail
|
||||
|
||||
@@ -32,31 +32,31 @@ namespace units::detail {
|
||||
|
||||
inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10");
|
||||
|
||||
template<typename Ratio>
|
||||
template<ratio R>
|
||||
constexpr auto ratio_text()
|
||||
{
|
||||
if constexpr(Ratio::num == 1 && Ratio::den == 1 && Ratio::exp != 0) {
|
||||
return base_multiplier + superscript<Ratio::exp>() + basic_fixed_string(" ");
|
||||
if constexpr(R.num == 1 && R.den == 1 && R.exp != 0) {
|
||||
return base_multiplier + superscript<R.exp>() + basic_fixed_string(" ");
|
||||
}
|
||||
else if constexpr(Ratio::num != 1 || Ratio::den != 1 || Ratio::exp != 0) {
|
||||
auto txt = basic_fixed_string("[") + regular<Ratio::num>();
|
||||
if constexpr(Ratio::den == 1) {
|
||||
if constexpr(Ratio::exp == 0) {
|
||||
else if constexpr(R.num != 1 || R.den != 1 || R.exp != 0) {
|
||||
auto txt = basic_fixed_string("[") + regular<R.num>();
|
||||
if constexpr(R.den == 1) {
|
||||
if constexpr(R.exp == 0) {
|
||||
return txt + basic_fixed_string("] ");
|
||||
}
|
||||
else {
|
||||
return txt + " " + base_multiplier + superscript<Ratio::exp>() +
|
||||
return txt + " " + base_multiplier + superscript<R.exp>() +
|
||||
basic_fixed_string("] ");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if constexpr(Ratio::exp == 0) {
|
||||
return txt + basic_fixed_string("/") + regular<Ratio::den>() +
|
||||
if constexpr(R.exp == 0) {
|
||||
return txt + basic_fixed_string("/") + regular<R.den>() +
|
||||
basic_fixed_string("] ");
|
||||
}
|
||||
else {
|
||||
return txt + basic_fixed_string("/") + regular<Ratio::den>() +
|
||||
" " + base_multiplier + superscript<Ratio::exp>() +
|
||||
return txt + basic_fixed_string("/") + regular<R.den>() +
|
||||
" " + base_multiplier + superscript<R.exp>() +
|
||||
basic_fixed_string("] ");
|
||||
}
|
||||
}
|
||||
@@ -66,30 +66,30 @@ constexpr auto ratio_text()
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ratio, typename PrefixFamily>
|
||||
template<ratio R, typename PrefixFamily>
|
||||
constexpr auto prefix_or_ratio_text()
|
||||
{
|
||||
if constexpr(Ratio::num == 1 && Ratio::den == 1 && Ratio::exp == 0) {
|
||||
if constexpr(R.num == 1 && R.den == 1 && R.exp == 0) {
|
||||
// no ratio/prefix
|
||||
return basic_fixed_string("");
|
||||
}
|
||||
else {
|
||||
if constexpr (!std::is_same_v<PrefixFamily, no_prefix>) {
|
||||
// try to form a prefix
|
||||
using prefix = downcast<detail::prefix_base<PrefixFamily, Ratio>>;
|
||||
using prefix = downcast<detail::prefix_base<PrefixFamily, R>>;
|
||||
|
||||
if constexpr(!std::is_same_v<prefix, prefix_base<PrefixFamily, Ratio>>) {
|
||||
if constexpr(!std::is_same_v<prefix, prefix_base<PrefixFamily, R>>) {
|
||||
// print as a prefixed unit
|
||||
return prefix::symbol;
|
||||
}
|
||||
else {
|
||||
// print as a ratio of the coherent unit
|
||||
return ratio_text<Ratio>();
|
||||
return ratio_text<R>();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// print as a ratio of the coherent unit
|
||||
return ratio_text<Ratio>();
|
||||
return ratio_text<R>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,8 +150,7 @@ constexpr auto unit_text()
|
||||
else {
|
||||
// print as a prefix or ratio of a coherent unit
|
||||
using coherent_unit = dimension_unit<Dim>;
|
||||
using ratio = ratio_divide<typename U::ratio, typename coherent_unit::ratio>;
|
||||
auto prefix_txt = prefix_or_ratio_text<ratio, typename U::reference::prefix_family>();
|
||||
auto prefix_txt = prefix_or_ratio_text<U::ratio / coherent_unit::ratio, typename U::reference::prefix_family>();
|
||||
|
||||
if constexpr(has_symbol<coherent_unit>) {
|
||||
// use predefined coherent unit symbol
|
||||
|
||||
Reference in New Issue
Block a user