NTTP ratio support added (resolves #49)

This commit is contained in:
Mateusz Pusz
2020-06-27 19:15:46 +02:00
parent 03facbefd2
commit d171f5451e
47 changed files with 504 additions and 664 deletions
+13 -22
View File
@@ -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
+5 -5
View File
@@ -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>
+14 -30
View File
@@ -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
+3 -3
View File
@@ -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 {
+28 -8
View File
@@ -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
+19 -20
View File
@@ -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