LCOV - code coverage report
Current view: top level - include/xo/unit - Quantity.hpp (source / functions) Hit Total Coverage
Test: out3.info Lines: 56 59 94.9 %
Date: 1980-01-01 00:00:00 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /** @file Quantity.hpp
       2             :  *
       3             :  *  Author: Roland Conybeare
       4             :  **/
       5             : 
       6             : #pragma once
       7             : 
       8             : #include "quantity2_concept.hpp"
       9             : #include "scaled_unit.hpp"
      10             : #include "natural_unit.hpp"
      11             : 
      12             : namespace xo {
      13             :     namespace qty {
      14             :         /** @class quantity
      15             :          *  @brief represent a scalar quantity with attached units.  enforce dimensional consistency.
      16             :          *
      17             :          *  Constexpr implementation,  but units are explicitly represented:
      18             :          *  sizeof(Quantity2) > sizeof(Repr)
      19             :          *
      20             :          *  Explicit unit representation allows introducing units at runtime,
      21             :          *  for example in python bindings
      22             :          *
      23             :          *  Require:
      24             :          *  - Repr supports numeric operations (+, -, *, /)
      25             :          *  - Repr supports conversion from double.
      26             :          **/
      27             :         template <typename Repr = double,
      28             :                   typename Int = std::int64_t>
      29             :         class Quantity {
      30             :         public:
      31             :             using repr_type = Repr;
      32             :             using unit_type = natural_unit<Int>;
      33             :             using ratio_int_type = Int;
      34             : 
      35             :         public:
      36          22 :             constexpr Quantity(Repr scale,
      37             :                                const natural_unit<Int> & unit)
      38          22 :                 : scale_{scale}, unit_{unit} {}
      39             : 
      40          22 :             constexpr const repr_type & scale() const { return scale_; }
      41          11 :             constexpr const unit_type & unit() const { return unit_; }
      42             : 
      43             :             constexpr bool is_dimensionless() const { return unit_.is_dimensionless(); }
      44             : 
      45             :             constexpr Quantity unit_qty() const { return Quantity(1, unit_); }
      46             :             constexpr Quantity reciprocal() const { return Quantity(1.0 / scale_, unit_.reciprocal()); }
      47             : 
      48             :             constexpr
      49          12 :             auto rescale(const natural_unit<Int> & unit2) const {
      50             :                 /* conversion factor from .unit -> unit2*/
      51          12 :                 auto rr = detail::nu_ratio(this->unit_, unit2);
      52             : 
      53          12 :                 if (rr.natural_unit_.is_dimensionless()) {
      54          12 :                     repr_type r_scale = (::sqrt(rr.outer_scale_sq_)
      55          12 :                                          * rr.outer_scale_exact_.template to<repr_type>()
      56          12 :                                          * this->scale_);
      57          12 :                     return Quantity(r_scale, unit2);
      58             :                 } else {
      59           0 :                     return Quantity(std::numeric_limits<repr_type>::quiet_NaN(), unit2);
      60             :                 }
      61             :             }
      62             : 
      63             :             template <typename Dimensionless>
      64             :             requires std::is_arithmetic_v<Dimensionless>
      65             :             constexpr auto scale_by(Dimensionless x) const {
      66             :                 return Quantity(x * this->scale_, this->unit_);
      67             :             }
      68             : 
      69             :             template <typename Dimensionless>
      70             :             requires std::is_arithmetic_v<Dimensionless>
      71             :             constexpr auto divide_by(Dimensionless x) const {
      72             :                 return Quantity(this->scale_ / x, this->unit_);
      73             :             }
      74             : 
      75             :             template <typename Dimensionless>
      76             :             requires std::is_arithmetic_v<Dimensionless>
      77             :             constexpr auto divide_into(Dimensionless x) const {
      78             :                 return Quantity(x / this->scale_, this->unit_.reciprocal());
      79             :             }
      80             : 
      81             :             template <typename Quantity2>
      82             :             static constexpr
      83           3 :             auto multiply(const Quantity & x, const Quantity2 & y) {
      84             :                 using r_repr_type = std::common_type_t<typename Quantity::repr_type,
      85             :                                                        typename Quantity2::repr_type>;
      86             :                 using r_int_type = std::common_type_t<typename Quantity::ratio_int_type,
      87             :                                                       typename Quantity2::ratio_int_type>;
      88             : 
      89           3 :                 auto rr = detail::nu_product(x.unit(), y.unit());
      90             : 
      91           3 :                 r_repr_type r_scale = (::sqrt(rr.outer_scale_sq_)
      92           3 :                                        * rr.outer_scale_exact_.template to<r_repr_type>()
      93           3 :                                        * static_cast<r_repr_type>(x.scale())
      94           3 :                                        * static_cast<r_repr_type>(y.scale()));
      95             : 
      96             :                 return Quantity<r_repr_type, r_int_type>(r_scale,
      97           3 :                                                          rr.natural_unit_);
      98             :             }
      99             : 
     100             :             template <typename Quantity2>
     101             :             static constexpr
     102           3 :             auto divide(const Quantity & x, const Quantity2 & y) {
     103             :                 using r_repr_type = std::common_type_t<typename Quantity::repr_type,
     104             :                                                        typename Quantity2::repr_type>;
     105             :                 using r_int_type = std::common_type_t<typename Quantity::ratio_int_type,
     106             :                                                       typename Quantity2::ratio_int_type>;
     107             : 
     108           3 :                 auto rr = detail::nu_ratio(x.unit(), y.unit());
     109             : 
     110             :                 /* note: nu_ratio() reports multiplicative outer scaling factors,
     111             :                  *       so multiply is correct here
     112             :                  */
     113           3 :                 r_repr_type r_scale = (::sqrt(rr.outer_scale_sq_)
     114           3 :                                        * rr.outer_scale_exact_.template to<r_repr_type>()
     115           3 :                                        * static_cast<r_repr_type>(x.scale())
     116           3 :                                        / static_cast<r_repr_type>(y.scale()));
     117             : 
     118             :                 return Quantity<r_repr_type, r_int_type>(r_scale,
     119           3 :                                                          rr.natural_unit_);
     120             :             }
     121             : 
     122             :             template <typename Quantity2>
     123             :             static constexpr
     124           2 :             auto add(const Quantity & x, const Quantity2 & y) {
     125             :                 using r_repr_type = std::common_type_t<typename Quantity::repr_type,
     126             :                                                        typename Quantity2::repr_type>;
     127             :                 using r_int_type = std::common_type_t<typename Quantity::ratio_int_type,
     128             :                                                       typename Quantity2::ratio_int_type>;
     129             : 
     130             :                 /* conversion to get y in same units as x:  multiply by y/x */
     131           2 :                 auto rr = detail::nu_ratio(y.unit(), x.unit());
     132             : 
     133           2 :                 if (rr.natural_unit_.is_dimensionless()) {
     134           2 :                     r_repr_type r_scale = (static_cast<r_repr_type>(x.scale())
     135           2 :                                            + (::sqrt(rr.outer_scale_sq_)
     136           2 :                                               * rr.outer_scale_exact_.template to<r_repr_type>()
     137           2 :                                               * static_cast<r_repr_type>(y.scale())));
     138             : 
     139           2 :                     return Quantity<r_repr_type, r_int_type>(r_scale, x.unit_.template to_repr<r_int_type>());
     140             :                 } else {
     141             :                     /* units don't match! */
     142             :                     return Quantity<r_repr_type, r_int_type>(std::numeric_limits<Repr>::quiet_NaN(),
     143           0 :                                                              x.unit_.template to_repr<r_int_type>());
     144             :                 }
     145             :             }
     146             : 
     147             :             template <typename Quantity2>
     148             :             static constexpr
     149           2 :             auto subtract(const Quantity & x, const Quantity2 & y) {
     150             :                 using r_repr_type = std::common_type_t<typename Quantity::repr_type,
     151             :                                                        typename Quantity2::repr_type>;
     152             :                 using r_int_type = std::common_type_t<typename Quantity::ratio_int_type,
     153             :                                                       typename Quantity2::ratio_int_type>;
     154             : 
     155             :                 /* conversion to get y in same units as x:  multiply by y/x */
     156           2 :                 auto rr = detail::nu_ratio(y.unit(), x.unit());
     157             : 
     158           2 :                 if (rr.natural_unit_.is_dimensionless()) {
     159           2 :                     r_repr_type r_scale = (static_cast<r_repr_type>(x.scale())
     160           2 :                                            - (::sqrt(rr.outer_scale_sq_)
     161           2 :                                               * rr.outer_scale_exact_.template to<r_repr_type>()
     162           2 :                                               * static_cast<r_repr_type>(y.scale())));
     163             : 
     164           2 :                     return Quantity<r_repr_type, r_int_type>(r_scale, x.unit_.template to_repr<r_int_type>());
     165             :                 } else {
     166             :                     /* units don't match! */
     167             :                     return Quantity<r_repr_type, r_int_type>(std::numeric_limits<Repr>::quiet_NaN(),
     168           0 :                                                              x.unit_.template to_repr<r_int_type>());
     169             :                 }
     170             :             }
     171             : 
     172             :             template <typename Quantity2>
     173             :             static constexpr
     174          12 :             auto compare(const Quantity & x, const Quantity2 & y) {
     175          12 :                 Quantity y2 = y.rescale(x.unit_);
     176             : 
     177          12 :                 return x.scale() <=> y2.scale();
     178             :             }
     179             : 
     180             :         private:
     181             :             /** @brief quantity represents this multiple of a unit amount **/
     182             :             Repr scale_ = Repr{};
     183             :             /** @brief unit for this quantity **/
     184             :             natural_unit<Int> unit_;
     185             :         }; /*Quantity2*/
     186             : 
     187             :         /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
     188             :          **/
     189             :         template <typename Repr = double,
     190             :                   typename Int = std::int64_t>
     191             :         inline constexpr Quantity<Repr, Int>
     192             :         unit_qty(const scaled_unit<Int> & u) {
     193             :             return Quantity<Repr, Int>
     194             :                 (u.outer_scale_exact_.template to<double>() * ::sqrt(u.outer_scale_sq_),
     195             :                  u.natural_unit_);
     196             :         }
     197             : 
     198             :         /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
     199             :          **/
     200             :         template <typename Repr = double,
     201             :                   typename Int = std::int64_t>
     202             :         inline constexpr Quantity<Repr, Int>
     203             :         natural_unit_qty(const natural_unit<Int> & nu) {
     204             :             return Quantity<Repr, Int>(1.0, nu);
     205             :         }
     206             : 
     207             :         /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
     208             :          **/
     209             :         template <typename Quantity, typename Quantity2>
     210             :         requires quantity2_concept<Quantity> && quantity2_concept<Quantity2>
     211             :         constexpr auto
     212           3 :         operator* (const Quantity & x, const Quantity2 & y)
     213             :         {
     214           3 :             return Quantity::multiply(x, y);
     215             :         }
     216             : 
     217             :         /** note: does not require unit scaling,  so constexpr with c++23 **/
     218             :         template <typename Dimensionless, typename Quantity>
     219             :         requires std::is_arithmetic_v<Dimensionless> && quantity2_concept<Quantity>
     220             :         constexpr auto
     221             :         operator* (Dimensionless x, const Quantity & y)
     222             :         {
     223             :             return y.scale_by(x);
     224             :         }
     225             : 
     226             :         /** note: does not require unit scaling,  so constexpr with c++23 **/
     227             :         template <typename Dimensionless, typename Quantity>
     228             :         requires std::is_arithmetic_v<Dimensionless> && quantity2_concept<Quantity>
     229             :         constexpr auto
     230             :         operator* (const Quantity & x, Dimensionless y)
     231             :         {
     232             :             return x.scale_by(y);
     233             :         }
     234             : 
     235             :         /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
     236             :          **/
     237             :         template <typename Quantity, typename Quantity2>
     238             :         requires quantity2_concept<Quantity> && quantity2_concept<Quantity2>
     239             :         constexpr auto
     240           3 :         operator/ (const Quantity & x, const Quantity2 & y)
     241             :         {
     242           3 :             return Quantity::divide(x, y);
     243             :         }
     244             : 
     245             :         /** note: doesn not require unit scaling, so constexpr with c++23 **/
     246             :         template <typename Quantity, typename Dimensionless>
     247             :         requires quantity2_concept<Quantity> && std::is_arithmetic_v<Dimensionless>
     248             :         constexpr auto
     249             :         operator/ (const Quantity & x, Dimensionless y)
     250             :         {
     251             :             return x.divide_by(y);
     252             :         }
     253             : 
     254             :         /** note: doesn not require unit scaling, so constexpr with c++23 **/
     255             :         template <typename Dimensionless, typename Quantity>
     256             :         requires std::is_arithmetic_v<Dimensionless> && quantity2_concept<Quantity>
     257             :         constexpr auto
     258             :         operator/ (Dimensionless x, const Quantity & y)
     259             :         {
     260             :             return y.divide_into(x);
     261             :         }
     262             : 
     263             :         /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
     264             :          **/
     265             :         template <typename Quantity, typename Quantity2>
     266             :         requires quantity2_concept<Quantity> && quantity2_concept<Quantity2>
     267             :         constexpr auto
     268           2 :         operator+ (const Quantity & x, const Quantity2 & y)
     269             :         {
     270           2 :             return Quantity::add(x, y);
     271             :         }
     272             : 
     273             :         /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
     274             :          **/
     275             :         template <typename Quantity, typename Quantity2>
     276             :         requires quantity2_concept<Quantity> && quantity2_concept<Quantity2>
     277             :         constexpr auto
     278           2 :         operator- (const Quantity & x, const Quantity2 & y)
     279             :         {
     280           2 :             return Quantity::subtract(x, y);
     281             :         }
     282             : 
     283             :         /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
     284             :          **/
     285             :         template <typename Quantity, typename Quantity2>
     286             :         requires quantity2_concept<Quantity> && quantity2_concept<Quantity2>
     287             :         constexpr auto
     288           4 :         operator== (const Quantity & x, const Quantity2 & y)
     289             :         {
     290           4 :             return (Quantity::compare(x, y) == 0);
     291             :         }
     292             : 
     293             :         /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
     294             :          **/
     295             :         template <typename Quantity, typename Quantity2>
     296             :         requires quantity2_concept<Quantity> && quantity2_concept<Quantity2>
     297             :         constexpr auto
     298           8 :         operator<=> (const Quantity & x, const Quantity2 & y)
     299             :         {
     300           8 :             return Quantity::compare(x, y);
     301             :         }
     302             : 
     303             :         namespace unit {
     304             :             constexpr auto nanogram = natural_unit_qty(nu2::nanogram);
     305             :         }
     306             :     } /*namespace qty*/
     307             : } /*namespace xo*/
     308             : 
     309             : /** end Quantity.hpp **/

Generated by: LCOV version 1.0