401 lines
17 KiB
C++
401 lines
17 KiB
C++
/** @file scaled_unit.hpp
|
|
*
|
|
* Author: Roland Conybeare
|
|
**/
|
|
|
|
#pragma once
|
|
|
|
#include "width2x.hpp"
|
|
|
|
namespace xo {
|
|
namespace qty {
|
|
/** @class scaled_unit
|
|
* @brief Represents the product sqrt(outer_scale_sq) * outer_scale_exact * nat_unit
|
|
**/
|
|
template < typename Int,
|
|
typename OuterScale = ratio::ratio<Int> >
|
|
struct scaled_unit {
|
|
/** @defgroup scaled-unit-type-traits scaled-unit type traits **/
|
|
///@{
|
|
|
|
/** type for representing individual basis-unit scalefactors **/
|
|
using ratio_int_type = typename natural_unit<Int>::ratio_int_type;
|
|
|
|
///@}
|
|
|
|
public:
|
|
/** @defgroup scaled-unit-ctors scaled-unit constructors **/
|
|
///@{
|
|
|
|
/** create scaled unit representing a multiple
|
|
* @p outer_scale_factor * @p sqrt(outer_scale_sq)
|
|
* of natural unit @p nat_unit
|
|
**/
|
|
constexpr scaled_unit(const natural_unit<Int> & nat_unit,
|
|
OuterScale outer_scale_factor,
|
|
double outer_scale_sq)
|
|
: outer_scale_factor_{outer_scale_factor},
|
|
outer_scale_sq_{outer_scale_sq},
|
|
natural_unit_{nat_unit}
|
|
{}
|
|
|
|
///@}
|
|
|
|
/** @defgroup scaled-unit-access-methods scaled-unit access methods **/
|
|
///@{
|
|
|
|
/** always true for scaled_unit **/
|
|
static constexpr bool is_scaled_unit_type_v = true;
|
|
|
|
/** always true for scaled_unit **/
|
|
constexpr bool is_scaled_unit_type() const { return true; }
|
|
|
|
/** true iff scaled unit can be faithfully represented by a @ref natural_unit **/
|
|
constexpr bool is_natural() const {
|
|
return (outer_scale_factor_ == OuterScale(1) && (outer_scale_sq_ == 1.0));
|
|
}
|
|
|
|
/** true if this scaled unit has no dimension **/
|
|
constexpr bool is_dimensionless() const { return natural_unit_.is_dimensionless(); }
|
|
|
|
/** get number of distinct native dimensions present.
|
|
* e.g. for unit Newton = 1 kg.m.s^-2, n_bpu would be 3,
|
|
* with {mass, distance, time} present.
|
|
* Note that this value does not count exponents
|
|
**/
|
|
constexpr std::size_t n_bpu() const { return natural_unit_.n_bpu(); }
|
|
|
|
///@}
|
|
|
|
/** @defgroup scaled-unit-general-methods scaled-unit access methods **/
|
|
///@{
|
|
|
|
/** return reciprocal of this unit. **/
|
|
constexpr scaled_unit reciprocal() const {
|
|
return scaled_unit(natural_unit_.reciprocal(),
|
|
1 / outer_scale_factor_,
|
|
1.0 / outer_scale_sq_);
|
|
}
|
|
|
|
/** get bpu for dimension @p d. if d isn't present, construct bpu with 0 power **/
|
|
constexpr bpu<Int> lookup_dim(dimension d) const {
|
|
return natural_unit_.lookup_dim(d);
|
|
}
|
|
|
|
/** return @p i'th bpu associated with this unit **/
|
|
constexpr bpu<Int> & operator[](std::size_t i) { return natural_unit_[i]; }
|
|
/** return @p i'th bpu associated with this unit (const version) **/
|
|
constexpr const bpu<Int> & operator[](std::size_t i) const { return natural_unit_[i]; }
|
|
|
|
///@}
|
|
|
|
public: /* public members so scaled_unit instance can be a non-type template parameter (a structural type) */
|
|
|
|
/** @defgroup scaled-unit-instance-vars **/
|
|
///@{
|
|
|
|
/** scale factor multiplying @ref natural_unit_ **/
|
|
OuterScale outer_scale_factor_;
|
|
|
|
/** squared scale factor multiplying @ref natural_unit_ **/
|
|
double outer_scale_sq_;
|
|
|
|
/** natural unit term in this scaled unit **/
|
|
natural_unit<Int> natural_unit_;
|
|
|
|
///@}
|
|
};
|
|
|
|
// TODO: comparison operators
|
|
|
|
namespace detail {
|
|
/** promote natural unit to scaled unit (with unit outer scalefactors) **/
|
|
template <typename Int>
|
|
constexpr auto su_promote(const natural_unit<Int> & bpuv) {
|
|
return scaled_unit<Int>(bpuv,
|
|
ratio::ratio<Int>(1, 1),
|
|
1.0);
|
|
}
|
|
}
|
|
|
|
namespace u {
|
|
/* values here can be used as template arguments to quantity:
|
|
* e.g.
|
|
* quantity<u:picogram> qty1;
|
|
* quantity<u:meter/u:second> velocity;
|
|
*/
|
|
|
|
constexpr auto
|
|
su_from_bu(const basis_unit & bu,
|
|
const power_ratio_type & power = power_ratio_type(1))
|
|
{
|
|
return detail::su_promote(natural_unit<std::int64_t>::from_bu(bu, power));
|
|
}
|
|
|
|
/** @defgroup scaled-unit-dimensionless scaled-unit dimensionless constant **/
|
|
///@{
|
|
|
|
/** dimensionless unit; equivalent to 1 **/
|
|
constexpr auto dimensionless = detail::su_promote(natural_unit<std::int64_t>());
|
|
|
|
///@}
|
|
|
|
// ----- mass units -----
|
|
|
|
/** @defgroup scaled-unit-mass scaled-unit mass units **/
|
|
///@{
|
|
|
|
/** unit of 10^-12 grams **/
|
|
constexpr auto picogram = su_from_bu(detail::bu::picogram);
|
|
/** unit of 10^-9 grams **/
|
|
constexpr auto nanogram = su_from_bu(detail::bu::nanogram);
|
|
/** unit of 10^-6 grams **/
|
|
constexpr auto microgram = su_from_bu(detail::bu::microgram);
|
|
/** unit of 10^-3 grams **/
|
|
constexpr auto milligram = su_from_bu(detail::bu::milligram);
|
|
/** unit of 1 gram **/
|
|
constexpr auto gram = su_from_bu(detail::bu::gram);
|
|
/** unit of 10^3 grams **/
|
|
constexpr auto kilogram = su_from_bu(detail::bu::kilogram);
|
|
/** unit of 1 metric tonne = 10^3 kg **/
|
|
constexpr auto tonne = su_from_bu(detail::bu::tonne);
|
|
/** unit of 10^3 tonnes = 10^6 kg **/
|
|
constexpr auto kilotonne = su_from_bu(detail::bu::kilotonne);
|
|
/** unit of 10^6 tonnes = 10^9 kg **/
|
|
constexpr auto megatonne = su_from_bu(detail::bu::megatonne);
|
|
/** unit of 10^9 tonnes = 10^12 kg **/
|
|
constexpr auto gigatonne = su_from_bu(detail::bu::gigatonne);
|
|
|
|
///@}
|
|
|
|
// ----- distance units -----
|
|
|
|
/** @defgroup scaled-unit-distance scaled-unit distance units **/
|
|
///@{
|
|
|
|
/** unit of 10^-12 meters **/
|
|
constexpr auto picometer = su_from_bu(detail::bu::picometer);
|
|
/** unit of 10^-9 meters **/
|
|
constexpr auto nanometer = su_from_bu(detail::bu::nanometer);
|
|
/** unit of 10^-6 meters **/
|
|
constexpr auto micrometer = su_from_bu(detail::bu::micrometer);
|
|
/** unit of 10^-3 meters **/
|
|
constexpr auto millimeter = su_from_bu(detail::bu::millimeter);
|
|
/** unit of 1 meter **/
|
|
constexpr auto meter = su_from_bu(detail::bu::meter);
|
|
/** unit of 10^3 meters **/
|
|
constexpr auto kilometer = su_from_bu(detail::bu::kilometer);
|
|
/** unit of 10^6 meters (not commonly used) **/
|
|
constexpr auto megameter = su_from_bu(detail::bu::megameter);
|
|
/** unit of 10^9 meters (not commonly used) **/
|
|
constexpr auto gigameter = su_from_bu(detail::bu::gigameter);
|
|
|
|
/** unit of 1 light-second = distance light travels in a vacuum in 1 second **/
|
|
constexpr auto lightsecond = su_from_bu(detail::bu::lightsecond);
|
|
/** unit of 1 astronomical unit, for approximate radius of earth orbit **/
|
|
constexpr auto astronomicalunit = su_from_bu(detail::bu::astronomicalunit);
|
|
|
|
/** unit of 1 inch = 1/12 feet **/
|
|
constexpr auto inch = su_from_bu(detail::bu::inch);
|
|
/** unit of 1 foot = 0.3048 meters **/
|
|
constexpr auto foot = su_from_bu(detail::bu::foot);
|
|
/** unit of 1 yard = 3 feet **/
|
|
constexpr auto yard = su_from_bu(detail::bu::yard);
|
|
/** unit of 1 mile = 1760 yards **/
|
|
constexpr auto mile = su_from_bu(detail::bu::mile);
|
|
|
|
///@}
|
|
|
|
// ----- time units -----
|
|
|
|
/** @defgroup scaled-unit-time scaled-unit time units **/
|
|
///@{
|
|
|
|
/** unit of 1 picosecond = 10^-12 seconds **/
|
|
constexpr auto picosecond = su_from_bu(detail::bu::picosecond);
|
|
/** unit of 1 nanosecond = 10^-9 seconds **/
|
|
constexpr auto nanosecond = su_from_bu(detail::bu::nanosecond);
|
|
/** unit of 1 microseccond = 10^-6 seconds **/
|
|
constexpr auto microsecond = su_from_bu(detail::bu::microsecond);
|
|
/** unit of 1 millisecond = 10^-3 seconds **/
|
|
constexpr auto millisecond = su_from_bu(detail::bu::millisecond);
|
|
/** unit of 1 second **/
|
|
constexpr auto second = su_from_bu(detail::bu::second);
|
|
/** unit of 1 minute **/
|
|
constexpr auto minute = su_from_bu(detail::bu::minute);
|
|
/** unit of 1 hour **/
|
|
constexpr auto hour = su_from_bu(detail::bu::hour);
|
|
/** unit for a 24-hour day **/
|
|
constexpr auto day = su_from_bu(detail::bu::day);
|
|
/** unit for a week comprising exactly 7 24-hour days **/
|
|
constexpr auto week = su_from_bu(detail::bu::week);
|
|
/** unit for a 30-day month **/
|
|
constexpr auto month = su_from_bu(detail::bu::month);
|
|
/** unit for a year containing exactly 365.25 24-hour days **/
|
|
constexpr auto year = su_from_bu(detail::bu::year);
|
|
/** unit for a 'year' containing exactly 250 24-hour days.
|
|
* (approximates the number of business days in a year)
|
|
**/
|
|
constexpr auto year250 = su_from_bu(detail::bu::year250);
|
|
/** unit for a 'year' containing exactly 360 24-hour days **/
|
|
constexpr auto year360 = su_from_bu(detail::bu::year360);
|
|
/** unit for a 'year' containing exactly 365 24-hour days **/
|
|
constexpr auto year365 = su_from_bu(detail::bu::year365);
|
|
|
|
///@}
|
|
|
|
/** @defgroup scaled-unit-misc scaled-unit miscellaneous units **/
|
|
///@{
|
|
|
|
// ----- currency -----
|
|
|
|
/** generic currency unit **/
|
|
constexpr auto currency = su_from_bu(detail::bu::currency);
|
|
|
|
// ----- price - ---
|
|
|
|
/** generic price unit **/
|
|
constexpr auto price = su_from_bu(detail::bu::price);
|
|
|
|
///@}
|
|
|
|
// ----- volatility units -----
|
|
|
|
/** @defgroup scaled-unit-volatility scaled-unit volatility units **/
|
|
///@{
|
|
|
|
/** volatility, in 30-day units **/
|
|
constexpr auto volatility_30d = su_from_bu(detail::bu::month,
|
|
power_ratio_type(-1,2));
|
|
/** volatility, in 250-day 'annual' units **/
|
|
constexpr auto volatility_250d = su_from_bu(detail::bu::year250,
|
|
power_ratio_type(-1,2));
|
|
/** volatility, in 360-day 'annual' units **/
|
|
constexpr auto volatility_360d = su_from_bu(detail::bu::year360,
|
|
power_ratio_type(-1,2));
|
|
/** volatility, in 365-day 'annual' units **/
|
|
constexpr auto volatility_365d = su_from_bu(detail::bu::year365,
|
|
power_ratio_type(-1,2));
|
|
///@}
|
|
}
|
|
|
|
namespace detail {
|
|
template <typename Int,
|
|
typename Int2x = width2x<Int>,
|
|
typename OuterScale = ratio::ratio<Int2x>>
|
|
constexpr
|
|
scaled_unit<Int, OuterScale>
|
|
su_product(const natural_unit<Int> & lhs_bpu_array,
|
|
const natural_unit<Int> & rhs_bpu_array)
|
|
{
|
|
natural_unit<Int2x> prod = lhs_bpu_array.template to_repr<Int2x>();
|
|
|
|
/* accumulate product of scalefactors spun off by rescaling
|
|
* any basis-units in rhs_bpu_array that conflict with the same dimension
|
|
* in lh_bpu_array
|
|
*/
|
|
auto sfr = (detail::outer_scalefactor_result<Int2x>
|
|
(OuterScale(1) /*outer_scale_factor*/,
|
|
1.0 /*outer_scale_sq*/));
|
|
|
|
for (std::size_t i = 0; i < rhs_bpu_array.n_bpu(); ++i) {
|
|
auto sfr2 = nu_product_inplace<Int2x, OuterScale>(&prod,
|
|
rhs_bpu_array[i].template to_repr<Int2x>());
|
|
|
|
sfr.outer_scale_factor_ = sfr.outer_scale_factor_ * sfr2.outer_scale_factor_;
|
|
sfr.outer_scale_sq_ *= sfr2.outer_scale_sq_;
|
|
}
|
|
|
|
return scaled_unit<Int, OuterScale>(prod.template to_repr<Int>(),
|
|
sfr.outer_scale_factor_,
|
|
sfr.outer_scale_sq_);
|
|
}
|
|
|
|
/* use Int2x to accumulate scalefactor
|
|
*/
|
|
template < typename Int,
|
|
typename Int2x = width2x<Int>,
|
|
typename OuterScale = ratio::ratio<Int2x> >
|
|
constexpr
|
|
scaled_unit<Int, OuterScale>
|
|
su_ratio(const natural_unit<Int> & nu_lhs,
|
|
const natural_unit<Int> & nu_rhs)
|
|
{
|
|
natural_unit<Int2x> ratio = nu_lhs.template to_repr<Int2x>();
|
|
|
|
/* accumulate product of scalefactors spun off by rescaling
|
|
* any basis-units in rhs_bpu_array that conflict with the same dimension
|
|
* in lh_bpu_array
|
|
*/
|
|
auto sfr = (detail::outer_scalefactor_result<Int2x, OuterScale>
|
|
(OuterScale(1) /*outer_scale_factor*/,
|
|
1.0 /*outer_scale_sq*/));
|
|
|
|
for (std::size_t i = 0; i < nu_rhs.n_bpu(); ++i) {
|
|
auto sfr2 = nu_ratio_inplace<Int2x, OuterScale>(&ratio,
|
|
nu_rhs[i].template to_repr<Int2x>());
|
|
|
|
/* note: nu_ratio_inplace() reports multiplicative outer scaling factors,
|
|
* so multiply is correct here
|
|
*/
|
|
sfr.outer_scale_factor_ = (sfr.outer_scale_factor_
|
|
* sfr2.outer_scale_factor_);
|
|
sfr.outer_scale_sq_ *= sfr2.outer_scale_sq_;
|
|
}
|
|
|
|
return scaled_unit<Int, OuterScale>(ratio.template to_repr<Int>(),
|
|
sfr.outer_scale_factor_,
|
|
sfr.outer_scale_sq_);
|
|
}
|
|
}
|
|
|
|
/** @defgroup scaled-unit-operators **/
|
|
///@{
|
|
|
|
/** Multiply scaled_unit instances @p x_unit and @p y_unit.
|
|
* Result is a scaled_unit for the product dimension.
|
|
* For each basis dimension, result will prioritize scale from @p x_unit ahead of @p y_unit.
|
|
**/
|
|
template <typename Int,
|
|
typename Int2x = detail::width2x_t<Int>>
|
|
inline constexpr scaled_unit<Int>
|
|
operator* (const scaled_unit<Int> & x_unit,
|
|
const scaled_unit<Int> & y_unit)
|
|
{
|
|
auto rr = detail::su_product<Int, Int2x>(x_unit.natural_unit_,
|
|
y_unit.natural_unit_);
|
|
|
|
return (scaled_unit<Int>
|
|
(rr.natural_unit_,
|
|
(ratio::ratio<Int2x>(rr.outer_scale_factor_)
|
|
* ratio::ratio<Int2x>(x_unit.outer_scale_factor_)
|
|
* ratio::ratio<Int2x>(y_unit.outer_scale_factor_)),
|
|
rr.outer_scale_sq_ * x_unit.outer_scale_sq_ * y_unit.outer_scale_sq_));
|
|
}
|
|
|
|
/** Divide scaled_unit instances @p x_unit by @p y_unit.
|
|
* Result is a scaled_unit for the quotient dimension.
|
|
* For each basis dimension, result will prioritize scale from @p x_unit ahead of @p y_unit.
|
|
**/
|
|
template <typename Int,
|
|
typename Int2x = detail::width2x_t<Int>>
|
|
inline constexpr scaled_unit<Int>
|
|
operator/ (const scaled_unit<Int> & x_unit,
|
|
const scaled_unit<Int> & y_unit)
|
|
{
|
|
auto rr = detail::su_ratio<Int, Int2x>(x_unit.natural_unit_,
|
|
y_unit.natural_unit_);
|
|
|
|
return (scaled_unit<Int>
|
|
(rr.natural_unit_,
|
|
(ratio::ratio<Int2x>(rr.outer_scale_factor_)
|
|
* ratio::ratio<Int2x>(x_unit.outer_scale_factor_)
|
|
* ratio::ratio<Int2x>(y_unit.outer_scale_factor_)),
|
|
rr.outer_scale_sq_ * x_unit.outer_scale_sq_ * y_unit.outer_scale_sq_));
|
|
}
|
|
|
|
///@}
|
|
} /*namespace qty*/
|
|
} /*namespace xo*/
|
|
|
|
/** end scaled_unit.hpp **/
|