xo-unit: delete obsolete mpl-based implementation
This commit is contained in:
parent
c1b7e94e82
commit
a920d2c396
15 changed files with 0 additions and 2899 deletions
|
|
@ -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 **/
|
||||
|
|
@ -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 **/
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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*/
|
||||
|
|
@ -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*/
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue