xo-unit: delete obsolete mpl-based implementation

This commit is contained in:
Roland Conybeare 2024-05-24 18:07:16 -04:00
commit a920d2c396
15 changed files with 0 additions and 2899 deletions

View file

@ -1,86 +0,0 @@
/** @file basis_unit.hpp **/
#include "dim_util.hpp"
#include "ratio_util.hpp"
namespace xo {
namespace unit {
/** @class mpl_basis_unit
*
* @brief A dimensionless multiple with natively-specified (i.e. at compile-time) dimension
**/
template <dim BasisDim,
native_unit_id NativeUnitId = native_unit_for_v<BasisDim>,
typename InnerScale = std::ratio<1>>
struct basis_unit {
static_assert(ratio_concept<InnerScale>);
static constexpr dim c_native_dim = BasisDim;
static constexpr basis_unit c_native_unit = NativeUnitId;
using scalefactor_type = InnerScale;
};
/** Using struct wrapper so we can partially specialize
* Specializations in [dimension.hpp], see also
**/
template <dim dim_id>
struct native_unit_abbrev_helper;
template <>
struct native_unit_abbrev_helper<dim::mass> {
static constexpr auto value = stringliteral("g");
};
template <>
struct native_unit_abbrev_helper<dim::distance> {
static constexpr auto value = stringliteral("m");
};
template <>
struct native_unit_abbrev_helper<dim::time> {
static constexpr auto value = stringliteral("s");
};
template<>
struct native_unit_abbrev_helper<dim::currency> {
static constexpr auto value = stringliteral("ccy");
};
template<>
struct native_unit_abbrev_helper<dim::price> {
static constexpr auto value = stringliteral("px");
};
template<dim BasisDim>
constexpr auto native_unit_abbrev_v = native_unit_abbrev_helper<BasisDim>::value;
namespace units {
// ----- scaled_native_unit_abbrev_helper -----
/* Require: InnerScale is ratio type; InnerScale >= 1 */
template <dim BasisDim, typename InnerScale>
struct scaled_native_unit_abbrev;
template <dim BasisDim>
struct scaled_native_unit_abbrev<BasisDim, std::ratio<1>> {
static constexpr auto value = native_unit_abbrev_v<BasisDim>;
};
template <dim BasisDim, typename InnerScale>
struct scaled_native_unit_abbrev {
/* e.g. unit of '1000 grams' will have abbrev '1000g' in absence
* of a specialization for scaled_native_unit_abbrev
*/
static constexpr auto value = stringliteral_concat(stringliteral_from_ratio<InnerScale>().value_,
native_unit_abbrev_helper<BasisDim>::value.value_);
};
template <dim BasisDim, typename InnerScale>
constexpr auto scaled_native_unit_abbrev_v = scaled_native_unit_abbrev<BasisDim, InnerScale>::value;
}
} /*namespace unit*/
} /*namespace xo*/
/** end basis_unit.hpp **/

View file

@ -1,40 +0,0 @@
/** @file promoter.hpp
*
* Author: Roland Conybeare
**/
#pragma once
#include "../unit.hpp"
namespace xo {
namespace unit {
/** @class promoter
*
* Auxiliary class driver for quantity::promote().
* promoter has two specializations:
* 1. if Unit is dimensionless, @c promoter::promote() is the identity function.
* This has the effect of collapsing dimensionless quantities to their representation.
* 2. if Unit has at least one non-empty dimension,
* @c promoter::promote() builds an xo::unit::quantity instance
**/
template <typename Unit, typename Repr, bool Dimensionless = dimensionless_v<Unit> >
struct promoter;
template <typename Unit, typename Repr>
class quantity;
/* collapse dimensionless quantity to its repr_type> */
template <typename Unit, typename Repr>
struct promoter<Unit, Repr, /*Dimensionless*/ true> {
static constexpr Repr promote(Repr x) { return x; };
};
template <typename Unit, typename Repr>
struct promoter<Unit, Repr, /*Dimensionless*/ false> {
static constexpr quantity<Unit, Repr> promote(Repr x) { return quantity<Unit, Repr>(x); }
};
} /*namespace unit*/
} /*namespace xo*/
/** end promoter.hpp **/

View file

@ -1,82 +0,0 @@
/* @file dim_util.hpp */
#pragma once
//#include "stringliteral.hpp"
//#include "xo/flatstring/flatstring.hpp"
#include <cstdint>
namespace xo {
namespace unit {
enum class dim {
invalid = -1,
/** weight. native unit = 1 gram **/
mass,
/** distance. native unit = 1 meter **/
distance,
/** time. native unit = 1 second **/
time,
/** a currency amount. native unit depends on actual currency.
* For USD: one US dollar.
*
* NOTE: unit system isn't suitable for multicurrency work:
* (1usd + 1eur) is well-defined, but (1sec + 1m) is not.
**/
currency,
/** a screen price **/
price,
/** comes last, counts entries **/
n_dim
};
inline const char *
dim2str(dim x)
{
switch(x) {
case dim::mass: return "mass";
case dim::distance: return "distance";
case dim::time: return "time";
case dim::currency: return "currency";
case dim::price: return "price";
default: break;
}
return "?dim";
}
static constexpr std::size_t n_dim = static_cast<std::size_t>(dim::n_dim);
enum class native_unit_id {
gram,
meter,
second,
currency,
price
};
template <dim Dim>
struct native_unit_for;
template <>
struct native_unit_for<dim::mass> { static constexpr auto value = native_unit_id::gram; };
template <>
struct native_unit_for<dim::distance> { static constexpr auto value = native_unit_id::meter; };
template <>
struct native_unit_for<dim::time> { static constexpr auto value = native_unit_id::second; };
template <>
struct native_unit_for<dim::currency> { static constexpr auto value = native_unit_id::currency; };
template <>
struct native_unit_for<dim::price> { static constexpr auto value = native_unit_id::price; };
template <dim Dim>
constexpr auto native_unit_for_v = native_unit_for<Dim>::value;
} /*namespace unit*/
} /*namespace xo*/
/* end dim_util.hpp */

View file

@ -1,76 +0,0 @@
/* @file dimension_concept.hpp */
#pragma once
#include "native_bpu_concept.hpp"
namespace xo {
namespace unit {
/** checks most non-empty BPU (basis power unit) node types;
* cannot check BpuList::rest_type, because concept definition
* can't (as of c++23) be recursive.
*
* As workaround, revert to type traits, seend below.
**/
template <typename BpuList>
concept bpu_node_concept = requires(BpuList bpulist)
{
typename BpuList::front_type;
typename BpuList::rest_type;
}
&& (native_bpu_concept<typename BpuList::front_type>
&& (std::is_integral_v<decltype(BpuList::n_dimension)>)
//&& (std::same_as<typename BpuList::front_type, void>
// or nonempty_bpu_list_concept<BpuList::rest_type>))
);
namespace detail {
// ------------------------------------------------------------
/* check for 'list of native_bpu_concept'.
* Need type trait for this, to access partial specializations
*/
template < typename BpuList >
struct bpu_list_traits;
/* void (representing empty list) is fine */
template <>
struct bpu_list_traits<void> : public std::true_type {};
/* non-void must satisfy bpu-list rules */
template <typename BpuList>
struct bpu_list_traits {
/* checks everything except BpuList::rest_type */
static constexpr bool _value1 = bpu_node_concept<BpuList>;
static constexpr bool _value2 = bpu_list_traits<typename BpuList::rest_type>::value;
static constexpr bool value = (_value1 && _value2);
};
// ----------------------------------------------------------------
template <typename BpuList>
constexpr bool bpu_list_v = bpu_list_traits<BpuList>::value;
}
/* may want to rename this -> native_bpu_list */
template <typename BpuList>
concept bpu_list_concept = detail::bpu_list_v<BpuList>;
// ----------------------------------------------------------------
/* TODO: retire in favor of unit_concept? */
template <typename Dimension>
concept dimension_concept = requires(Dimension dim)
{
typename Dimension::dim_type;
typename Dimension::canon_type;
}
&& (bpu_list_concept<typename Dimension::dim_type>
&& bpu_list_concept<typename Dimension::canon_type>
);
} /*namespace unit*/
} /*namespace xo*/
/* end dimension_concept.hpp */

View file

