/* @file native_bpu.hpp */ #pragma once #include "native_bpu_concept.hpp" #include "basis_unit.hpp" #include namespace xo { namespace unit { // ----- native_bpu ----- /** @class native_bpu @brief represent product of a compile-time scale-factor with a rational power of a native unit Example: native_bpu, ratio<-1,2>> represents unit of 1/sqrt(t) **/ template< dim DimId, typename InnerScale, typename Power = std::ratio<1> > struct bpu : basis_unit, InnerScale> { static_assert(ratio_concept); /* native_unit provides * - scalefactor_type --> std::ratio * - c_native_dim :: dim * - c_native_unit :: native_unit */ using power_type = Power; static const int c_num = Power::num; static const int c_den = Power::den; }; /** @class bpu_assemble_abbrev * * @brief generate abbreviation literal. * * Abbreviation literal ignores outer scale factor; * (outer scale factor should be multiplied by run-time scale when printing a quantity) * * Separate template from native_bpu so that abbrev can independently be specialized **/ template < dim dim_id, typename InnerScale, typename Power = std::ratio<1> > constexpr auto bpu_assemble_abbrev_helper() { static_assert(ratio_concept); return stringliteral_concat(units::scaled_native_unit_abbrev_v.value_, stringliteral_from_exponent().value_); }; /** Expect: * - BPU is a native_bpu type: * - BPU::scalefactor_type = std::ratio<..> * - BPU::c_native_dim :: dim * - BPU::power_type = std::ratio<..> * - BPU::c_num :: int * - BPU::c_den :: int **/ template < typename BPU > constexpr auto bpu_assemble_abbrev() { static_assert(native_bpu_concept); return bpu_assemble_abbrev_helper< BPU::c_native_dim, typename BPU::scalefactor_type, typename BPU::power_type >(); }; // ----- bpu_rescale ----- /** * Part I * ------ * We have B satisfying native_bpu_concept: * B represents a basis-power-unit * p * (b.u) * * with * b = B::scalefactor_type, e.g. 60 for a 1-minute unit * u = B::dim, e.g. 1second for time * p = B::power_type * * We want to construct something with similar form: * * p * a'.(b'.u) * * representing the same dimensioned unit, * i.e. * p p' * (b.u) = a'.(b'.u) * * with NewInnerScale -> b' * * p p p p * (b.u) = (b/b') . (b'.u) = a'.(b'.u) * * p * with a' = (b/b') * * For example: if we have B(b=60,u=time,p=2), NewInnerScale=1: * then we want a'=3600, B'(b=1,u=time,p=2) * * Result represented with * bpu_rescale::outer_scalefactor_type -> 'a * bpu_rescale::native_bpu_type -> B' * * Part II * ------- * Want ability to rescale when p is a non-integer rational. * In that case a' = (b/b')^p won't in general be exactly-representable, * so we are forced to accept some loss of precision. * * Want to write: * p as p' + q' with: * p' = integer part of p * q' = fractional part of p * Then we can write * a' as c'.d' with: * c' = (b/b')^p' [exactly represented] * d' = (b/b')^q' [floating point] **/ template struct bpu_rescale { static_assert(native_bpu_concept); static_assert(ratio_concept); /* TODO: * - native_unit::c_scale -> std::ratio, call it c_inner_scalefactor * - ++ native_bpu::c_outer_scalefactor, will be a std::ratio */ /* b/b' */ using _t1_type = std::ratio_divide < typename B::scalefactor_type, NewInnerScale >; /* p' */ using p1_type = ratio_floor_t; /* q' */ using q1_type = ratio_frac_t; /** require p must be integral **/ static_assert(p1_type::den == 1); /* note: constexpr from c++26, but already present in earlier gcc */ static constexpr double c_outer_scalefactor_inexact = ::pow(from_ratio(), from_ratio()); /** p * a' = (b/b') **/ using outer_scalefactor_type = ratio_power_t< _t1_type, p1_type::num >; /** * p * (b'.u) **/ using native_bpu_type = bpu < B::c_native_dim, NewInnerScale, typename B::power_type >; }; // ----- bpu_invert ----- /** invert a native bpu: create type for space 1/B **/ template struct bpu_invert { using type = bpu < B::c_native_dim, typename B::scalefactor_type, std::ratio_multiply, typename B::power_type> >; }; // ----- bpu_product ----- /** Suppose we have two native_bpu's {B1, B2} that scale the same native basis unit u. * B1,B2 may be using different units {b1,b2} for u * * p1 * B1 (b1, u, p1) = (b1.u) * * p2 * B2 (b2, u, p2) = (b2.u) * * we want a representation in similar form: * * p' * a' . B' (b', u, p') = a'.(b'.u) * * for the product (B1 x B2), i.e. * * p' p1 p2 * a'.(b'.u) = (b1.u) (b2.u) * * We can use bpu_rescale to rewrite B2 in the form * * p2 * B2' = (c'.d').(b1.u) * * where c' is exact, d' is inexact. * (note however d' will be exactly 1.0 whenever p2 is integral) * * so we have * * p1 p2 * (B1 x B2) = (b1.u) (c'.d').(b1.u) * * p1+p2 * = (c'.d').(b1.u) * **/ template < typename B1, typename B2 > struct bpu_product { static_assert(native_bpu_concept); static_assert(native_bpu_concept); static_assert(B1::c_native_dim == B2::c_native_dim); /* c'.d'.B2' = c'.d'.(b1.u)^p2 * * _b2p_rescaled_type::native_bpu_type -> B2' (b1, u, p2) [same basis scalefactor as B1] * _b2p_rescaled_type::outer_scalefactor_type -> c' [exact factor] * _b2p_rescaled_type::c_outer_scalefactor_type -> d' [inexact factor, from fractional powers] */ using _b2p_rescaled_type = bpu_rescale; /* (b1.u)^p2 */ using _b2p_sf_bpu_type = _b2p_rescaled_type::native_bpu_type; /* p1+p2 */ using _p_type = std::ratio_add< typename B1::power_type, typename B2::power_type >; /* c' */ using outer_scalefactor_type = _b2p_rescaled_type::outer_scalefactor_type; /* d' */ static constexpr double c_outer_scalefactor_inexact = _b2p_rescaled_type::c_outer_scalefactor_inexact; /* (b1.u)^(p1+p2) */ using native_bpu_type = bpu < B1::c_native_dim, typename B1::scalefactor_type, _p_type /*Power*/ >; }; } /*namespace unit*/ } /*namespace xo*/ /* end native_bpu.hpp */