-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
xo-unit: + constexpr implementation (runtime+compiletime)
- Loading branch information
Showing
12 changed files
with
820 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 **/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 **/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 **/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 **/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 **/ |
Oops, something went wrong.