@ -1,558 +0,0 @@
/* @file dimension_impl.hpp */
#pragma once
#include "dimension_concept.hpp"
#include "native_bpu.hpp"
#include "dim_util.hpp"
#include "ratio_util.hpp"
//#include <json/value.h>
#include <ratio>
#include <algorithm>
#include <numeric>
#include <string_view>
namespace xo {
/* TODO:
* - bpu_list -> bpu_node
*/
namespace unit {
// ----- lookup_bpu -----
/**
* Select from dimension_impl by known index value
*
* Example:
* using t1 = native_bpu<dim::currency, std::ratio<1>, std::ratio<1,1>>;
* using t2 = native_bpu<dim::time, std::ratio<60>, std::ratio<-1,2>>;
* using dim = dimension_impl<t1,t2>
*
* then
* lookup_bpu<dim,0> --> t1
* lookup_bpu<dim,1> --> t2
**/
template <typename Dim, int Index>
struct lookup_bpu {
using power_unit_type = lookup_bpu<typename Dim::rest_type, Index-1>::power_unit_type;
};
template <typename Dim>
struct lookup_bpu<Dim, 0> {
using power_unit_type = Dim::front_type;
};
// ----- di_find_bpu -----
/**
* @brief Select from dimension_impl by native_dim_id
*
* Example:
* using t1 = native_bpu<dim::time, std::ratio<60>, std::ratio<-2>>;
* using t2 = native_bpu<dim::currency, std::ratio<1>, std::ratio<1>>;
* using di = dimension_impl<t1,t2>;
*
* then
* di_find_bpu<dim::time> -> t1
* di_find_bpu<dim::currency> -> t2
* di_find_bpu<dim::mass> -> native_bpu {dim::mass, std::ratio<1>, std::ratio<0>}
**/
template <typename BpuList, dim BasisDim>
struct di_find_bpu;
/**
* @brief Aux template helper for di_find_bpu<..>
**/
template <typename Front, typename Rest, dim BasisDim, bool MatchesFront = (Front::c_native_dim == BasisDim)>
struct di_find_bpu_aux;
/** specialization for non-empty BpuList **/
template <typename BpuList, dim BasisDim>
struct di_find_bpu {
using type = di_find_bpu_aux<typename BpuList::front_type, typename BpuList::rest_type, BasisDim>::type;
};
/** specialization for empty BpuList **/
template <dim BasisDim>
struct di_find_bpu<void, BasisDim> {
using type = bpu<BasisDim, std::ratio<1>, std::ratio<0>>;
};
template <typename Front, typename Rest, dim BasisDim>
struct di_find_bpu_aux<Front, Rest, BasisDim, /*MatchesFront*/ true> {
using type = Front;
};
template <typename Front, typename Rest, dim BasisDim>
struct di_find_bpu_aux<Front, Rest, BasisDim, /*MatchesFront*/ false> {
using type = di_find_bpu<Rest, BasisDim>::type;
};
// ----------------------------------------------------------------
/**
* Promise:
* - bpu_list::front_type
* - bpu_list::rest_type
* - bpu_node_concept<bpulist<P,D>>
**/
template <typename P, typename D = void>
struct bpu_node;
// ----------------------------------------------------------------
template <typename Front,
typename Rest>
constexpr bool FrontHasZeroPower = (Front::power_type::num == 0); //std::ratio_equal_v< typename Front::power_type, std::ratio<0,1> >;
template <typename Front,
typename Rest,
bool FHZP = FrontHasZeroPower<Front, Rest>>
struct bpu_smart_cons;
template <typename Front,
typename Rest>
struct bpu_smart_cons<Front, Rest, /*FrontHasZeroPower*/ true> {
using type = Rest;
};
template <typename Front,
typename Rest>
struct bpu_smart_cons<Front, Rest, /*FrontHasZeroPower*/ false> {
using type = bpu_node<Front, Rest>;
};
template <typename Front, typename Rest>
using bpu_smart_cons_t = bpu_smart_cons<Front, Rest>::type;
// ----------------------------------------------------------------
/** @class bwp
@brief represent (compile-time) result of search in a bpu_list<> type
short for (basis-with-native-power-unit)
**/
template <int index_arg, dim basis_arg>
struct bwp {
static constexpr int c_index = index_arg;
static constexpr dim c_basis = basis_arg;
};
template <typename T>
using bwp_incr_pos_type = bwp<T::c_index + 1, T::c_basis>;
// ----- lo_basis_with_pos_type -----
template < typename BasisWithPos1, typename BasisWithPos2>
using lo_basis_with_pos_type = std::conditional_t<(BasisWithPos1::c_basis < BasisWithPos2::c_basis),
BasisWithPos1, BasisWithPos2>;
// ----- native_lo_bwp_of -----
/* helper for canonically-ordering native dimension power-units */
template <typename Dim>
struct native_lo_bwp_of {
using _bwp_front = bwp<0, Dim::front_type::c_native_dim>;
using _pu_rest = native_lo_bwp_of<typename Dim::rest_type>;
using _bwp_rest = typename _pu_rest::bwp_type;
using bwp_type = lo_basis_with_pos_type<_bwp_front,
bwp_incr_pos_type<_bwp_rest>>;
};
template <typename P0>
struct native_lo_bwp_of<bpu_node<P0>> {
using bwp_type = bwp<0, P0::c_native_dim>;
};
// ----- without_elt -----
template <typename Dim, int Index>
struct without_elt {
using _without_rest_type = typename without_elt<typename Dim::rest_type, Index - 1>::dim_type;
using dim_type = bpu_node< typename Dim::front_type, _without_rest_type >;
};
template <typename Dim>
struct without_elt<Dim, 0> {
using dim_type = typename Dim::rest_type;
};
// ----- bpu_node -----
/** Represents the cartesian product of a list of 'native basis power units';
* represents something with dimensions
*
* Expect:
* - P isa native_bpu type
* - D satisfies bpu_list_concept
**/
template <typename P, typename D>
struct bpu_node {
static_assert(native_bpu_concept<P>);
static_assert(bpu_list_concept<D>);
/** For example:
* using b1 = basis_power_unit<dim::currency, std::ratio<1, 1>>;
* using b2 = basis_power_unit<dim::time, std::ratio<-1, 2>>;
* using foo = dimension_impl<b1,dimension_impl<b2>>;
*
* then
* foo::lookup_bpu<0> -> b1
* foo::lookup_bpu<1> -> b2
* foo::lookup_bpu<2> -> not defined
**/
using front_type = P;
using rest_type = D;
static constexpr std::uint32_t n_dimension = rest_type::n_dimension + 1;
};
/** @class dimension
@brief represent a composite dimension
**/
template <typename P0>
struct bpu_node<P0, void> {
static_assert(native_bpu_concept<P0>);
static_assert(bpu_list_concept<void>);
using front_type = P0;
using rest_type = void;
/** For example:
* using b1 = basis_power_unit<dim::time, std::ratio<-1, 2>>;
* using foo = dimension_impl<b1>;
* then
* foo::lookup_bpu<0> --> b1
* foo::lookup_bpu<1> --> not defined
**/
/** number of dimensions represented by this struct **/
static constexpr std::uint32_t n_dimension = 1;
};
// ----- di_replace_basis_scale -----
/**
* @brief Replace BpuList member with matching BasisDim, preserving everything except (inner) scalefactor
**/
template <typename BpuList, typename NewBpu>
struct di_replace_basis_scale;
template <typename Front, typename Rest, typename NewBpu, bool MatchesFront = (Front::c_native_dim == NewBpu::c_native_dim)>
struct di_replace_basis_scale_aux;
/** specialization for non-empty BpuList **/
template <typename BpuList, typename NewBpu>
struct di_replace_basis_scale {
using type = di_replace_basis_scale_aux<typename BpuList::front_type,
typename BpuList::rest_type,
NewBpu>::type;
};
/** specialization for empty BpuList -- error not found **/
template <typename NewBpu>
struct di_replace_basis_scale<void, NewBpu> {};
/** specialization for matching front **/
template <typename Front, typename Rest, typename NewBpu>
struct di_replace_basis_scale_aux<Front, Rest, NewBpu, /*MatchesFront*/ true> {
using _replace_bpu_type = bpu<Front::c_native_dim,
typename NewBpu::scalefactor_type,
typename Front::power_type>;
static_assert(native_bpu_concept<_replace_bpu_type>);
/* NewBpu replaces Front */
using type = bpu_node<_replace_bpu_type, Rest>;
};
template <typename Front, typename Rest, typename NewBpu>
struct di_replace_basis_scale_aux<Front, Rest, NewBpu, /*MatchesFront*/ false> {
/* keep Front, replace NewBpu in rest */
using type = bpu_node<Front, typename di_replace_basis_scale<Rest, NewBpu>::type>;
};
// ----- bpu_cartesian_product -----
/** Require:
* - B isa native_bpu type
* - DI_Front is a native_bpu type
* - DI_Rest is a dimension_impl type
*
* Promise:
* - type isa dimension_impl type
**/
template < typename B,
typename DI_Front,
typename DI_Rest,
bool MatchesFront = (B::c_native_dim == DI_Front::c_native_dim) >
struct bpu_cartesian_product_helper;
/** require:
* - B isa native_bpu type
* - DI isa (bpu_list | void) type
**/
template < typename B, typename DI >
struct bpu_cartesian_product {
static_assert(native_bpu_concept<B>);
static_assert(bpu_list_concept<DI>);
using _tmp = bpu_cartesian_product_helper<B,
typename DI::front_type,
typename DI::rest_type>;
using outer_scalefactor_type = typename _tmp::outer_scalefactor_type;
static constexpr double c_outer_scalefactor_inexact = _tmp::c_outer_scalefactor_inexact;
using bpu_list_type = typename _tmp::bpu_list_type;
static_assert(ratio_concept<outer_scalefactor_type>);
static_assert(bpu_list_concept<bpu_list_type>);
};
/** Reminder: void represents the 'no dimension' of a dimensionless quantity **/
template < typename B >
struct bpu_cartesian_product<B, void> {
using outer_scalefactor_type = std::ratio<1>;
static constexpr double c_outer_scalefactor_inexact = 1.0;
using bpu_list_type = bpu_node<B, void>;
static_assert(ratio_concept<outer_scalefactor_type>);
static_assert(bpu_list_concept<bpu_list_type>);
};
/* specialize for matching front */
template <typename B, typename DI_Front, typename DI_Rest>
struct bpu_cartesian_product_helper<B, DI_Front, DI_Rest, /*MatchesFront*/ true> {
static_assert(native_bpu_concept<B>);
static_assert(native_bpu_concept<DI_Front>);
static_assert(bpu_list_concept<DI_Rest>);
/* _mult_type may have zero exponent (power_type);
* in that case bpu_smart_cons will collapse to DI_Rest
*/
using _front_mult_type = bpu_product<B, DI_Front>;
using _front_type = typename _front_mult_type::native_bpu_type;
using _rest_type = DI_Rest;
using outer_scalefactor_type = typename _front_mult_type::outer_scalefactor_type;
static constexpr double c_outer_scalefactor_inexact = _front_mult_type::c_outer_scalefactor_inexact;
using bpu_list_type = bpu_smart_cons_t<_front_type, DI_Rest>;
static_assert(ratio_concept<outer_scalefactor_type>);
static_assert(bpu_list_concept<bpu_list_type>);
};
/* specialize for not-matching-front */
template <typename B, typename DI_Front, typename DI_Rest>
struct bpu_cartesian_product_helper<B, DI_Front, DI_Rest, /*MatchesFront*/ false> {
static_assert(native_bpu_concept<B>);
static_assert(native_bpu_concept<DI_Front>);
static_assert(bpu_list_concept<DI_Rest>);
using _rest_mult_type = bpu_cartesian_product< B, DI_Rest >;
using _front_type = DI_Front;
using _rest_type = typename _rest_mult_type::bpu_list_type;
using outer_scalefactor_type = typename _rest_mult_type::outer_scalefactor_type;
static constexpr double c_outer_scalefactor_inexact = _rest_mult_type::c_outer_scalefactor_inexact;
using bpu_list_type = bpu_node<DI_Front, _rest_type>;
static_assert(ratio_concept<outer_scalefactor_type>);
static_assert(bpu_list_concept<bpu_list_type>);
};
// ----- di_cartesian_product -----
template < typename D1, typename D2 > struct di_cartesian_product;
// ----- bpu_cartesian_product1 -----
template < typename B1, typename R1, typename D2 >
struct di_cartesian_product1 {
static_assert(native_bpu_concept<B1>);
static_assert(bpu_list_concept<R1>);
static_assert(bpu_list_concept<D2>);
using _tmp1_mult_type = bpu_cartesian_product<B1, D2>;
using _tmp1_scalefactor_type = _tmp1_mult_type::outer_scalefactor_type;
using _tmp1_bpu_list_type = _tmp1_mult_type::bpu_list_type;
using _tmp2_mult_type = di_cartesian_product<R1, _tmp1_bpu_list_type>;
using _tmp2_scalefactor_type = _tmp2_mult_type::outer_scalefactor_type;
using _tmp2_bpu_list_type = _tmp2_mult_type::bpu_list_type;
using outer_scalefactor_type = std::ratio_multiply<
_tmp1_scalefactor_type,
_tmp2_scalefactor_type>;
static constexpr double c_outer_scalefactor_inexact = (_tmp1_mult_type::c_outer_scalefactor_inexact
* _tmp2_mult_type::c_outer_scalefactor_inexact);
using bpu_list_type = _tmp2_bpu_list_type;
static_assert(ratio_concept<outer_scalefactor_type>);
static_assert(bpu_list_concept<bpu_list_type>);
};
template < typename B1, typename D2 >
struct di_cartesian_product1<B1, void, D2> {
static_assert(native_bpu_concept<B1>);
static_assert(bpu_list_concept<D2>);
using _tmp_mult_type = bpu_cartesian_product<B1, D2>;
using outer_scalefactor_type = _tmp_mult_type::outer_scalefactor_type;
static constexpr double c_outer_scalefactor_inexact = _tmp_mult_type::c_outer_scalefactor_inexact;
using bpu_list_type = _tmp_mult_type::bpu_list_type;
};
// ----- di_invert -----
/* note: rescaling never required here,
* since not combining basis dimensions.
*/
template <typename BpuList>
struct di_invert;
template <>
struct di_invert<void> {
using type = void;
};
template <typename BpuList>
struct di_invert {
using type = bpu_node<
typename bpu_invert<typename BpuList::front_type>::type,
typename di_invert<typename BpuList::rest_type>::type
>;
};
// ----- di_cartesian_product -----
template < typename D1, typename D2 >
struct di_cartesian_product {
static_assert(bpu_list_concept<D1>);
static_assert(bpu_list_concept<D2>);
using _mult_type = di_cartesian_product1<
typename D1::front_type,
typename D1::rest_type,
D2>;
using outer_scalefactor_type = _mult_type::outer_scalefactor_type;
static constexpr double c_outer_scalefactor_inexact = _mult_type::c_outer_scalefactor_inexact;
using bpu_list_type = _mult_type::bpu_list_type;
static_assert(ratio_concept<outer_scalefactor_type>);
static_assert(bpu_list_concept<bpu_list_type>);
};
template < typename D2 >
struct di_cartesian_product< void, D2 > {
static_assert(bpu_list_concept<D2>);
using outer_scalefactor_type = std::ratio<1>;
static constexpr double c_outer_scalefactor_inexact = 1.0;
using bpu_list_type = D2;
};
template <typename D1 >
struct di_cartesian_product< D1, void > {
static_assert(bpu_list_concept<D1>);
using outer_scalefactor_type = std::ratio<1>;
static constexpr double c_outer_scalefactor_inexact = 1.0;
using bpu_list_type = D1;
};
// ----- di_assemble_abbrev -----
/* reminder: can't partially specialize a template function -> need struct wrapper */
template < typename DI >
struct di_assemble_abbrev;
/** Expect:
* - P isa native_bpu type
* - P::power_type = std::ratio<..>
* - P::c_native_dim :: dim
* - P::c_num :: int
* - P::c_den :: int
* - D isa dimension_impl type
* - D::front_type = native_bpu<..>
* - D::rest_type = dimension_impl<..>
* - D::n_dimension :: int
**/
template <typename P, typename D>
struct di_assemble_abbrev_helper {
static_assert(native_bpu_concept<P>);
static_assert(bpu_list_concept<D>);
static constexpr auto _prefix = bpu_assemble_abbrev<P>();
static constexpr auto _suffix = di_assemble_abbrev<D>::value;
static constexpr auto value = stringliteral_concat(_prefix.value_,
".",
_suffix.value_);
};
template <typename P>
struct di_assemble_abbrev_helper<P, void> {
static constexpr auto value = bpu_assemble_abbrev<P>();
};
template < typename DI >
struct di_assemble_abbrev {
static_assert(bpu_list_concept<DI>);
using _helper_type = di_assemble_abbrev_helper <typename DI::front_type, typename DI::rest_type>;
static constexpr auto value = _helper_type::value;
};
template <>
struct di_assemble_abbrev<void> {
static constexpr auto value = stringliteral("");
};
// ----- canonical_impl -----
template <typename D>
struct canonical_impl {
/*
* bwp_front::c_index
* bwp_front::c_native_dim
*/
using _bwp_front = native_lo_bwp_of<D>::bwp_type;
using _front_type = typename lookup_bpu<D, _bwp_front::c_index>::power_unit_type;
using _rest0_type = typename without_elt<D, _bwp_front::c_index>::dim_type;
using _rest_type = canonical_impl<_rest0_type>::dim_type;
using dim_type = bpu_node<_front_type, _rest_type>;
};
/** compute canonical renumbering of a dimension
**/
template <>
struct canonical_impl<void> {
using dim_type = void;
};
template<typename D>
using canonical_t = canonical_impl<D>::dim_type;
} /*namespace unit*/
} /*namespace xo*/
/* end dimension_impl.hpp */

View file

@ -1,259 +0,0 @@
/* @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 */

View file

@ -1,41 +0,0 @@
/* @file native_bpu_concept.hpp */
#pragma once
#include "ratio_concept.hpp"
#include "dim_util.hpp"
#include <concepts>
namespace xo {
namespace unit {
/**
* e.g. see native_bpu<native_dim_id, std::ratio<..>>
*
* bpu short for 'basis power unit'.
*
* NOTE: in typical c++ use, there won't be a reason to declare
* a variable of type NativeBpu. Instead will appear
* as a template argument.
**/
template <typename NativeBpu>
concept native_bpu_concept = requires(NativeBpu bpu)
{
typename NativeBpu::scalefactor_type;
typename NativeBpu::power_type;
// NativeBpu::c_native_dim :: native_dim_id
// NativeBpu::c_scale :: std::intmax_t
// NativeBpu::num :: int
// NativeBpu::den :: int
}
&& ((std::is_same_v<decltype(NativeBpu::c_native_dim), const dim>)
&& ratio_concept<typename NativeBpu::scalefactor_type>
&& ratio_concept<typename NativeBpu::power_type>
&& (std::is_signed_v<decltype(NativeBpu::c_num)>)
&& (std::is_signed_v<decltype(NativeBpu::c_den)>))
// && std::copyable<Foo>
;
} /*namespace unit*/
} /*namespace xo*/
/* end native_bpu_concept.hpp */

View file

@ -1,34 +0,0 @@
/* @file numeric_concept.hpp */
#pragma once
#include <concepts>
namespace xo {
namespace unit {
/** @concept numeric_concept
* @brief Concept for values that participate in arithmetic operations (+,-,*,/) and comparisons
*
* Intended to include at least:
* - built-in integral and floating-point types
* - xo::raio<U>
* - xo::unit::quantity<U,R>
*
* Intend numeric_concept to apply to types suitable for
* xo::unit::quantity::repr_type.
**/
template <typename T, typename U = T>
concept numeric_concept = requires(T x, U y)
{
{ -x };
{ x - y };
{ x + y };
{ x * y };
{ x / y };
{ x == y };
{ x != y };
};
} /*namespace unit*/
} /*namespace xo*/
/* end numeric_concept.hpp */

View file

@ -1,831 +0,0 @@
/* @file quantity.hpp */
#pragma once
#include "quantity_concept.hpp"
#include "unit.hpp"
#include "detail/promoter.hpp"
//#include "xo/reflect/Reflect.hpp"
//#include "xo/indentlog/scope.hpp"
namespace xo {
namespace unit {
// ----- quantity -----
/** @class quantity
*
* @brief represents a scalar quantity; enforces dimensional consistency at compile time.
*
* - @p Unit is a type identifying dimension and scale attaching to this quantity.
* Unit must satisfy @c unit_concept<Unit>
* - @p Repr is a type used to represent quantity values, scaled by @p Unit.
* Repr must satisfy @c numeric_concept<Repr>
*
* A quantity's run-time state consists of exactly one @p Repr
* instance:
* @c sizeof(quantity<Unit, Repr>) == sizeof(Repr)
**/
template <typename Unit, typename Repr = double>
class quantity {
public:
/** @defgroup mpl-quantity-traits **/
///@{
/** @brief type capturing the units (and dimension) of this quantity **/
using unit_type = Unit;
/** @brief type used for representation of this quantity **/
using repr_type = Repr;
///@}
static_assert(unit_concept<Unit>);
static_assert(numeric_concept<Repr>);
/* non-unity compile-time scale factors can arise during unit conversion;
* for example see method quantity::in_units_of()
*/
static_assert(std::same_as< typename Unit::scalefactor_type, std::ratio<1> >);
static_assert(std::same_as< typename Unit::canon_type, typename Unit::canon_type >);
public:
/** @defgroup mpl-quantity-ctors constructors
**/
///@{
constexpr quantity() = default;
constexpr quantity(quantity const & x) = default;
constexpr quantity(quantity && x) = default;
///@}
/** @defgroup quantity-named-ctors named constructors
**/
///@{
/** @brief construct a unit quantity using @c unit_type
*
* @code
* auto q = qty::milliseconds(17) / qty::kilometers(23.0);
* q::unit_quantity(); // 1ms.km^-1
* @endcode
**/
static constexpr quantity unit_quantity() { return quantity(1); }
/** @brief promote representation to quantity. Same as multiplying by Unit
**/
static constexpr auto promote(Repr x);
///@}
/** @addtogroup mpl-quantity-traits **/
///@{
/** @brief report this quantity's basis-power-unit type for a given basis dimension
*
* Example:
* @code
* auto q = 1.0 / (qty::milliseconds(5) * qty::seconds(100.0));
* q.unit_cstr(); // "ms^-2"
*
* using tmp = q.find_bpu_t<dim::time>;
*
* tmp::c_native_dim; // dim::time
* tmp::c_native_unit; // native_unit_id::second
* tmp::scalefactor_type::num; // 1
* tmp::scalefactor_type::den; // 1000
* tmp::power_type::num; // -2
* tmp::pwoer_type::den; // 1
* @endcode
**/
template <dim BasisDim>
using find_bpu_t = unit_find_bpu_t<unit_type, BasisDim>;
/** @brief report this quantity's scalefactor type for given basis dimension **/
template <dim BasisDim>
using basis_scale_type = typename find_bpu_t<BasisDim>::scalefactor_type::type;
///@}
/** @defgroup mpl-quantity-access-methods **/
///@{
/** @brief get scale value (relative to unit) (@ref scale_) **/
constexpr Repr scale() const { return scale_; }
/** @brief abbreviation for this quantity's units
*
* This string literal is constructed at compile-time by concatenating
* abbreviations for each basis-power-unit.
* For implementation see:
* * @c xo::unit::native_unit_abbrev_helper
* (in xo/unit/basis_unit.hpp) for each native dimension
* * @c xo::unit::scaled_native_unit_abbrev
* (in xo/unit/basis_unit.hpp) last-resort handling for scaled native dimensions
* * @c xo::unit::scaled_native_unit_abbrev
* (in xo/unit/unit.hpp) specializations for scaled native dimensions
**/
static constexpr char const * unit_cstr() { return unit_abbrev_v<unit_type>.c_str(); }
///@}
/** @defgroup mpl-quantity-constants constants
**/
///@{
/** @brief report exponent of @p BasisDim in dimension of this quantity
*
* For example:
* @code
* auto q = qty::milliseconds(5) * qty::seconds(1);
* int p1 = q.basis_power<dim::time>; // p1 == 2
* int p2 = q.basis_power<dim::mass>; // p2 == 0
* @endcode
**/
template <dim BasisDim, typename PowerRepr = int>
static constexpr PowerRepr basis_power = from_ratio<PowerRepr,
typename find_bpu_t<BasisDim>::power_type>();
///@}
/** @defgroup quantity-unit-conversion **/
///@{
/** @brief convert to quantity representing the same amount, but changing units and perhaps representation.
*
* These two expressions are equivalent:
* @code
* q.with_unit<units::millisecond>();
* quantity<units::millisecond, q::repr_type>(q);
* @endcode
*
**/
template <typename Unit2, typename Repr2 = repr_type>
constexpr quantity<Unit2, Repr2> with_unit() const { return *this; }
/**
* @brief produce quantity scaled according to @p BasisUnit2, representing the same value as @c *this.
*
* For example:
*
* @code{.cpp}
* auto q1 = 1.0 / minutes(1) * kilograms(2.5); // q1 = 2.5kg.min^-1
* auto q2 = q1.with_basis_unit<units::millisecond>(); // q2 in kg.ms^-1
* @endcode
*
* Motivation is ability to chain rescaling to reach desired compound unit
*
* @code
* auto q3 = q1.with_basis_unit<units::second>()
* .with_basis_unit<units::gram>(); // q3 in g.s^-1
* @endcode
**/
template <typename BasisUnit2, typename Repr2 = repr_type>
constexpr auto with_basis_unit() const {
static_assert(basis_unit_concept<BasisUnit2>);
using new_bpu_type = BasisUnit2::dim_type::front_type;
using old_bpulist_type = unit_type::dim_type;
using new_bpulist_type = di_replace_basis_scale<old_bpulist_type, new_bpu_type>::type;
using new_unit_type = wrap_unit<std::ratio<1>, new_bpulist_type>;
# ifdef NOT_USING_DEBUG
using xo::reflect::Reflect;
scope log(XO_DEBUG(true /*c_debug_flag*/));
log && log(xtag("old_unit_type", Reflect::require<unit_type>()->canonical_name()));
log && log(xtag("new_unit_type", Reflect::require<new_unit_type>()->canonical_name()));
# endif
return this->with_unit<new_unit_type, Repr2>();
}
/**
* @brief express this quantity in the same units as @p q
*
* @pre @c *this and @p q must have the same dimension
*
* @param q take units from @c q::unit_type, ignoring @c q.scale()
* @return this amount, but expressed using the same units as @p q
**/
template <typename Quantity>
auto with_units_from(Quantity q) const {
return this->with_units<typename Quantity::unit_type>();
}
/**
* @brief express this quantity in units of @p Unit2.
*
* @p Unit2 specifies new units
* @p Repr2 specifies representation
* @return this amount, but expressed as a multiple of @p Unit2
**/
template <typename Unit2, typename Repr2 = repr_type>
auto with_units() const {
Repr2 x = this->in_units_of<Unit2, Repr2>();
return quantity<Unit2, Repr2>::promote(x);
}
/**
* @brief compute scale with respect to @p Unit2
*
* @pre @c *this must have the same dimension as @p Unit2
*
* @p Unit2 rescale in terms of this unit.
* @p Repr2 compute scale in this representation
* @return scale to use for @c quantity<Unit2,Repr2> representing the same amount as @c *this.
**/
template <typename Unit2, typename Repr2 = repr_type>
Repr2 in_units_of() const {
// static_assert(dimension_of<Unit> == dimension_of<Unit2>); // discard all the scaling values
static_assert(same_dimension_v<Unit, Unit2>);
using _convert_to_u2_type = unit_cartesian_product<Unit, unit_invert_t<Unit2>>;
using exact_scalefactor_type = _convert_to_u2_type::exact_unit_type::scalefactor_type;
constexpr double c_scalefactor_inexact = _convert_to_u2_type::c_scalefactor_inexact;
// _convert_u2_type
// - scalefactor_type
// - dim_type
// - canon_type
/* if _convert_u2_type isn't dimensionless, then {Unit2, Unit} have different dimensions */
return ((this->scale_ * c_scalefactor_inexact * exact_scalefactor_type::num) / exact_scalefactor_type::den);
}
/**
* @brief convert to quantity with representation @p Repr2
*
* @return a quantity representing the same amount as @c *this, but using representation @p Repr2
**/
template <typename Repr2>
constexpr quantity<unit_type, Repr2> with_repr() const { return quantity<unit_type, Repr2>::promote(scale_); }
///@}
/** @defgroup quantity-arithmeticsupport **/
///@{
/**
* @brief multiply this quantity *x* by another quantity *y*.
*
* Result will propagate dimension and units appropriately.
* If *x* and *y* use conflicting scale factors for a dimension,
* adopt scalefactor from *x*.
*
* note: result will be a dimensionless value (e.g. type @c double)
* if units cancel.
*
* @pre @p Quantity2 must satisfy @c quantity_concept<Quantity2>
*
* @param y multiply by this amount
* @return x.multiply(y) returns amount representing x*y
**/
template <typename Quantity2>
auto multiply(Quantity2 y) const {
//constexpr bool c_debug_flag = false;
//using Reflect = xo::reflect::Reflect;
//scope log(XO_DEBUG(c_debug_flag));
static_assert(quantity_concept<Quantity2>);
/* unit: may have non-unit scalefactor_type */
using unit_product_type = unit_cartesian_product<Unit, typename Quantity2::unit_type>;
using exact_unit_type = unit_product_type::exact_unit_type;
using norm_unit_type = normalize_unit_t<exact_unit_type>;
using exact_scalefactor_type = exact_unit_type::scalefactor_type;
constexpr double c_scalefactor_inexact = unit_product_type::c_scalefactor_inexact;
using repr_type = std::common_type_t<repr_type, typename Quantity2::repr_type>;
repr_type r_scale = ((scale() * y.scale() * c_scalefactor_inexact * exact_scalefactor_type::num)
/ exact_scalefactor_type::den);
# ifdef NOT_USING_DEBUG
log && log(xtag("unit_product_type", Reflect::require<unit_product_type>()->canonical_name()));
log && log(xtag("exact_unit_type", Reflect::require<exact_unit_type>()->canonical_name()));
log && log(xtag("norm_unit_type", Reflect::require<norm_unit_type>()->canonical_name()));
log && log(xtag("exact_scalefactor_type", Reflect::require<exact_scalefactor_type>()->canonical_name()));
log && log(xtag("c_scalefactor_inexact", c_scalefactor_inexact));
log && log(xtag("repr_type", Reflect::require<repr_type>()->canonical_name()));
log && log(xtag("repr_type", Reflect::require<repr_type>()->canonical_name()));
# endif
return quantity<norm_unit_type, repr_type>::promote(r_scale);
}
/**
* @brief multiply this quantity *x* by another quantity *y*
*
* Result will propagate dimension and units appropriately.
* If *x* and *y* use conflicting scale factors for a dimension,
* adopt scalefactor from *x*.
*
* note: result will be a dimensionless value (e.g. type @c double)
* if units cancel.
*
* @pre @p Quantity2 must satisfy @c quantity_concept<Quantity2>
*
* @param y divide by this amount
* @return x.divide(y) returns amount representing x/y
**/
template <typename Quantity2>
auto divide(Quantity2 y) const {
using unit_divide_type = unit_divide<Unit, typename Quantity2::unit_type>;
using exact_unit_type = unit_divide_type::exact_unit_type;
using norm_unit_type = normalize_unit_t<exact_unit_type>;
using exact_scalefactor_type = exact_unit_type::scalefactor_type;
constexpr double c_scalefactor_inexact = unit_divide_type::c_scalefactor_inexact;
using repr_type = std::common_type_t<repr_type, typename Quantity2::repr_type>;
repr_type r_scale = ((scale() * c_scalefactor_inexact * exact_scalefactor_type::num)
/ (y.scale() * exact_scalefactor_type::den));
# ifdef NOT_USING_DEBUG
using xo::reflect::Reflect;
scope log(XO_DEBUG(true /*c_debug_flag*/));
log && log(xtag("unit_divide_type", Reflect::require<unit_divide_type>()->canonical_name()));
log && log(xtag("exact_unit_type", Reflect::require<exact_unit_type>()->canonical_name()));
log && log(xtag("norm_unit_type", Reflect::require<norm_unit_type>()->canonical_name()));
log && log(xtag("exact_scalefactor_type", Reflect::require<exact_scalefactor_type>()->canonical_name()));
log && log(xtag("c_scalefactor_inexact", c_scalefactor_inexact));
log && log(xtag("r_scale", r_scale));
log && log(xtag("repr_type", Reflect::require<repr_type>()->canonical_name()));
# endif
return quantity<norm_unit_type, repr_type>::promote(r_scale);
}
// quantity operator*=()
// quantity operator/=()
/**
* @brief scale this quantity *x* by dimensionless amount @p y
*
* @return quantity representing @c x*y
**/
template <typename Repr2>
auto scale_by(Repr2 y) const {
static_assert(!quantity_concept<Repr2>);
using r_repr_type = std::common_type_t<repr_type, Repr2>;
r_repr_type r_scale = this->scale_ * y;
//std::cerr << "quantity::scale_by: scale=" << scale << ", repr_type=" << reflect::Reflect::require<repr_type>()->canonical_name() << std::endl;
return quantity<unit_type, r_repr_type>::promote(r_scale);
}
/**
* @brief divide this quantity *x* by dimensionless amount @p y
*
* @return quantity representing @c x/y
**/
template <typename Repr2>
auto divide_by(Repr2 x) const {
using r_repr_type = std::common_type_t<repr_type, Repr2>;
r_repr_type r_scale = this->scale_ / x;
return quantity<unit_type, r_repr_type>::promote(r_scale);
}
/**
* @brief divide dimensionless number @p x by this quantity @c y
*
* @return quantity representing @c x/y
**/
template <typename Repr2>
auto divide_into(Repr2 x) const {
using r_unit_type = unit_invert_t<Unit>;
using r_repr_type = std::common_type_t<repr_type, Repr2>;
r_repr_type r_scale = ((x * r_unit_type::scalefactor_type::num)
/ (this->scale_ * r_unit_type::scalefactor_type::den));
return quantity<r_unit_type, r_repr_type>::promote(r_scale);
}
///@}
/** @defgroup mpl-quantity-arithmetic **/
///@{
/** @brief add quantity in-place
*
* @pre @p y must have the same dimension as @c *this.
*
* @param y quantity to add
* @return this quantity after adding y
**/
template <typename Quantity2>
quantity & operator+=(Quantity2 y) {
static_assert(same_dimension_v<unit_type, typename Quantity2::unit_type>);
/* relying on assignment that correctly converts-to-lhs-units */
quantity y2 = y;
this->scale_ += y2.scale();
return *this;
}
/** @brief subtract quantity in-place
*
* @pre @p y must have the same dimensions as @c *this
*
* @param y quantity to subtract
* @return this quantity after subtracting y
**/
template <typename Quantity2>
quantity & operator-=(Quantity2 y) {
static_assert(same_dimension_v<unit_type, typename Quantity2::unit_type>);
quantity y2 = y;
/* relying on assignment that correctly converts-to-lhs-units */
this->scale_ -= y2.scale();
return *this;
}
///@}
/** @defgroup quantity-comparisonsupport **/
///@{
/** @brief compare this quantity with another, return 3-way comparison
*
* @pre arguments must be quantities having the same dimension
*
* @param y rhs quantity to compare
* @return signed integer; {-ve, 0, +ve} when @c *this is {less than, equal, greater than} @p y
**/
template <typename Quantity2>
requires quantity_concept<Quantity2> && same_dimension_v<unit_type, typename Quantity2::unit_type>
auto compare(Quantity2 y) const {
/* convert y to same {units, repr} as *this */
quantity y2 = y;
auto cmp = (this->scale_ <=> y2.scale());
return cmp;
}
///@}
/** @defgroup quantity-comparison **/
///@{
/** @brief 3-way comparison of two quantities
*
* @pre arguments must be quantities having the same dimension
*
* @param y rhs quantity to compare
* @return std::partial_ordering
**/
template <typename Quantity2>
requires quantity_concept<Quantity2> && same_dimension_v<unit_type, typename Quantity2::unit_type>
auto operator<=>(Quantity2 y) const {
return this->compare(y);
}
/** @brief compare two quantities for equality
*
* Although compiler generates this (due to presence of 3-way comparison operator),
* it flags ambiguous overload when included alongside .h files from std::distribution.
* Look like ambiguity would need to be resolved by a header change
**/
template <typename Quantity2>
requires quantity_concept<Quantity2> && same_dimension_v<unit_type, typename Quantity2::unit_type>
bool operator==(Quantity2 y) const {
return std::is_eq(this->compare(y));
}
template <typename Quantity2>
requires quantity_concept<Quantity2> && same_dimension_v<unit_type, typename Quantity2::unit_type>
bool operator!=(Quantity2 y) const {
return std::is_neq(this->compare(y));
}
/** @addtogroup mpl-quantity-unit-conversion **/
///@{
/** @brief convert to quantity with same dimension, different {unit_type, repr_type}
*
* @pre @c Quantity2 must have the same dimension as @c *this.
**/
template <typename Quantity2>
requires quantity_concept<Quantity2> && same_dimension_v<unit_type, typename Quantity2::unit_type>
constexpr operator Quantity2 () const {
/* avoid truncating precision when converting:
* use best available representation
*/
using tmp_repr_type = std::common_type_t<repr_type, typename Quantity2::repr_type>;
return Quantity2::promote(this->in_units_of<typename Quantity2::unit_type, tmp_repr_type>());
}
///@}
/** @defgroup mpl-quantity-print-support **/
///@{
/** @brief write printed representation on stream
*
* @param os write on this output stream
**/
void display(std::ostream & os) const {
os << this->scale() << unit_cstr();
}
///@}
/** @defgroup mpl-quantity-assignment **/
///@{
/** @brief copy constructor **/
quantity & operator=(quantity const & x) = default;
/** @brief move constructor **/
quantity & operator=(quantity && x) = default;
///@}
private:
explicit constexpr quantity(Repr x) : scale_{x} {}
friend class promoter<Unit, Repr, true>;
friend class promoter<Unit, Repr, false>;
private:
/** @brief quantity represents this multiple of a unit (that has compile-time outer-scalefactor of 1) **/
Repr scale_ = 0;
}; /*quantity*/
template <typename Unit, typename Repr>
constexpr auto
quantity<Unit, Repr>::promote(Repr x) {
//std::cerr << "quantity<U,R>::promote: x=" << x << ", R=" << reflect::Reflect::require<Repr>()->canonical_name() << std::endl;
return promoter<Unit, Repr>::promote(x);
}
// ----- operator+ -----
template <typename Quantity1, typename Quantity2>
inline constexpr Quantity1 operator+ (Quantity1 x, Quantity2 y) {
static_assert(same_dimension_v<typename Quantity1::unit_type, typename Quantity2::unit_type>);
/* convert y to match units used by x;
* would fail at compile time if this isn't well-defined
*/
Quantity1 y2 = y;
return Quantity1::promote(x.scale() + y2.scale());
}
template <typename Quantity1, typename Quantity2>
inline constexpr Quantity1 operator- (Quantity1 x, Quantity2 y) {
static_assert(std::same_as
< typename Quantity1::unit_type::dimension_type::canon_type,
typename Quantity2::unit_type::dimension_type::canon_type >);
/* convert y to match units used by x */
Quantity1 y2 = y;
return Quantity1::promote(x.scale() - y2.scale());
}
template <typename Quantity>
inline Quantity operator- (Quantity x) {
return Quantity::promote(- x.scale());
}
template <typename Quantity1, typename Quantity2>
inline constexpr auto operator* (Quantity1 x, Quantity2 y) {
static_assert(quantity_concept<Quantity1>);
static_assert(quantity_concept<Quantity2>);
return x.multiply(y);
}
/** e.g. DECLARE_LH_MULT(int32_t) **/
# define DECLARE_LH_MULT(lhtype) \
template <typename Quantity> \
inline constexpr auto \
operator* (lhtype x, Quantity y) { \
static_assert(quantity_concept<Quantity>); \
return y.scale_by(x); \
}
DECLARE_LH_MULT(int8_t);
DECLARE_LH_MULT(uint8_t);
DECLARE_LH_MULT(int16_t);
DECLARE_LH_MULT(uint16_t);
DECLARE_LH_MULT(int32_t);
DECLARE_LH_MULT(uint32_t);
DECLARE_LH_MULT(int64_t);
DECLARE_LH_MULT(uint64_t);
DECLARE_LH_MULT(float);
DECLARE_LH_MULT(double);
# undef DECLARE_LH_MULT
/** e.g. DECLARE_RH_MULT(int32_t) **/
# define DECLARE_RH_MULT(rhtype) \
template <typename Quantity> \
inline constexpr auto \
operator* (Quantity x, rhtype y) { \
static_assert(quantity_concept<Quantity>); \
return x.scale_by(y); \
}
DECLARE_RH_MULT(int8_t);
DECLARE_RH_MULT(uint8_t);
DECLARE_RH_MULT(int16_t);
DECLARE_RH_MULT(uint16_t);
DECLARE_RH_MULT(int32_t);
DECLARE_RH_MULT(uint32_t);
DECLARE_RH_MULT(int64_t);
DECLARE_RH_MULT(uint64_t);
DECLARE_RH_MULT(float);
DECLARE_RH_MULT(double);
# undef DECLARE_LH_MULT
template <typename Quantity1, typename Quantity2>
inline constexpr auto operator/ (Quantity1 x, Quantity2 y) {
static_assert(quantity_concept<Quantity1>);
static_assert(quantity_concept<Quantity2>);
return x.divide(y);
}
# define DECLARE_LH_DIV(lhtype) \
template <typename Quantity> \
inline constexpr auto \
operator/ (lhtype x, Quantity y) { \
static_assert(quantity_concept<Quantity>); \
return y.divide_into(x); \
}
DECLARE_LH_DIV(int8_t);
DECLARE_LH_DIV(uint8_t);
DECLARE_LH_DIV(int16_t);
DECLARE_LH_DIV(uint16_t);
DECLARE_LH_DIV(int32_t);
DECLARE_LH_DIV(uint32_t);
DECLARE_LH_DIV(int64_t);
DECLARE_LH_DIV(uint64_t);
DECLARE_LH_DIV(float);
DECLARE_LH_DIV(double);
# undef DECLARE_LH_DIV
# define DECLARE_RH_DIV(rhtype) \
template <typename Quantity> \
inline constexpr auto \
operator/ (Quantity x, rhtype y) { \
static_assert(quantity_concept<Quantity>); \
return x.divide_by(y); \
}
DECLARE_RH_DIV(int8_t)
DECLARE_RH_DIV(uint8_t)
DECLARE_RH_DIV(int16_t)
DECLARE_RH_DIV(uint16_t)
DECLARE_RH_DIV(int32_t)
DECLARE_RH_DIV(uint32_t)
DECLARE_RH_DIV(int64_t)
DECLARE_RH_DIV(uint64_t)
DECLARE_RH_DIV(float)
DECLARE_RH_DIV(double)
# undef DECLARE_RH_DIV
template <typename Unit, typename Repr>
inline std::ostream &
operator<< (std::ostream & os, quantity<Unit, Repr> const & x) {
x.display(os);
return os;
}
namespace qty {
// ----- mass -----
/** @brief create quantity representing @p x milligrams **/
template <typename Repr = double>
inline constexpr auto milligrams(Repr x) -> quantity<units::milligram, Repr> {
return quantity<units::milligram, Repr>::promote(x);
};
/** @brief create quantity representing @p x grams **/
template <typename Repr = double>
inline constexpr auto grams(Repr x) -> quantity<units::gram, Repr> {
return quantity<units::gram, Repr>::promote(x);
};
/** @brief create quantity representing @p x kilograms **/
template <typename Repr = double>
inline constexpr auto kilograms(Repr x) -> quantity<units::kilogram, Repr> {
return quantity<units::kilogram, Repr>::promote(x);
};
// ----- distance -----
/** @brief create quantity representing @p x millimeters **/
template <typename Repr = double>
inline constexpr auto millimeters(Repr x) -> quantity<units::millimeter, Repr> {
return quantity<units::millimeter, Repr>::promote(x);
}
/** @brief create quantity representing @p x meters **/
template <typename Repr = double>
inline constexpr auto meters(Repr x) -> quantity<units::meter, Repr> {
return quantity<units::meter, Repr>::promote(x);
}
/** @brief create quantity representing @p x kilometers **/
template <typename Repr = double>
inline constexpr auto kilometers(Repr x) -> quantity<units::kilometer, Repr> {
return quantity<units::kilometer, Repr>::promote(x);
}
// ----- time -----
/** @brief create quantity representing @p x nanoseconds **/
template <typename Repr = double>
inline constexpr auto nanoseconds(Repr x) -> quantity<units::nanosecond, Repr> {
return quantity<units::nanosecond, Repr>::promote(x);
}
/** @brief create quantity representing @p x microseconds **/
template <typename Repr = double>
inline constexpr auto microseconds(Repr x) -> quantity<units::microsecond, Repr> {
return quantity<units::microsecond, Repr>::promote(x);
}
/** @brief create quantity representing @p x milliseconds **/
template <typename Repr = double>
inline constexpr auto milliseconds(Repr x) -> quantity<units::millisecond, Repr> {
return quantity<units::millisecond, Repr>::promote(x);
}
/** @brief create quantity representing @p x seconds **/
template <typename Repr = double>
inline constexpr auto seconds(Repr x) -> quantity<units::second, Repr> {
return quantity<units::second, Repr>::promote(x);
}
/** @brief create quantity representing @p x minutes **/
template <typename Repr = double>
inline constexpr auto minutes(Repr x) -> quantity<units::minute, Repr> {
return quantity<units::minute, Repr>::promote(x);
}
/** @brief create quantity representing @p x hours **/
template <typename Repr = double>
inline constexpr auto hours(Repr x) -> quantity<units::hour, Repr> {
return quantity<units::hour, Repr>::promote(x);
}
/** @brief create quantity representing @p x days (1 day = exactly 24 hours) **/
template <typename Repr = double>
inline constexpr auto days(Repr x) -> quantity<units::day, Repr> {
return quantity<units::day, Repr>::promote(x);
}
// ----- time/volatility -----
/** quantity in units of 1/sqrt(1dy) **/
template <typename Repr = double>
inline constexpr auto volatility1d(Repr x) -> quantity<units::volatility_1d, Repr> {
return quantity<units::volatility_1d, Repr>::promote(x);
}
/** quantity in units of 1/sqrt(30days)
**/
template <typename Repr = double>
inline constexpr auto volatility30d(Repr x) -> quantity<units::volatility_30d, Repr> {
return quantity<units::volatility_30d, Repr>::promote(x);
}
/** quantity in units of 1/sqrt(250days)
**/
template <typename Repr = double>
inline constexpr auto volatility250d(Repr x) -> quantity<units::volatility_250d, Repr> {
return quantity<units::volatility_250d, Repr>::promote(x);
}
} /*namespace qty*/
namespace unit_qty {
/** @brief quantity with mass dimension, representing 1mg (1 milligram = 10^-3 grams) **/
static constexpr auto milligram = qty::milligrams(1.0);
/** @brief quantity with mass dimension, representing 1g (1 gram) **/
static constexpr auto gram = qty::grams(1.0);
/** @brief quantity with mass dimension, representing 1kg (1 kilogram = 1000 grams) **/
static constexpr auto kilogram = qty::kilograms(1.0);
/** @brief quantity with length dimension representing 1mm (10^-3 meters) **/
static constexpr auto millimeter = qty::millimeters(1.0);
/** @brief quantity with length dimension representing 1m (1 meter) **/
static constexpr auto meter = qty::meters(1.0);
/** @brief quantity with length dimension representing 1km (1 kilometer = 1000 meters) **/
static constexpr auto kilometer = qty::kilometers(1.0);
/** @brief quantity with time dimension representing 1ns (1 nanosecond = 10^-9 seconds) **/
static constexpr auto nanosecond = qty::microseconds(1);
/** @brief quantity with time dimension representing 1us (1 microsecond = 10^-6 seconds) **/
static constexpr auto microsecond = qty::microseconds(1);
/** @brief quantity with time dimension representing 1ms (1 milliseconds = 10^-3 seconds) **/
static constexpr auto millisecond = qty::milliseconds(1);
/** @brief quantity with time dimension representing 1s (1 second) **/
static constexpr auto second = qty::seconds(1);
/** @brief quantity with time dimension representing 1min (1 minute = 60 seconds) **/
static constexpr auto minute = qty::minutes(1);
/** @brief quantity with time dimension representing 1hr (1 hour = 60 minutes) **/
static constexpr auto hour = qty::hours(1);
/** @brief quantity with time dimension representing 1dy (1 day = 24 hours) **/
static constexpr auto day = qty::days(1);
}
} /*namespace unit*/
} /*namespace xo*/
/* end quantity.hpp */

