xo-unit: + constexpr implementation (runtime+compiletime)
This commit is contained in:
parent
3643f6a0a0
commit
5bde1bfb94
12 changed files with 820 additions and 0 deletions
94
include/xo/unit/Quantity2.hpp
Normal file
94
include/xo/unit/Quantity2.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/** @file Quantity2.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "quantity2_concept.hpp"
|
||||
#include "scaled_unit2.hpp"
|
||||
#include "unit2.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
/** @class quantity
|
||||
* @brief represent a scalar quantity with attached units. enforce dimensional consistency.
|
||||
*
|
||||
* Constexpr implementation, but units are explicitly represented:
|
||||
* sizeof(Quantity2) > sizeof(Repr)
|
||||
*
|
||||
* Explicit unit representation allows introducing units at runtime,
|
||||
* for example in python bindings
|
||||
*
|
||||
* Require:
|
||||
* - Repr supports numeric operations (+, -, *, /)
|
||||
* - Repr supports conversion from double.
|
||||
**/
|
||||
template <typename Repr = double,
|
||||
typename Int = std::int64_t>
|
||||
class Quantity2 {
|
||||
public:
|
||||
using repr_type = Repr;
|
||||
using unit_type = unit2<Int>;
|
||||
using ratio_int_type = Int;
|
||||
|
||||
public:
|
||||
constexpr Quantity2(Repr scale, const unit2<Int> & unit)
|
||||
: scale_{scale}, unit_{unit} {}
|
||||
|
||||
constexpr const repr_type & scale() const { return scale_; }
|
||||
constexpr const unit_type & unit() const { return unit_; }
|
||||
|
||||
constexpr Quantity2 unit_qty() const { return Quantity2(1, unit_); }
|
||||
|
||||
constexpr Quantity2 reciprocal() const { return Quantity2(1.0 / scale_, unit_.reciprocal()); }
|
||||
|
||||
template <typename OtherQuantity>
|
||||
static constexpr
|
||||
auto multiply(const Quantity2 & x, const OtherQuantity & y) {
|
||||
using r_repr_type = std::common_type_t<typename Quantity2::repr_type,
|
||||
typename OtherQuantity::repr_type>;
|
||||
using r_int_type = std::common_type_t<typename Quantity2::ratio_int_type,
|
||||
typename OtherQuantity::ratio_int_type>;
|
||||
|
||||
auto rr = detail::nu_product(x.unit(), y.unit());
|
||||
|
||||
r_repr_type r_scale = (::sqrt(rr.outer_scale_sq_)
|
||||
* rr.outer_scale_exact_.template to<r_repr_type>()
|
||||
* static_cast<r_repr_type>(x.scale())
|
||||
* static_cast<r_repr_type>(y.scale()));
|
||||
|
||||
return Quantity2<r_repr_type, r_int_type>(r_scale,
|
||||
rr.natural_unit_);
|
||||
}
|
||||
|
||||
private:
|
||||
/** @brief quantity represents this multiple of a unit amount **/
|
||||
Repr scale_ = Repr{};
|
||||
/** @brief unit for this quantity **/
|
||||
unit2<Int> unit_;
|
||||
}; /*Quantity2*/
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Repr = double,
|
||||
typename Int = std::int64_t>
|
||||
inline constexpr Quantity2<Repr, Int>
|
||||
unit_qty(const scaled_unit2<Int> & u) {
|
||||
return Quantity2<Repr, Int>(u.outer_scale_exact_.template to<double>() * ::sqrt(u.outer_scale_sq_),
|
||||
u.natural_unit_);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity, typename OtherQuantity>
|
||||
constexpr auto
|
||||
operator* (const Quantity & x, const OtherQuantity & y)
|
||||
{
|
||||
return Quantity::multiply(x, y);
|
||||
}
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end Quantity2.hpp **/
|
||||
29
include/xo/unit/Quantity2_iostream.hpp
Normal file
29
include/xo/unit/Quantity2_iostream.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/** @file Quantity2_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Quantity2.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
template <typename Repr = double,
|
||||
typename Int = std::int64_t>
|
||||
inline std::ostream &
|
||||
operator<< (std::ostream & os,
|
||||
const Quantity2<Repr, Int> & x)
|
||||
{
|
||||
os << "<qty"
|
||||
<< xtag("scale", x.scale())
|
||||
<< xtag("unit", x.unit())
|
||||
<< ">";
|
||||
|
||||
return os;
|
||||
}
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end Quantity2_iostream.hpp **/
|
||||
162
include/xo/unit/bpu_store.hpp
Normal file
162
include/xo/unit/bpu_store.hpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/** @file bpu_store.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "native_bpu2.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
/** @class basis_unit2_store
|
||||
* @brief Store known basis units for runtime
|
||||
**/
|
||||
template <typename Tag>
|
||||
struct basis_unit2_store {
|
||||
basis_unit2_store() : bu_abbrev_vv_(static_cast<std::size_t>(dim::n_dim)) {
|
||||
this->bu_establish_abbrev_for<dim::mass, 1, 1000000000>();
|
||||
this->bu_establish_abbrev_for<dim::mass, 1, 1000000>();
|
||||
this->bu_establish_abbrev_for<dim::mass, 1, 1000>();
|
||||
this->bu_establish_abbrev_for<dim::mass, 1, 1>();
|
||||
this->bu_establish_abbrev_for<dim::mass, 1000, 1>();
|
||||
this->bu_establish_abbrev_for<dim::mass, 1000000, 1>();
|
||||
this->bu_establish_abbrev_for<dim::mass, 1000000000, 1>();
|
||||
|
||||
this->bu_establish_abbrev_for<dim::distance, 1, 1000000000>();
|
||||
this->bu_establish_abbrev_for<dim::distance, 1, 1000000>();
|
||||
this->bu_establish_abbrev_for<dim::distance, 1, 1000>();
|
||||
this->bu_establish_abbrev_for<dim::distance, 1, 1>();
|
||||
this->bu_establish_abbrev_for<dim::distance, 1000, 1>();
|
||||
|
||||
this->bu_establish_abbrev_for<dim::time, 1, 1000000000>();
|
||||
this->bu_establish_abbrev_for<dim::time, 1, 1000000>();
|
||||
this->bu_establish_abbrev_for<dim::time, 1, 1000>();
|
||||
this->bu_establish_abbrev_for<dim::time, 1, 1>();
|
||||
this->bu_establish_abbrev_for<dim::time, 60, 1>();
|
||||
this->bu_establish_abbrev_for<dim::time, 3600, 1>();
|
||||
this->bu_establish_abbrev_for<dim::time, 24*3600, 1>();
|
||||
this->bu_establish_abbrev_for<dim::time, 250*24*3600, 1>();
|
||||
this->bu_establish_abbrev_for<dim::time, 360*24*3600, 1>();
|
||||
this->bu_establish_abbrev_for<dim::time, 365*24*3600, 1>();
|
||||
|
||||
this->bu_establish_abbrev_for<dim::currency, 1, 1>();
|
||||
|
||||
this->bu_establish_abbrev_for<dim::price, 1, 1>();
|
||||
}
|
||||
|
||||
/* e.g.
|
||||
* [(1/1000000000, "nm"), (1/1000000, "um"), (1/1000, "mm"), (1/1, "m"), (1000/1, "km")]
|
||||
*/
|
||||
using native_scale_v = std::vector<std::pair<scalefactor_ratio_type, basis_unit2_abbrev_type>>;
|
||||
|
||||
/** @brief get basis-unit abbreviation at runtime **/
|
||||
basis_unit2_abbrev_type bu_abbrev(dim basis_dim,
|
||||
const scalefactor_ratio_type & scalefactor) const
|
||||
{
|
||||
const auto & bu_abbrev_v = bu_abbrev_vv_[static_cast<std::size_t>(basis_dim)];
|
||||
|
||||
std::size_t i_abbrev = bu_abbrev_lub_ix(basis_dim, scalefactor, bu_abbrev_v);
|
||||
|
||||
if ((i_abbrev < bu_abbrev_v.size())
|
||||
&& (bu_abbrev_v[i_abbrev].first == scalefactor))
|
||||
{
|
||||
return bu_abbrev_v[i_abbrev].second;
|
||||
} else {
|
||||
return units::bu_fallback_abbrev(basis_dim, scalefactor);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief get basis-power-unit abbreviation at runtime **/
|
||||
bpu2_abbrev_type bpu_abbrev(dim basis_dim,
|
||||
const scalefactor_ratio_type & scalefactor,
|
||||
const power_ratio_type & power)
|
||||
{
|
||||
return abbrev::bpu2_abbrev(basis_dim,
|
||||
scalefactor,
|
||||
power);
|
||||
}
|
||||
|
||||
template <dim BasisDim, std::int64_t InnerScaleNum, std::int64_t InnerScaleDen>
|
||||
void bu_establish_abbrev_for() {
|
||||
this->bu_establish_abbrev
|
||||
(basis_unit2(BasisDim,
|
||||
scalefactor_ratio_type(InnerScaleNum, InnerScaleDen)),
|
||||
units::scaled_native_unit2_abbrev_v<BasisDim, InnerScaleNum, InnerScaleDen>);
|
||||
}
|
||||
|
||||
/** @brief establish abbreviation @p abbrev for basis unit @p bu
|
||||
**/
|
||||
void bu_establish_abbrev(const basis_unit2 & bu,
|
||||
const basis_unit2_abbrev_type & abbrev) {
|
||||
|
||||
auto & bu_abbrev_v = bu_abbrev_vv_[static_cast<std::size_t>(bu.native_dim())];
|
||||
|
||||
std::int32_t i_abbrev = 0;
|
||||
|
||||
if (!bu_abbrev_v.empty()) {
|
||||
i_abbrev = bu_abbrev_lub_ix(bu.native_dim(),
|
||||
bu.scalefactor(),
|
||||
bu_abbrev_v);
|
||||
}
|
||||
|
||||
auto entry = std::make_pair(bu.scalefactor(), abbrev);
|
||||
|
||||
if ((i_abbrev < bu_abbrev_v.size())
|
||||
&& (bu_abbrev_v[i_abbrev].first == bu.scalefactor()))
|
||||
{
|
||||
bu_abbrev_v[i_abbrev] = entry;
|
||||
} else {
|
||||
bu_abbrev_v.insert(bu_abbrev_v.begin() + i_abbrev, entry);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/** @brief get least-upper-bound index position in bu_abbrev_v[]
|
||||
*
|
||||
* return value in [0, n] where n = bu_abbrev_v.size()
|
||||
**/
|
||||
static std::size_t bu_abbrev_lub_ix(dim basis_dim,
|
||||
const scalefactor_ratio_type & scalefactor,
|
||||
const native_scale_v & bu_abbrev_v)
|
||||
{
|
||||
std::size_t n = bu_abbrev_v.size();
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
std::size_t lo = 0;
|
||||
std::size_t hi = n-1;
|
||||
|
||||
if (scalefactor <= bu_abbrev_v[lo].first)
|
||||
return 0;
|
||||
|
||||
auto cmp = (scalefactor <=> bu_abbrev_v[hi].first);
|
||||
|
||||
if (cmp > 0)
|
||||
return n;
|
||||
|
||||
if (cmp == 0)
|
||||
return hi;
|
||||
|
||||
while (hi-lo > 1) {
|
||||
/* inv:
|
||||
* bu_abbrev_v[lo].first < scalefactor <= bu_abbrev_v[hi].first
|
||||
*/
|
||||
|
||||
std::size_t mid = lo + (hi - lo)/2;
|
||||
|
||||
if (scalefactor > bu_abbrev_v[mid].first)
|
||||
lo = mid;
|
||||
else
|
||||
hi = mid;
|
||||
}
|
||||
|
||||
return hi;
|
||||
}
|
||||
|
||||
private:
|
||||
/* bu_abbrev_v[dim] holds known units for native unit dim */
|
||||
std::vector<native_scale_v> bu_abbrev_vv_;
|
||||
};
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end bpu_store.hpp **/
|
||||
21
include/xo/unit/dim_iostream.hpp
Normal file
21
include/xo/unit/dim_iostream.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/** @file dim_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dim_util.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, dim x) {
|
||||
os << dim2str(x);
|
||||
return os;
|
||||
}
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end dim_iostream.hpp **/
|
||||
29
include/xo/unit/native_bpu2_iostream.hpp
Normal file
29
include/xo/unit/native_bpu2_iostream.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/** @file native_bpu2_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/ratio/ratio_iostream.hpp"
|
||||
#include "native_bpu2.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
template <typename Int>
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, const bpu2<Int> & x) {
|
||||
os << "<bpu"
|
||||
<< xtag("dim", x.native_dim())
|
||||
<< xtag("mult", x.scalefactor())
|
||||
<< xtag("pwr", x.power())
|
||||
<< ">";
|
||||
|
||||
return os;
|
||||
}
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end native_bpu2_iostream.hpp **/
|
||||
229
include/xo/unit/natural_unit.hpp
Normal file
229
include/xo/unit/natural_unit.hpp
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/** @file natural_unit.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "native_bpu2.hpp"
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
/** @class natural_unit
|
||||
* @brief an array representing the cartesian product of distinct basis-power-units
|
||||
*
|
||||
* 1. Each bpu in the array represents a power of a basis dimension, e.g. "meter" or "second^2".
|
||||
* 2. Each bpu in an array has a different dimension id.
|
||||
* For example dim::time, if present, appears once.
|
||||
* 3. Basis dimensions can appear in any order.
|
||||
* Order used for constructing abbreviations: will get @c "kg.m" or @c "m.kg"
|
||||
* depending on the orderin of @c dim::distance and @c dim::mass in @c bpu_v_
|
||||
**/
|
||||
template <typename Int>
|
||||
class natural_unit {
|
||||
public:
|
||||
using ratio_int_type = Int;
|
||||
|
||||
public:
|
||||
constexpr natural_unit() : n_bpu_{0} {}
|
||||
|
||||
constexpr std::size_t n_bpu() const { return n_bpu_; }
|
||||
constexpr bpu2<Int> * bpu_v() const { return bpu_v_; }
|
||||
|
||||
constexpr void push_back(const bpu2<Int> & bpu) {
|
||||
if (n_bpu_ < n_dim)
|
||||
bpu_v_[n_bpu_++] = bpu;
|
||||
}
|
||||
|
||||
constexpr bpu2<Int> & operator[](std::size_t i) { return bpu_v_[i]; }
|
||||
constexpr const bpu2<Int> & operator[](std::size_t i) const { return bpu_v_[i]; }
|
||||
|
||||
private:
|
||||
/** @brief the number of occupied slots in @c bpu_v_ **/
|
||||
std::size_t n_bpu_;
|
||||
|
||||
/** @brief storage for basis power units **/
|
||||
bpu2<Int> bpu_v_[n_dim];
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Int, typename... Ts>
|
||||
constexpr void
|
||||
push_bpu_array(natural_unit<Int> * p_target, Ts... args);
|
||||
|
||||
template <typename Int>
|
||||
constexpr void
|
||||
push_bpu_array(natural_unit<Int> * p_target) {}
|
||||
|
||||
template <typename Int, typename T0, typename... Ts>
|
||||
constexpr void
|
||||
push_bpu_array(natural_unit<Int> * p_target, T0 && bpu0, Ts... args) {
|
||||
p_target->push_back(bpu0);
|
||||
push_bpu_array(p_target, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
struct bpu_array_maker {
|
||||
template <typename... Ts>
|
||||
static constexpr natural_unit<Int>
|
||||
make_bpu_array(Ts... args) {
|
||||
natural_unit<Int> bpu_array;
|
||||
detail::push_bpu_array(&bpu_array, args...);
|
||||
return bpu_array;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
/**
|
||||
* Given bpu ~ (b.u)^p:
|
||||
* - b = bpu.scalefactor
|
||||
* - u = bpu.native_dim
|
||||
* - p = bpu.power
|
||||
*
|
||||
* want to rewrite in the form a'.(b'.u)^p
|
||||
*
|
||||
* Can compute a' exactly iff p is integral.
|
||||
* In that case:
|
||||
* (b.u)^p = ((b/b').b'.u)^p
|
||||
* = (b/b')^p.(b'.u)^p
|
||||
* = a'.(b'.u)^p with a' = (b/b')^p
|
||||
*
|
||||
* Can write p = p0 + q, with p0 = floor(p) integral, q = frac(p) in [0,1)
|
||||
*
|
||||
* Then
|
||||
* (b/b')^p = (b/b')^p0 * (b/b')^q
|
||||
*
|
||||
* we'll compute:
|
||||
* - (b/b')^p0 exactly (as a ratio)
|
||||
* - (b/b')^q inexactly (as a double)
|
||||
**/
|
||||
|
||||
template <typename Int>
|
||||
struct outer_scalefactor_result {
|
||||
constexpr outer_scalefactor_result(const ratio::ratio<Int> & outer_scale_exact,
|
||||
double outer_scale_sq)
|
||||
: outer_scale_exact_{outer_scale_exact},
|
||||
outer_scale_sq_{outer_scale_sq} {}
|
||||
|
||||
/* (b/b')^p0 */
|
||||
ratio::ratio<Int> outer_scale_exact_;
|
||||
/* (b/b')^q -- until c++26 only allow q=0 or q=1/2 */
|
||||
double outer_scale_sq_;
|
||||
};
|
||||
|
||||
template <typename Int>
|
||||
struct bpu2_rescale_result {
|
||||
constexpr bpu2_rescale_result(const bpu2<Int> & bpu_rescaled,
|
||||
const ratio::ratio<Int> & outer_scale_exact,
|
||||
double outer_scale_sq)
|
||||
: bpu_rescaled_{bpu_rescaled},
|
||||
outer_scale_exact_{outer_scale_exact},
|
||||
outer_scale_sq_{outer_scale_sq}
|
||||
{}
|
||||
|
||||
/* (b'.u)^p */
|
||||
bpu2<Int> bpu_rescaled_;
|
||||
/* (b/b')^p0 */
|
||||
ratio::ratio<Int> outer_scale_exact_;
|
||||
/* [(b/b')^q]^2 -- until c++26 only allow q=0 or q=1/2 */
|
||||
double outer_scale_sq_;
|
||||
};
|
||||
|
||||
template <typename Int>
|
||||
constexpr
|
||||
bpu2_rescale_result<Int>
|
||||
bpu2_rescale(const bpu2<Int> & orig,
|
||||
const scalefactor_ratio_type & new_scalefactor)
|
||||
{
|
||||
ratio::ratio<Int> mult = (orig.scalefactor() / new_scalefactor);
|
||||
|
||||
/* inv: p_frac in [0, 1) */
|
||||
auto p_frac = orig.power().frac();
|
||||
|
||||
/* asof c++26: replace mult_sq with ::pow(mult, p_frac) */
|
||||
double mult_sq = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
if (p_frac.den() == 1) {
|
||||
mult_sq = 1.0;
|
||||
} else if(p_frac.den() == 2) {
|
||||
mult_sq = mult.template to<double>();
|
||||
} else {
|
||||
// remaining possibilities not supported until c++26
|
||||
}
|
||||
|
||||
return bpu2_rescale_result<Int>(bpu2<Int>(orig.native_dim(),
|
||||
new_scalefactor,
|
||||
orig.power()),
|
||||
mult.power(orig.power().floor()),
|
||||
mult_sq);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
constexpr
|
||||
outer_scalefactor_result<Int>
|
||||
bpu_product_inplace(bpu2<Int> * p_target_bpu,
|
||||
const bpu2<Int> & rhs_bpu_orig)
|
||||
{
|
||||
assert(rhs_bpu_orig.native_dim() == p_target_bpu->native_dim());
|
||||
|
||||
bpu2_rescale_result<Int> rhs_bpu_rr = bpu2_rescale(rhs_bpu_orig,
|
||||
p_target_bpu->scalefactor());
|
||||
|
||||
*p_target_bpu = bpu2<Int>(p_target_bpu->native_dim(),
|
||||
p_target_bpu->scalefactor(),
|
||||
p_target_bpu->power() + rhs_bpu_orig.power());
|
||||
|
||||
return outer_scalefactor_result<Int>(rhs_bpu_rr.outer_scale_exact_,
|
||||
rhs_bpu_rr.outer_scale_sq_);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
constexpr
|
||||
outer_scalefactor_result<Int>
|
||||
bpu_array_product_inplace(natural_unit<Int> * p_target,
|
||||
const bpu2<Int> & bpu)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
for (; i < p_target->n_bpu(); ++i) {
|
||||
if ((*p_target)[i].native_dim() == bpu.native_dim()) {
|
||||
outer_scalefactor_result<Int> retval = bpu_product_inplace(&((*p_target)[i]), bpu);
|
||||
|
||||
/* TODO: strip 0 power */
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/* control here: i=p_target->n_bpu() */
|
||||
p_target->push_back(bpu);
|
||||
|
||||
return outer_scalefactor_result<Int>
|
||||
(ratio::ratio<Int>(1, 1) /*outer_scale_exact*/,
|
||||
1.0 /*outer_scale_sq*/);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
constexpr natural_unit<Int>
|
||||
nu_reciprocal(const natural_unit<Int> & nu)
|
||||
{
|
||||
natural_unit<Int> retval;
|
||||
|
||||
for (std::size_t i = 0; i < nu.n_bpu(); ++i)
|
||||
retval.push_back(nu[i].reciprocal());
|
||||
|
||||
return retval;
|
||||
} /*nunit_reciprocal*/
|
||||
|
||||
} /*namespace detail*/
|
||||
|
||||
namespace nu2 {
|
||||
constexpr auto nanogram = bpu_array_maker<std::int64_t>::make_bpu_array(make_unit_power<std::int64_t>(bu2::nanogram));
|
||||
constexpr auto microgram = bpu_array_maker<std::int64_t>::make_bpu_array(make_unit_power<std::int64_t>(bu2::microgram));
|
||||
}
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end natural_unit.hpp **/
|
||||
28
include/xo/unit/natural_unit_iostream.hpp
Normal file
28
include/xo/unit/natural_unit_iostream.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/** @file natural_unit_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "natural_unit.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
template <typename Int>
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, const natural_unit<Int> & x) {
|
||||
os << "<natural-unit [";
|
||||
for (std::size_t i=0; i<x.n_bpu(); ++i) {
|
||||
if (i > 0)
|
||||
os << ", ";
|
||||
os << x[i];
|
||||
}
|
||||
os << "]>";
|
||||
return os;
|
||||
}
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end natural_unit_iostream.hpp **/
|
||||
28
include/xo/unit/quantity2.hpp
Normal file
28
include/xo/unit/quantity2.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/** @file quantity2.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bpu_array.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
/** @class quantity
|
||||
* @brief represent a scalar quantity with attached units. enforce dimensional consistency.
|
||||
*
|
||||
* Constexpr implementation, can compute units at compile time
|
||||
**/
|
||||
template <typename Repr = double, typename Int = std::int64_t>
|
||||
class quantity2 {
|
||||
public:
|
||||
using repr_type = Repr;
|
||||
|
||||
private:
|
||||
};
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end quantity2.hpp **/
|
||||
23
include/xo/unit/quantity2_concept.hpp
Normal file
23
include/xo/unit/quantity2_concept.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/** @file quantity2_concept.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "unit_concept.hpp"
|
||||
#include "numeric_concept.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
template <typename Quantity>
|
||||
concept quantity2_concept = requires(Quantity qty, typename Quantity::repr_type repr)
|
||||
{
|
||||
typename Quantity::unit_type;
|
||||
typename Quantity::repr_type;
|
||||
|
||||
{ qty.scale() } -> std::same_as<typename Quantity::repr_type>;
|
||||
//{ Quantity::unit_cstr() } -> std::same_as<char const *>;
|
||||
//{ Quantity::unit_quantity() } -> std::same_as<Quantity>;
|
||||
//{ Quantity::promote(repr) } -> std::same_as<Quantity>;
|
||||
} && (true //unit_concept<typename Quantity::unit_type>
|
||||
&& numeric_concept<typename Quantity::repr_type>);
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
126
include/xo/unit/scaled_unit2.hpp
Normal file
126
include/xo/unit/scaled_unit2.hpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/** @file scaled_unit2.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "natural_unit.hpp"
|
||||
//#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
/** @class bpu2_array_rescale_result
|
||||
* @brief Represents the product sqrt(outer_scale_sq) * outer_scale_exact * nat_unit
|
||||
**/
|
||||
template <typename Int>
|
||||
struct scaled_unit2 {
|
||||
constexpr scaled_unit2(const natural_unit<Int> & nat_unit,
|
||||
ratio::ratio<Int> outer_scale_exact,
|
||||
double outer_scale_sq)
|
||||
: natural_unit_{nat_unit},
|
||||
outer_scale_exact_{outer_scale_exact},
|
||||
outer_scale_sq_{outer_scale_sq}
|
||||
{}
|
||||
|
||||
constexpr scaled_unit2 reciprocal() const {
|
||||
return scaled_unit2(nu_reciprocal(natural_unit_,
|
||||
outer_scale_exact_.reciprocal(),
|
||||
1.0 / outer_scale_sq_));
|
||||
}
|
||||
|
||||
natural_unit<Int> natural_unit_;
|
||||
ratio::ratio<Int> outer_scale_exact_;
|
||||
double outer_scale_sq_;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Int>
|
||||
constexpr auto make_unit_rescale_result(const natural_unit<Int> & bpuv) {
|
||||
return scaled_unit2<Int>(bpuv,
|
||||
ratio::ratio<Int>(1, 1),
|
||||
1.0);
|
||||
}
|
||||
}
|
||||
|
||||
namespace su2 {
|
||||
constexpr auto nanogram = detail::make_unit_rescale_result<std::int64_t>(nu2::nanogram);
|
||||
constexpr auto microgram = detail::make_unit_rescale_result<std::int64_t>(nu2::microgram);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename Int>
|
||||
constexpr
|
||||
detail::bpu2_rescale_result<Int>
|
||||
bpu2_product(const bpu2<Int> & lhs_bpu,
|
||||
const bpu2<Int> & rhs_bpu)
|
||||
{
|
||||
assert(lhs_bpu.native_dim() == rhs_bpu.native_dim());
|
||||
|
||||
bpu2<Int> prod_bpu = lhs_bpu;
|
||||
auto rr = bpu_product_inplace(&prod_bpu, rhs_bpu);
|
||||
|
||||
return bpu2_rescale_result<Int>(prod_bpu, rr.outer_scale_exact_, rr.outer_scale_sq_);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
constexpr
|
||||
scaled_unit2<Int>
|
||||
nu_product(const natural_unit<Int> & lhs_bpu_array,
|
||||
const bpu2<Int> & rhs_bpu)
|
||||
{
|
||||
natural_unit<Int> prod = lhs_bpu_array;
|
||||
auto rr = bpu_array_product_inplace(&prod, rhs_bpu);
|
||||
|
||||
return scaled_unit2<Int>(prod,
|
||||
rr.outer_scale_exact_,
|
||||
rr.outer_scale_sq_);
|
||||
};
|
||||
|
||||
template <typename Int>
|
||||
constexpr
|
||||
scaled_unit2<Int>
|
||||
nu_product(const natural_unit<Int> & lhs_bpu_array,
|
||||
const natural_unit<Int> & rhs_bpu_array)
|
||||
{
|
||||
natural_unit<Int> prod = lhs_bpu_array;
|
||||
|
||||
/* accumulate product of scalefactors spun off by rescaling
|
||||
* any basis-units in rhs_bpu_array that conflict with the same dimension
|
||||
* in lh_bpu_array
|
||||
*/
|
||||
auto sfr = (detail::outer_scalefactor_result<Int>
|
||||
(scalefactor_ratio_type(1, 1) /*outer_scale_exact*/,
|
||||
1.0 /*outer_scale_sq*/));
|
||||
|
||||
for (std::size_t i = 0; i < rhs_bpu_array.n_bpu(); ++i) {
|
||||
auto sfr2 = bpu_array_product_inplace(&prod, rhs_bpu_array[i]);
|
||||
|
||||
sfr.outer_scale_exact_ = sfr.outer_scale_exact_ * sfr2.outer_scale_exact_;
|
||||
sfr.outer_scale_sq_ *= sfr2.outer_scale_sq_;
|
||||
}
|
||||
|
||||
return scaled_unit2<Int>(prod,
|
||||
sfr.outer_scale_exact_,
|
||||
sfr.outer_scale_sq_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
inline constexpr scaled_unit2<Int>
|
||||
operator* (const scaled_unit2<Int> & x_unit,
|
||||
const scaled_unit2<Int> & y_unit)
|
||||
{
|
||||
auto rr = detail::nu_product(x_unit.natural_unit_,
|
||||
y_unit.natural_unit_);
|
||||
|
||||
return (scaled_unit2<Int>
|
||||
(rr.natural_unit_,
|
||||
rr.outer_scale_exact_ * x_unit.outer_scale_exact_ * y_unit.outer_scale_exact_,
|
||||
rr.outer_scale_sq_ * x_unit.outer_scale_sq_ * y_unit.outer_scale_sq_));
|
||||
}
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end scaled_unit2.hpp **/
|
||||
27
include/xo/unit/scaled_unit_iostream.hpp
Normal file
27
include/xo/unit/scaled_unit_iostream.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/** @file scaled_unit_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scaled_unit2.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
template <typename Int>
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, const scaled_unit2<Int> & x) {
|
||||
os << "<scaled-unit"
|
||||
<< xtag("bpuv", x.natural_unit_)
|
||||
<< xtag("outer_scale_exact", x.outer_scale_exact_)
|
||||
<< xtag("outer_scale_sq", x.outer_scale_sq_)
|
||||
<< ">";
|
||||
|
||||
return os;
|
||||
};
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end scaled_unit_iostream.hpp **/
|
||||
24
include/xo/unit/unit2.hpp
Normal file
24
include/xo/unit/unit2.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/** @file unit2.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "natural_unit.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace unit {
|
||||
/** @class unit2
|
||||
* @brief represent an arbitrary unit along with dimension details
|
||||
*
|
||||
* For example,
|
||||
* kg.m.s^-2 or
|
||||
* (kilogram * meter) / (second * second)
|
||||
**/
|
||||
template <typename Int>
|
||||
using unit2 = natural_unit<Int>;
|
||||
} /*namespace unit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end unit2.hpp **/
|
||||
Loading…
Add table
Add a link
Reference in a new issue