xo-tokenizer2/include/xo/unit/native_bpu.hpp

259 lines
8.9 KiB
C++

/* @file native_bpu.hpp */
#pragma once
#include "native_bpu_concept.hpp"
#include "basis_unit.hpp"
#include <ratio>
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<universal::time, ratio<1>, ratio<-1,2>> represents unit of 1/sqrt(t)
**/
template<
dim DimId,
typename InnerScale,
typename Power = std::ratio<1> >
struct bpu : basis_unit<DimId, native_unit_for_v<DimId>, InnerScale> {
static_assert(ratio_concept<Power>);
/* 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<Power>);
return stringliteral_concat(units::scaled_native_unit_abbrev_v<dim_id, InnerScale>.value_,
stringliteral_from_exponent<Power>().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<BPU>);
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<B,NewInnerScale>::outer_scalefactor_type -> 'a
* bpu_rescale<B,NewInnerScale>::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 <typename B,
typename NewInnerScale>
struct bpu_rescale {
static_assert(native_bpu_concept<B>);
static_assert(ratio_concept<NewInnerScale>);
/* 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<typename B::power_type>;
/* q' */
using q1_type = ratio_frac_t<typename B::power_type>;
/** 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<double, _t1_type>(),
from_ratio<double, q1_type>());
/** 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 <typename B>
struct bpu_invert {
using type = bpu <
B::c_native_dim,
typename B::scalefactor_type,
std::ratio_multiply<std::ratio<-1>, 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<B1>);
static_assert(native_bpu_concept<B2>);
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<B2,
typename B1::scalefactor_type>;
/* (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 */