View file

@ -1,23 +0,0 @@
/** @file quantity_concept.hpp **/
#pragma once
#include "unit_concept.hpp"
#include "numeric_concept.hpp"
namespace xo {
namespace unit {
template <typename Quantity>
concept quantity_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>;
} && (unit_concept<typename Quantity::unit_type>
&& numeric_concept<typename Quantity::repr_type>);
} /*namespace unit*/
} /*namespace xo*/

View file

@ -1,13 +0,0 @@
/* @file ratio_concept.hpp */
#pragma once
#include <concepts>
namespace xo {
namespace unit {
template <typename Ratio>
concept ratio_concept = (std::is_signed_v<decltype(Ratio::num)>
&& std::is_signed_v<decltype(Ratio::den)>);
} /*namespace unit*/
} /*namespace xo*/

View file

@ -1,242 +0,0 @@
/* @file ratio_util.hpp */
#pragma once
#include "ratio_concept.hpp"
#include "stringliteral.hpp"
#include <ratio>
#include <numeric>
namespace xo {
namespace unit {
// ----- ratio_floor -----
template <typename Ratio>
struct ratio_floor {
using type = std::ratio<Ratio::num/Ratio::den, 1>;
};
template <typename Ratio>
using ratio_floor_t = ratio_floor<Ratio>::type;
// ----- ratio_frac -----
template <typename Ratio>
struct ratio_frac {
static_assert(ratio_concept<Ratio>);
using type = std::ratio_subtract<typename Ratio::type,
ratio_floor_t<Ratio>>::type;
};
template <typename Ratio>
using ratio_frac_t = ratio_frac<Ratio>::type;
// ----- ratio_power -----
/** ratio to an integer power
*
* ratio_power<Base,Power>::type
*
* yields a std::ratio representing Base^Power.
**/
template <typename Base, int Power, bool IsNegative = std::signbit(Power)>
struct ratio_power_aux;
template <typename Base, int Power>
struct ratio_power_aux<Base, Power, true> {
/** std::ratio representing Base^(-Power) **/
using _inverse_ratio_type = typename ratio_power_aux<Base, -Power>::type;
/** Base^(-Power)^-1 = Base^Power **/
using type = std::ratio_divide<std::ratio<1>, _inverse_ratio_type>;
static_assert(ratio_concept<type>);
};
template <typename Base>
struct ratio_power_aux<Base, 0, false> {
using type = std::ratio<1>;
};
template <typename Base>
struct ratio_power_aux<Base, 1, false> {
using type = Base;
};
template <typename Base, int Power>
struct ratio_power_aux<Base, Power, false> {
/** Base^(Power/2) **/
using _p2_ratio_type = ratio_power_aux<Base, Power/2>::type;
/** Base^(Power%2) :
* - Base^0 if Power is even
* - Base^1 if Power is odd
**/
using _x_ratio_type = ratio_power_aux<Base, Power%2>::type;
using type = std::ratio_multiply< _x_ratio_type,
std::ratio_multiply<_p2_ratio_type,
_p2_ratio_type> >;
static_assert(ratio_concept<type>);
};
// ----- ratio_power_v -----
/** compute Base^Power at compile time **/
template <typename Base, int Power>
using ratio_power_t = ratio_power_aux<Base, Power>::type;
// ----- from_ratio -----
/** Example:
* from_ratio<double, std::ratio<1,10> --> 0.1
* from_ratio<double, std::ratio<4,5> --> 0.8
**/
template <typename Repr, typename Ratio>
constexpr Repr from_ratio() {
static_assert(ratio_concept<Ratio>);
return Ratio::num / static_cast<Repr>(Ratio::den);
}
// ----- ratio2str_aux -----
/* note: using struct wrapper because partial specialization of function templates
* is not allowed
*/
template <int Num,
int Den,
bool Signbit = std::signbit(Num) ^ std::signbit(Den)>
struct ratio2str_aux;
template <int Num,
int Den>
struct ratio2str_aux<Num, Den, false> {
static constexpr auto value = stringliteral_concat("(",
stringliteral_from_int_v<Num>().value_,
"/",
stringliteral_from_int_v<Den>().value_,
")");
};
template <int Num,
int Den>
struct ratio2str_aux<Num, Den, true> {
/* note: using struct wrapper because partial specialization of function templates
* is not allowed
*/
static constexpr auto value = stringliteral_concat("-(",
stringliteral_from_int_v<-Num>().value_,
"/",
stringliteral_from_int_v<Den>().value_,
")");
};
template <int Num>
struct ratio2str_aux<Num, /*Den*/ 1, true> {
static constexpr auto value = stringliteral_concat("-",
stringliteral_from_int_v<-Num>().value_);
};
template <int Num>
struct ratio2str_aux<Num, /*Den*/ 1, false> {
static constexpr auto value = stringliteral_from_int_v<Num>();
};
template <>
struct ratio2str_aux<1, 1, false> {
static constexpr auto value = stringliteral("");
};
// ----- stringliteral_from_ratio -----
/** Example:
* - stringliteral_from_ratio<std::ratio<2,3> -> "^(2,3)"
**/
template <typename Ratio>
constexpr auto stringliteral_from_ratio() {
static_assert(ratio_concept<Ratio>);
using lowest_terms_type = Ratio::type;
return ratio2str_aux<lowest_terms_type::num, lowest_terms_type::den>().value;
};
template <typename Ratio>
constexpr char const * cstr_from_ratio() {
static_assert(ratio_concept<Ratio>);
using lowest_terms_type = Ratio::type;
return ratio2str_aux<lowest_terms_type::num, lowest_terms_type::den>().value.c_str();
};
// ----- exponent2str_aux -----
/* note: using struct wrapper because partial specialization of function templates
* is not allowed
*/
template <int Num,
int Den,
bool Signbit = std::signbit(Num) ^ std::signbit(Den)>
struct exponent2str_aux;
template <int Num,
int Den>
struct exponent2str_aux<Num, Den, false> {
static constexpr auto value = stringliteral_concat("^(",
stringliteral_from_int_v<Num>().value_,
"/",
stringliteral_from_int_v<Den>().value_,
")");
};
template <int Num,
int Den>
struct exponent2str_aux<Num, Den, true> {
/* note: using struct wrapper because partial specialization of function templates
* is not allowed
*/
static constexpr auto value = stringliteral_concat("^-(",
stringliteral_from_int_v<-Num>().value_,
"/",
stringliteral_from_int_v<Den>().value_,
")");
};
template <int Num>
struct exponent2str_aux<Num, 1, false> {
static constexpr auto value = stringliteral_concat("^",
stringliteral_from_int_v<Num>().value_);
};
template <int Num>
struct exponent2str_aux<Num, 1, true> {
/* Example:
* - exponent2str_aux<-1, 1, true> --> "^-1"
* - exponent2str_aux<-2, 1, true> --> "^-2"
*/
static constexpr auto value = stringliteral_concat("^",
stringliteral_from_int_v<Num>().value_);
};
template <>
struct exponent2str_aux<1, 1, false> {
static constexpr auto value = stringliteral("");
};
// ----- stringliteral_from_exponent -----
template <typename Ratio>
constexpr auto stringliteral_from_exponent() {
static_assert(ratio_concept<Ratio>);
return exponent2str_aux<Ratio::type::num, Ratio::type::den>().value;
};
} /*namespace unit*/
} /*namespace xo*/
/* end ratio_util.hpp */

