/** @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 { /** @defgroup scaled-unit-type-traits scaled-unit type traits **/ ///@{ /** type for representing individual basis-unit scalefactors **/ using ratio_int_type = typename natural_unit::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 & 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 lookup_dim(dimension d) const { return natural_unit_.lookup_dim(d); } /** return @p i'th bpu associated with this unit **/ constexpr bpu & operator[](std::size_t i) { return natural_unit_[i]; } /** return @p i'th bpu associated with this unit (const version) **/ constexpr const bpu & 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 natural_unit_; ///@} }; // TODO: comparison operators namespace detail { /** promote natural unit to scaled unit (with unit outer scalefactors) **/ template constexpr auto su_promote(const natural_unit & bpuv) { return scaled_unit(bpuv, ratio::ratio(1, 1), 1.0); } } namespace u { /* values here can be used as template arguments to quantity: * e.g. * quantity qty1; * quantity 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::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()); ///@} // ----- 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 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_); } } /** @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 > 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_)); } /** 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 > 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_)); } ///@} } /*namespace qty*/ } /*namespace xo*/ /** end scaled_unit.hpp **/