/** @file quantity.hpp * * Author: Roland Conybeare **/ #pragma once #include "quantity_ops.hpp" #include "natural_unit.hpp" #include "scaled_unit.hpp" namespace xo { namespace qty { /** @class quantity * @brief represent a scalar quantity with associated units. * * Enforce dimensional consistency at compile time. * sizeof(quantity) == sizeof(Repr). **/ template < typename Repr = double, typename Int = std::int64_t, natural_unit NaturalUnit = natural_unit(), typename Int2x = detail::width2x_t > class quantity { public: using repr_type = Repr; using unit_type = natural_unit; using ratio_int_type = Int; using ratio_int2x_type = Int2x; public: constexpr quantity() : scale_{0} {} explicit constexpr quantity(Repr scale) : scale_{scale} {} static constexpr bool always_constexpr_unit = true; constexpr const repr_type & scale() const { return scale_; } constexpr const unit_type & unit() const { return s_unit; } // is_dimensionless // unit_qty // zero_qty // reciprocal template constexpr auto with_repr() const { return quantity(scale_); } /* parallel implementation to Quantity::rescale(), * except that NaturalUnit2 is a compile-time-only template-argument * * NOTE: constexpr as long as no fractional units involved. */ template NaturalUnit2> constexpr auto rescale() const { /* conversion factor from .unit -> unit2*/ auto rr = detail::su_ratio(NaturalUnit, NaturalUnit2); if (rr.natural_unit_.is_dimensionless()) { repr_type r_scale = (((rr.outer_scale_sq_ == 1.0) ? 1.0 : ::sqrt(rr.outer_scale_sq_)) * rr.outer_scale_factor_.template convert_to() * this->scale_); return quantity(r_scale); } else { return quantity(std::numeric_limits::quiet_NaN()); } } template ScaledUnit2> constexpr auto rescale_ext() const { /* conversion factor from .unit -> unit2*/ auto rr = detail::su_ratio(NaturalUnit, ScaledUnit2.natural_unit_); if (rr.natural_unit_.is_dimensionless()) { /* NOTE: test for unit .outer_scale_sq values to get constexpr result with c++23 * and integer dimension powers. */ repr_type r_scale = ((((rr.outer_scale_sq_ == 1.0) && (ScaledUnit2.outer_scale_sq_ == 1.0)) ? 1.0 : ::sqrt(rr.outer_scale_sq_ / ScaledUnit2.outer_scale_sq_)) * rr.outer_scale_factor_.template convert_to() * this->scale_ / ScaledUnit2.outer_scale_factor_.template convert_to()); return quantity(r_scale); } else { return quantity(std::numeric_limits::quiet_NaN()); } } template requires std::is_arithmetic_v constexpr auto scale_by(Dimensionless x) const { return quantity(x * this->scale_); } // divide_by // divide_into // divide // add // subtract /* parallel implementation to Quantity */ template static constexpr auto compare(const quantity &x, const Quantity2 & y) { quantity y2 = y.template rescale(); return x.scale() <=> y2.scale(); } // operator- // operator+= // operator-= // operator*= // operator/= constexpr nu_abbrev_type abbrev() const { return s_unit.abbrev(); } quantity & operator=(const quantity & x) { this->scale_ = x.scale_; return *this; } template requires(quantity_concept && Q2::always_constexpr_unit) quantity & operator=(const Q2 & x) { auto x2 = x.template rescale(); this->scale_ = x2.scale(); return *this; } template requires(quantity_concept && Q2::always_constexpr_unit) constexpr operator Q2() const { return this->template rescale().template with_repr(); } public: /* need public members so that a quantity instance can be a non-type template parameter (is a structural type) */ static constexpr natural_unit s_unit = NaturalUnit; Repr scale_ = Repr{}; }; template NaturalUnit = natural_unit()> using stdquantity = quantity; template constexpr auto rescale(const Quantity & x, const scaled_unit & su) { return x.template rescale(); } namespace detail { struct quantity_util { /* parallel implementation to Quantity multiply, * but return type will have dimension computed at compile-time */ template requires (quantity_concept && quantity_concept && Q1::always_constexpr_unit && Q2::always_constexpr_unit) static constexpr auto multiply(Q1 x, Q2 y) { using r_repr_type = std::common_type_t; using r_int_type = std::common_type_t; using r_int2x_type = std::common_type_t; constexpr auto rr = detail::su_product(x.unit(), y.unit()); r_repr_type r_scale = (((rr.outer_scale_sq_ == 1.0) ? 1.0 : ::sqrt(rr.outer_scale_sq_)) * rr.outer_scale_factor_.template convert_to() * static_cast(x.scale()) * static_cast(y.scale())); return quantity(r_scale); } template requires (quantity_concept && quantity_concept && Q1::always_constexpr_unit && Q2::always_constexpr_unit) static constexpr auto divide(Q1 x, Q2 y) { using r_repr_type = std::common_type_t; using r_int_type = std::common_type_t; using r_int2x_type = std::common_type_t; constexpr auto rr = detail::su_ratio(x.unit(), y.unit()); r_repr_type r_scale = (((rr.outer_scale_sq_ == 1.0) ? 1.0 : ::sqrt(rr.outer_scale_sq_)) * rr.outer_scale_factor_.template convert_to() * static_cast(x.scale()) / static_cast(y.scale())); return quantity(r_scale); } }; } /*namespace detail*/ template Unit = Q2::s_unit> requires (quantity_concept && quantity_concept && Q1::always_constexpr_unit && Q2::always_constexpr_unit) constexpr auto with_units_from(const Q1 & x, const Q2 & y) { return x.template rescale(); } template requires (quantity_concept && Q1::always_constexpr_unit) constexpr auto with_repr(const Q1 & x) { return x.template with_repr(); } /** note: won't have constexpr result w/ fractional dimension until c++26 (when ::sqrt(), ::pow() are constexpr) **/ template requires (quantity_concept && quantity_concept && Q1::always_constexpr_unit && Q2::always_constexpr_unit) constexpr auto operator* (const Q1 & x, const Q2 & y) { return detail::quantity_util::multiply(x, y); } /** note: won't have constexpr result w/ fractional dimension until c++26 (when ::sqrt(), ::pow() are constexpr) **/ template requires (quantity_concept && quantity_concept && Q1::always_constexpr_unit && Q2::always_constexpr_unit) constexpr auto operator/ (const Q1 & x, const Q2 & y) { return detail::quantity_util::divide(x, y); } namespace qty { // ----- mass ----- inline constexpr auto picograms(double x) { return quantity(x); } inline constexpr auto nanograms(double x) { return quantity(x); } inline constexpr auto micrograms(double x) { return quantity(x); } inline constexpr auto milligrams(double x) { return quantity(x); } inline constexpr auto grams(double x) { return quantity(x); } inline constexpr auto kilograms(double x) { return quantity(x); } inline constexpr auto tonnes(double x) { return quantity(x); } inline constexpr auto kilotonnes(double x) { return quantity(x); } inline constexpr auto megatonnes(double x) { return quantity(x); } inline constexpr auto gigatonnes(double x) { return quantity(x); } // ----- distance ----- inline constexpr auto picometers(double x) { return quantity(x); } inline constexpr auto nanometers(double x) { return quantity(x); } inline constexpr auto micrometers(double x) { return quantity(x); } inline constexpr auto millimeters(double x) { return quantity(x); } inline constexpr auto meters(double x) { return quantity(x); } inline constexpr auto kilometers(double x) { return quantity(x); } inline constexpr auto megameters(double x) { return quantity(x); } inline constexpr auto gigameters(double x) { return quantity(x); } inline constexpr auto lightseconds(double x) { return quantity(x); } inline constexpr auto astronomicalunits(double x) { return quantity(x); } static constexpr auto meter = meters(1); // ----- time ----- inline constexpr auto picoseconds(double x) { return quantity(x); } inline constexpr auto nanoseconds(double x) { return quantity(x); } inline constexpr auto microseconds(double x) { return quantity(x); } inline constexpr auto milliseconds(double x) { return quantity(x); } template inline constexpr auto seconds(Repr x) { return quantity(x); } template inline constexpr auto minutes(Repr x) { return quantity(x); } inline constexpr auto hours(double x) { return quantity(x); } inline constexpr auto days(double x) { return quantity(x); } inline constexpr auto weeks(double x) { return quantity(x); } inline constexpr auto months(double x) { return quantity(x); } inline constexpr auto years(double x) { return quantity(x); } inline constexpr auto year250s(double x) { return quantity(x); } inline constexpr auto year360s(double x) { return quantity(x); } inline constexpr auto year365s(double x) { return quantity(x); } //inline constexpr auto year366s(double x) { return quantity(x); } static constexpr auto second = seconds(1); // ----- volatility ----- /* volatility in units of 1/yr */ inline constexpr auto volatility_250d(double x) { return quantity(x); } inline constexpr auto volatility_360d(double x) { return quantity(x); } } /* reminder: see [quantity_ops.hpp] for operator* etc */ } /*namespace qty*/ } /*namespace xo*/ /** end quantity.hpp **/