View file

@ -1,170 +0,0 @@
/* @file stringliteral.hpp */
#pragma once
#include <cmath>
#include <string_view>
#include <algorithm>
#include <cstdint>
namespace xo {
namespace unit {
// ----- stringliteral -----
/** @class stringliteral
*
* @brief class to represent a literal string at compile time, for use as template argument
**/
template <std::size_t N>
struct stringliteral {
constexpr stringliteral() { if (N > 0) value_[0] = '\0'; }
constexpr stringliteral(const char (&str)[N]) { std::copy_n(str, N, value_); }
constexpr int size() const { return N; }
constexpr char const * c_str() const { return value_; }
char value_[N];
};
template < typename First, typename... Rest >
constexpr auto
all_same_v = std::conjunction_v< std::is_same<First, Rest>... >;
/** args and result must be stringliteral **/
template < typename... Ts>
constexpr auto
stringliteral_concat(Ts && ... args)
{
#ifdef NOT_USING
static_assert(all_same_v<std::decay_t<Ts>...>,
"string must share the same char type");
using char_type = std::remove_const_t< std::remove_pointer_t < std::common_type_t < Ts... > > >;
#endif
using char_type = char;
/** n1: total number of bytes used by arguments **/
constexpr size_t n1 = (sizeof(Ts) + ...);
/** z1: each string arg has a null terminator included in its size,
* z1 is the number of arguments in parameter pack Ts,
* which equals the number of null terminators used
**/
constexpr size_t z1 = sizeof...(Ts);
/** n: number of chars in concatenated string. +1 for final null **/
constexpr size_t n
= (n1 / sizeof(char_type)) - z1 + 1;
stringliteral<n> result;
size_t pos = 0;
auto detail_concat = [ &pos, &result ](auto && arg) {
constexpr auto count = (sizeof(arg) - sizeof(char_type)) / sizeof(char_type);
std::copy_n(arg, count, result.value_ + pos);
pos += count;
};
(detail_concat(args), ...);
return result;
}
template <std::size_t N1, std::size_t N2>
constexpr auto
stringliteral_compare(stringliteral<N1> && s1, stringliteral<N2> && s2)
{
return std::string_view(s1.value_) <=> std::string_view(s2.value_);
}
// ----- literal_size -----
/** @brief compute number of chars needed to stringify an int **/
template < int d, bool signbit = std::signbit(d) >
struct literal_size;
template < int d >
struct literal_size<d, true> {
/* d < 0 */
static constexpr int size = 1 + literal_size<-d, false>::size;
};
template < int d >
struct literal_size<d, false> {
/* d >= 0 */
static constexpr int size = 1 + literal_size<d/10, false>::size;
};
template <> struct literal_size<0, false> { static constexpr int size = 1; };
template <> struct literal_size<1, false> { static constexpr int size = 1; };
template <> struct literal_size<2, false> { static constexpr int size = 1; };
template <> struct literal_size<3, false> { static constexpr int size = 1; };
template <> struct literal_size<4, false> { static constexpr int size = 1; };
template <> struct literal_size<5, false> { static constexpr int size = 1; };
template <> struct literal_size<6, false> { static constexpr int size = 1; };
template <> struct literal_size<7, false> { static constexpr int size = 1; };
template <> struct literal_size<8, false> { static constexpr int size = 1; };
template <> struct literal_size<9, false> { static constexpr int size = 1; };
template < int d >
constexpr int literal_size_v = literal_size<d>::size;
// ----- literal_from_digit -----
constexpr auto /*stringliteral<2>*/ stringliteral_from_digit( int d ) {
return stringliteral({ static_cast<char>('0' + d), '\0' });
}
#ifdef NOT_YET_22mar24
template < int d >
struct literal_from_digit;
template <> struct literal_from_digit<0> { static constexpr auto value = stringliteral("0"); };
template <> struct literal_from_digit<1> { static constexpr auto value = stringliteral("1"); };
template <> struct literal_from_digit<2> { static constexpr auto value = stringliteral("2"); };
template <> struct literal_from_digit<3> { static constexpr auto value = stringliteral("3"); };
template <> struct literal_from_digit<4> { static constexpr auto value = stringliteral("4"); };
template <> struct literal_from_digit<5> { static constexpr auto value = stringliteral("5"); };
template <> struct literal_from_digit<6> { static constexpr auto value = stringliteral("6"); };
template <> struct literal_from_digit<7> { static constexpr auto value = stringliteral("7"); };
template <> struct literal_from_digit<8> { static constexpr auto value = stringliteral("8"); };
template <> struct literal_from_digit<9> { static constexpr auto value = stringliteral("9"); };
template < int d >
constexpr auto literal_from_digit_v() { return literal_from_digit<d>::value; }
#endif
// ----- stringliteral_from_int -----
template < int D, int N = literal_size_v<D>, bool signbit = std::signbit(D) >
struct stringliteral_from_int;
template < int D, int N = literal_size_v<D>, bool signbit = std::signbit(D) >
constexpr auto stringliteral_from_int_v() { return stringliteral_from_int<D, N, signbit>::value; }
template < int D >
struct stringliteral_from_int< D, 1, false > {
static constexpr auto value = stringliteral_from_digit(D);
};
template < int D, int N >
struct stringliteral_from_int< D, N, false > {
static constexpr auto _prefix = stringliteral_from_int_v< D / 10, N - 1, false >();
static constexpr auto _suffix = stringliteral_from_digit(D % 10);
static constexpr auto value = stringliteral_concat(_prefix.value_, _suffix.value_);
};
template < int D, int N >
struct stringliteral_from_int< D, N, true > {
static constexpr auto _suffix = stringliteral_from_int_v< -D, N - 1, false>();
static constexpr auto value = stringliteral_concat("-", _suffix.value_);
};
} /*namespace unit*/
} /*namespace xo*/
/* end stringliteral.hpp */

View file

@ -1,395 +0,0 @@
/* @file unit.hpp */
#pragma once
#include "unit_concept.hpp"
#include "dimension_impl.hpp"
namespace xo {
namespace unit {
// ----- wrap_unit -----
template <typename Scalefactor, typename BpuList>
struct wrap_unit {
static_assert(ratio_concept<Scalefactor>);
static_assert(bpu_list_concept<BpuList>);
//using _norm_type = bpu_normalize<BpuList>;
using scalefactor_type = Scalefactor;
using dim_type = BpuList;
/* canon_type just orders dimensions by increasing native_dim_id */
using canon_type = canonical_impl<dim_type>::dim_type;
static_assert(bpu_list_concept<canon_type>);
};
// ----- normalize_unit -----
/* like Unit, but with scalefactor 1 */
template <typename Unit>
struct normalize_unit {
static_assert(unit_concept<Unit>);
using type = wrap_unit<std::ratio<1>, typename Unit::dim_type>;
};
template <typename Unit>
using normalize_unit_t = normalize_unit<Unit>::type;
// ----- dimensionless_v -----
template <typename Unit>
constexpr auto dimensionless_v = std::same_as<typename Unit::dim_type, void>;
// ----- unit_find_bpu -----
/** @brief find basis-power-unit matching native_dim_id
*
* Constructs dimensionless native_bpu if no match
**/
template <typename U, dim BasisDim>
struct unit_find_bpu {
using type = di_find_bpu<typename U::dim_type, BasisDim>::type;
};
template <typename U, dim BasisDim>
using unit_find_bpu_t = unit_find_bpu<U, BasisDim>::type;
// ----- unit_abbrev_v -----
/** @brief canonical stringliteral abbreviation for dimension D. **/
template <typename U>
constexpr auto unit_abbrev_v = di_assemble_abbrev<typename U::canon_type>::value;
// ----- unit_invert -----
template <typename U>
struct unit_invert {
static_assert(unit_concept<U>);
using _sf = std::ratio_divide<std::ratio<1>, typename U::scalefactor_type>;
using _di = di_invert< typename U::dim_type >::type;
using type = wrap_unit< _sf, _di >;
static_assert(unit_concept<type>);
};
template <typename U>
using unit_invert_t = unit_invert<U>::type;
// ----- unit_cartesian_product -----
template <typename U1, typename U2>
struct unit_cartesian_product {
/* when a basis dimension (represented by a bpu<..>::c_native_dim)
* is present in both {U1, U2}, we need to pick a common unit
* (represented by bpu<..>::scalefactor_type).
*
* scale factors from such conversions are collected in:
* 1a. _mult_type::outer_scalefactor_type (compile-time exact representation using std::ratio)
* 1b. _mult_type::outer_scalefactor_inexact (compile-time constexpr)
*/
static_assert(unit_concept<U1>);
static_assert(unit_concept<U2>);
/* _mult_type -> describes product dimension */
using _mult_type = di_cartesian_product<
typename U1::dim_type,
typename U2::dim_type>;
/* compile-time exact scalefactor for product dimension
* (distilled from any forced rescaling)
*/
using _mult_sf_type = _mult_type::outer_scalefactor_type;
/* bpulist specifying basis factors (possibly to rational powers) in product dimension */
using _mult_di_type = _mult_type::bpu_list_type;
/* note: inexact scalefactor doesn't come up here.
* It's not present in unit types, only appears as byproduct
* of products/ratios of units
*/
using _sf1_type = typename std::ratio_multiply<
typename U1::scalefactor_type,
typename U2::scalefactor_type>::type;
using _sf_type = typename std::ratio_multiply<_mult_sf_type, _sf1_type>::type;
/* note: we can compute inexact scale factor,
* but can't make it a template argument
*/
using exact_unit_type = wrap_unit< _sf_type, _mult_di_type >;
static constexpr double c_scalefactor_inexact = _mult_type::c_outer_scalefactor_inexact;
static_assert(unit_concept<exact_unit_type>);
};
/* WARNING: omits inexact scalefactor */
template <typename U1, typename U2>
using unit_cartesian_product_t = unit_cartesian_product<U1, U2>::exact_unit_type;
// ----- unit_divide -----
template <typename U1, typename U2>
struct unit_divide {
static_assert(unit_concept<U1>);
static_assert(unit_concept<U2>);
using _mult_type = unit_cartesian_product<U1, unit_invert_t<U2>>;
using exact_unit_type = _mult_type::exact_unit_type;
static constexpr double c_scalefactor_inexact = _mult_type::c_scalefactor_inexact;
};
/* WARNING: omits inexact scalefactor */
template <typename U1, typename U2>
using unit_divide_t = unit_divide<U1, U2>::exact_unit_type;
// ----- same_dimension -----
/* true iff U1 and U2 represent the same dimension, i.e. differ only in dimensionless scaling factor
*
* To verify scale also, use same_unit<U1,U2> instead
*/
template <typename U1, typename U2>
struct same_dimension {
static_assert(unit_concept<U1>);
static_assert(unit_concept<U2>);
using _unit_ratio_type = typename unit_cartesian_product<U1, unit_invert_t<U2>>::exact_unit_type;
static_assert(std::same_as<typename _unit_ratio_type::dim_type, void>);
static constexpr bool value = std::same_as<typename _unit_ratio_type::dim_type, void>;
};
template <typename U1, typename U2>
constexpr bool same_dimension_v = same_dimension<U1, U2>::value;
// ----- same_unit -----
template <typename U1, typename U2>
struct same_unit {
static_assert(unit_concept<U1>);
static_assert(unit_concept<U2>);
using _unit_ratio_type = unit_cartesian_product<U1, unit_invert_t<U2>>;
using _unit_exact_type = typename _unit_ratio_type::exact_unit_type;
using _unit_scalefactor_type = _unit_exact_type::scalefactor_type;
static constexpr double c_unit_ratio_inexact = _unit_ratio_type::c_scalefactor_inexact;
static_assert(std::same_as<_unit_scalefactor_type, std::ratio<1>>);
static_assert(std::same_as<typename _unit_exact_type::dim_type, void>);
static constexpr bool value = (std::same_as<_unit_scalefactor_type, std::ratio<1>>
&& (c_unit_ratio_inexact == 1.0)
&& std::same_as<typename _unit_exact_type::dim_type, void>);
};
template <typename U1, typename U2>
constexpr bool same_unit_v = same_unit<U1, U2>::value;
// ----- unit_conversion_factor -----
template <typename U1, typename U2>
struct unit_conversion_factor {
static_assert(same_dimension_v<U1, U2>);
using _unit_ratio_type = typename unit_cartesian_product<U1, unit_invert_t<U2>>::exact_unit_type;
using type = _unit_ratio_type::scalefactor_type;
static constexpr double c_scalefactor_inexact = _unit_ratio_type::c_scalefactor_inexact;
};
/** conversion factor from U1 to U2:
* U1 = x.U2
* with:
* x = R::num / R::den
* R = unit_conversion_factor_t<U1,U2>
*
* WARNING: omits inexact scalefactor unit_conversion_factor<U1,U2>::c_scalefactor_inexact
**/
template <typename U1, typename U2>
using unit_conversion_factor_t = unit_conversion_factor<U1, U2>::type;
// ----- units -----
namespace units {
/* computing abbreviations:
* - unit_abbrev_v<Unit> :: stringliteral<...>
* - unit_abbrev_v<Unit>.c_str() :: const char *
*
* relies on
* - di_assemble_abbrev, di_assemble_abbrev_helper [dimension_impl.hpp]
*
* - bpu_assemble_abbrev<native_bpu>() [native_bpu.hpp]
* - bpu_assemble_abbrev_helper< native_bpu::c_native_dim,
* native_bpu::scalefactor_type,
* native_bpu::power_type >
* -> stringliteral
*
* + can specialize for specific combinations
*
* - native_unit_abbrev_helper< native_bpu::c_native_dim,
* native_bpu::power_type >
*/
// ----- weight -----
/** @brief a unit type representing 1mg (10^-3 grams) **/
using milligram = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::mass,
std::ratio<1, 1000>> > >;
template <>
struct scaled_native_unit_abbrev<dim::mass, std::ratio<1, 1000>> {
static constexpr auto value = stringliteral("mg");
};
using gram = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::mass,
std::ratio<1>> > >;
using kilogram = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::mass,
std::ratio<1000>> > >;
template <>
struct scaled_native_unit_abbrev<dim::mass, std::ratio<1000>> {
static constexpr auto value = stringliteral("kg");
};
// ----- distance -----
using millimeter = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::distance,
std::ratio<1,1000>> > >;
template <>
struct scaled_native_unit_abbrev<dim::distance, std::ratio<1,1000>> {
static constexpr auto value = stringliteral("mm");
};
using meter = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::distance,
std::ratio<1>> > >;
using kilometer = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::distance,
std::ratio<1000>> > >;
template <>
struct scaled_native_unit_abbrev<dim::distance, std::ratio<1000>> {
static constexpr auto value = stringliteral("km");
};
// ----- time -----
using nanosecond = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::time,
std::ratio<1, 1000000000>> > >;
template <>
struct scaled_native_unit_abbrev<dim::time, std::ratio<1,1000000000>> {
static constexpr auto value = stringliteral("ns");
};
using microsecond = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::time,
std::ratio<1, 1000000>> > >;
template <>
struct scaled_native_unit_abbrev<dim::time, std::ratio<1,1000000>> {
static constexpr auto value = stringliteral("us");
};
using millisecond = wrap_unit< std::ratio<1>, bpu_node< bpu<dim::time,
std::ratio<1,1000>> > >;
template <>
struct scaled_native_unit_abbrev<dim::time, std::ratio<1,1000>> {
static constexpr auto value = stringliteral("ms");
};
using second = wrap_unit< std::ratio<1>, bpu_node< bpu<dim::time,
std::ratio<1>> > >;
using minute = wrap_unit< std::ratio<1>, bpu_node< bpu<dim::time,
std::ratio<60>> > >;
template <>
struct scaled_native_unit_abbrev<dim::time, std::ratio<60>> {
static constexpr auto value = stringliteral("min");
};
using hour = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::time,
std::ratio<3600>> > >;
template <>
struct scaled_native_unit_abbrev<dim::time, std::ratio<3600>> {
static constexpr auto value = stringliteral("hr");
};
using day = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::time,
std::ratio<24*3600>> > >;
template <>
struct scaled_native_unit_abbrev<dim::time, std::ratio<24*3600>> {
static constexpr auto value = stringliteral("dy");
};
using month = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::time,
std::ratio<30*24*3600>> > >;
template <>
struct scaled_native_unit_abbrev<dim::time, std::ratio<30*24*3600>> {
static constexpr auto value = stringliteral("mo");
};
using yr250 = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::time,
std::ratio<250*24*3600>> > >;
template <>
struct scaled_native_unit_abbrev<dim::time, std::ratio<250*24*3600>> {
static constexpr auto value = stringliteral("yr250");
};
// ------ volatility ------
/* volatility in units of 1/sqrt(1day)
* volatility^2 in units of 1/day
*/
using volatility_1d = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::time,
std::ratio<24*3600>,
std::ratio<-1,2>> > >;
/* volatility in units of 1/sqrt(30day)
* volatility^2 in units of 1/(30day)
*/
using volatility_30d = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::time,
std::ratio<30*24*3600>,
std::ratio<-1,2>> > >;
/* volatility in units of 1/sqrt(250day)
* volatility^2 in units of 1/(250day)
*/
using volatility_250d = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::time,
std::ratio<250*24*3600>,
std::ratio<-1,2>> > >;
using currency = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::currency,
std::ratio<1>> > >;
using price = wrap_unit< std::ratio<1>,
bpu_node< bpu<dim::price,
std::ratio<1>> > >;
} /*namespace units*/
} /*namespace unit*/
} /*namespace xo*/
/* end unit.hpp */

