Skip to content

Commit

Permalink
xo-unit: + constexpr implementation (runtime+compiletime)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rconybea committed Apr 22, 2024
1 parent 3643f6a commit 5bde1bf
Show file tree
Hide file tree
Showing 12 changed files with 820 additions and 0 deletions.
94 changes: 94 additions & 0 deletions include/xo/unit/Quantity2.hpp
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 **/
29 changes: 29 additions & 0 deletions include/xo/unit/Quantity2_iostream.hpp
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 **/
162 changes: 162 additions & 0 deletions include/xo/unit/bpu_store.hpp
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 **/
21 changes: 21 additions & 0 deletions include/xo/unit/dim_iostream.hpp
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 **/
29 changes: 29 additions & 0 deletions include/xo/unit/native_bpu2_iostream.hpp
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 **/
Loading

0 comments on commit 5bde1bf

Please sign in to comment.