/** @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 > struct scaled_unit { using ratio_int_type = typename natural_unit::ratio_int_type; constexpr scaled_unit(const natural_unit & nat_unit, OuterScale outer_scale_factor, double outer_scale_sq) : natural_unit_{nat_unit}, outer_scale_factor_{outer_scale_factor}, outer_scale_sq_{outer_scale_sq} {} constexpr bool is_scaled_unit_type() const { return true; } constexpr scaled_unit reciprocal() const { return scaled_unit(natural_unit_.reciprocal(), 1 / outer_scale_factor_, 1.0 / outer_scale_sq_); } /** @brief true iff scaled unit can be faithfully represented by a @ref natural_unit * * @see natural_unit::is_natural **/ constexpr bool is_natural() const { return (outer_scale_factor_ == OuterScale(1) && (outer_scale_sq_ == 1.0)); } constexpr bool is_dimensionless() const { return natural_unit_.is_dimensionless(); } constexpr std::size_t n_bpu() const { return natural_unit_.n_bpu(); } constexpr bpu & operator[](std::size_t i) { return natural_unit_[i]; } constexpr const bpu & operator[](std::size_t i) const { return natural_unit_[i]; } public: /* need public members so that a scaled_unit instance can be a non-type template parameter (a structural type) */ natural_unit natural_unit_; OuterScale outer_scale_factor_; double outer_scale_sq_; }; namespace detail { template constexpr auto make_unit_rescale_result(const natural_unit & bpuv) { return scaled_unit(bpuv, ratio::ratio(1, 1), 1.0); } } namespace su { constexpr auto picogram = detail::make_unit_rescale_result(nu::picogram); constexpr auto nanogram = detail::make_unit_rescale_result(nu::nanogram); constexpr auto microgram = detail::make_unit_rescale_result(nu::microgram); constexpr auto milligram = detail::make_unit_rescale_result(nu::milligram); constexpr auto gram = detail::make_unit_rescale_result(nu::gram); constexpr auto kilogram = detail::make_unit_rescale_result(nu::kilogram); constexpr auto tonne = detail::make_unit_rescale_result(nu::tonne); constexpr auto kilotonne = detail::make_unit_rescale_result(nu::kilotonne); constexpr auto megatonne = detail::make_unit_rescale_result(nu::megatonne); constexpr auto gigatonne = detail::make_unit_rescale_result(nu::gigatonne); constexpr auto picometer = detail::make_unit_rescale_result(nu::picometer); constexpr auto nanometer = detail::make_unit_rescale_result(nu::nanometer); constexpr auto micrometer = detail::make_unit_rescale_result(nu::micrometer); constexpr auto millimeter = detail::make_unit_rescale_result(nu::millimeter); constexpr auto meter = detail::make_unit_rescale_result(nu::meter); constexpr auto kilometer = detail::make_unit_rescale_result(nu::kilometer); constexpr auto megameter = detail::make_unit_rescale_result(nu::megameter); constexpr auto gigameter = detail::make_unit_rescale_result(nu::gigameter); constexpr auto lightsecond = detail::make_unit_rescale_result(nu::lightsecond); constexpr auto astronomicalunit = detail::make_unit_rescale_result(nu::astronomicalunit); constexpr auto inch = detail::make_unit_rescale_result(nu::inch); constexpr auto foot = detail::make_unit_rescale_result(nu::foot); constexpr auto yard = detail::make_unit_rescale_result(nu::yard); constexpr auto mile = detail::make_unit_rescale_result(nu::mile); constexpr auto picosecond = detail::make_unit_rescale_result(nu::picosecond); constexpr auto nanosecond = detail::make_unit_rescale_result(nu::nanosecond); constexpr auto microsecond = detail::make_unit_rescale_result(nu::microsecond); constexpr auto millisecond = detail::make_unit_rescale_result(nu::millisecond); constexpr auto second = detail::make_unit_rescale_result(nu::second); constexpr auto minute = detail::make_unit_rescale_result(nu::minute); constexpr auto hour = detail::make_unit_rescale_result(nu::hour); constexpr auto day = detail::make_unit_rescale_result(nu::day); constexpr auto week = detail::make_unit_rescale_result(nu::week); constexpr auto month = detail::make_unit_rescale_result(nu::month); constexpr auto year = detail::make_unit_rescale_result(nu::year); constexpr auto year250 = detail::make_unit_rescale_result(nu::year250); constexpr auto year360 = detail::make_unit_rescale_result(nu::year360); constexpr auto year365 = detail::make_unit_rescale_result(nu::year365); constexpr auto volatility_30d = detail::make_unit_rescale_result(nu::volatility_30d); constexpr auto volatility_250d = detail::make_unit_rescale_result(nu::volatility_250d); constexpr auto volatility_360d = detail::make_unit_rescale_result(nu::volatility_360d); constexpr auto volatility_365d = detail::make_unit_rescale_result(nu::volatility_365d); } namespace detail { template , typename OuterScale = ratio::ratio> constexpr scaled_unit su_product(const natural_unit & lhs_bpu_array, const natural_unit & rhs_bpu_array) { natural_unit prod = lhs_bpu_array.template to_repr(); /* 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 (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(&prod, rhs_bpu_array[i].template to_repr()); sfr.outer_scale_factor_ = sfr.outer_scale_factor_ * sfr2.outer_scale_factor_; sfr.outer_scale_sq_ *= sfr2.outer_scale_sq_; } return scaled_unit(prod.template to_repr(), sfr.outer_scale_factor_, sfr.outer_scale_sq_); } /* use Int2x to accumulate scalefactor */ template < typename Int, typename Int2x = width2x, typename OuterScale = ratio::ratio > constexpr scaled_unit su_ratio(const natural_unit & nu_lhs, const natural_unit & nu_rhs) { natural_unit ratio = nu_lhs.template to_repr(); /* 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 (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(&ratio, nu_rhs[i].template to_repr()); /* 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(ratio.template to_repr(), sfr.outer_scale_factor_, sfr.outer_scale_sq_); } } template > inline constexpr scaled_unit operator* (const scaled_unit & x_unit, const scaled_unit & y_unit) { auto rr = detail::su_product(x_unit.natural_unit_, y_unit.natural_unit_); return (scaled_unit (rr.natural_unit_, (ratio::ratio(rr.outer_scale_factor_) * ratio::ratio(x_unit.outer_scale_factor_) * ratio::ratio(y_unit.outer_scale_factor_)), rr.outer_scale_sq_ * x_unit.outer_scale_sq_ * y_unit.outer_scale_sq_)); } template > inline constexpr scaled_unit operator* (const scaled_unit & x_unit, const natural_unit & y_unit) { auto y_unit2 = detail::make_unit_rescale_result(y_unit); return x_unit * y_unit2; } template > inline constexpr scaled_unit operator* (const natural_unit & x_unit, const scaled_unit & y_unit) { auto x_unit2 = detail::make_unit_rescale_result(x_unit); return x_unit2 * y_unit; } template > inline constexpr scaled_unit operator/ (const scaled_unit & x_unit, const scaled_unit & y_unit) { auto rr = detail::su_ratio(x_unit.natural_unit_, y_unit.natural_unit_); return (scaled_unit (rr.natural_unit_, (ratio::ratio(rr.outer_scale_factor_) * ratio::ratio(x_unit.outer_scale_factor_) * ratio::ratio(y_unit.outer_scale_factor_)), rr.outer_scale_sq_ * x_unit.outer_scale_sq_ * y_unit.outer_scale_sq_)); } template > inline constexpr scaled_unit operator/ (const scaled_unit & x_unit, const natural_unit & y_unit) { auto y_unit2 = detail::make_unit_rescale_result(y_unit); return x_unit / y_unit2; } template > inline constexpr scaled_unit operator/ (const natural_unit & x_unit, const scaled_unit & y_unit) { auto x_unit2 = detail::make_unit_rescale_result(x_unit); return x_unit2 / y_unit; } } /*namespace qty*/ } /*namespace xo*/ /** end scaled_unit.hpp **/