View file

@ -1,49 +0,0 @@
/* @file unit_concept.hpp */
#pragma once
#include "dimension_concept.hpp"
namespace xo {
namespace unit {
/** @brief concept for a Unit type, suitable for use with the quantity template
*
* Example:
* @code
* using namespace xo::unit;
* static_assert(unit_concept<units::day>);
* @endcode
**/
template <typename Unit>
concept unit_concept = requires(Unit unit)
{
typename Unit::scalefactor_type;
typename Unit::dim_type;
typename Unit::canon_type;
}
&& (ratio_concept<typename Unit::scalefactor_type>
&& bpu_list_concept<typename Unit::dim_type>
&& bpu_list_concept<typename Unit::canon_type>);
/** @brief concept for a Unit type, that contains exactly one basis dimension
*
* Example:
* @code
* using namespace xo::unit
* static_assert(basis_unit_concept<units::volatility_250d>);
* @endcode
**/
template <typename Unit>
concept basis_unit_concept = requires(Unit unit)
{
typename Unit::dim_type;
typename Unit::dim_type::rest_type;
}
&& (std::same_as<typename Unit::dim_type::rest_type, void>)
&& (unit_concept<Unit>);
} /*namespace unit*/
} /*namespace xo*/
/* end unit_concept.hpp */