LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
quantities.h
Go to the documentation of this file.
1 
29 #ifndef LARDATAALG_UTILITIES_QUANTITIES_H
30 #define LARDATAALG_UTILITIES_QUANTITIES_H
31 
32 // LArSoft libraries
33 #include "larcorealg/CoreUtils/MetaUtils.h" // util::always_true_v<>
34 #include "larcorealg/CoreUtils/StdUtils.h" // util::to_string(), ...
35 #include "lardataalg/Utilities/constexpr_math.h" // util::abs()
36 
37 // Boost libraries
38 #include "boost/integer/common_factor_rt.hpp" // boost::integer::gcd()
39 
40 // C/C++ standard libraries
41 #include <cctype> // std::isblank()
42 #include <functional> // std::hash<>
43 #include <limits>
44 #include <map>
45 #include <ostream>
46 #include <ratio>
47 #include <regex>
48 #include <string>
49 #include <string_view>
50 #include <type_traits> // std::is_same<>, std::enable_if_t<>, ...
51 
217 namespace util::quantities {
218 
226  namespace concepts {
227 
228  namespace details {
229 
230  //------------------------------------------------------------------------
231  //--- Ratio metaprogramming
232  //------------------------------------------------------------------------
234  template <typename Ratio, typename Value>
235  static constexpr auto applyRatioToValue(Value&& v)
236  {
237  return v * Ratio::num / Ratio::den;
238  }
239 
240  template <typename R>
241  struct invert_ratio;
242 
243  template <typename R>
244  using invert_t = typename invert_ratio<R>::type;
245 
246  template <typename R>
248 
249  template <typename R>
251 
252  //------------------------------------------------------------------------
253  //--- Unit-related
254  //------------------------------------------------------------------------
256  template <typename U>
257  struct has_unit;
258 
260  template <typename U>
261  constexpr bool has_unit_v = has_unit<U>();
262 
264  template <typename Q>
265  struct is_quantity;
266 
268  template <typename Q>
269  constexpr bool is_quantity_v = is_quantity<Q>();
270 
272  template <typename Q>
273  struct has_quantity;
274 
276  template <typename Q>
277  constexpr bool has_quantity_v = has_quantity<Q>();
278 
279  //------------------------------------------------------------------------
282  template <typename T>
284 
287  template <typename T>
289 
294  template <typename T, typename Q>
296 
298  template <typename T, typename Q>
300 
309  template <typename T, typename U>
311 
313  template <typename T, typename U>
315 
316  //------------------------------------------------------------------------
317  //--- constexpr string concatenation
318  //------------------------------------------------------------------------
319  // giving up for now; Boost 1.68.0 offers some help, but we don;t use it
320  // yet
321 
322  //------------------------------------------------------------------------
323  template <typename Q>
325 
326  //------------------------------------------------------------------------
327 
328  } // namespace details
329 
330  using namespace std::string_view_literals;
331 
332  //------------------------------------------------------------------------
334  template <typename ARatio, typename BRatio>
336 
338  template <typename NumRatio, typename DenRatio>
340 
341  //------------------------------------------------------------------------
359  struct UnitBase {
361  static constexpr std::string_view symbol = "?"sv;
363  static constexpr std::string_view name = "unknown"sv;
364  }; // struct UnitBase
365 
366  template <typename R>
367  struct Prefix {
368  using ratio = R;
369 
371  static constexpr auto names(bool Long = false);
372 
374  static constexpr auto symbol() { return names(false); }
375 
377  static constexpr auto name() { return names(true); }
378 
379  }; // struct Prefix
380 
381  template <typename U, typename R = std::ratio<1>>
382  struct ScaledUnit {
383 
384  using baseunit_t = U;
386  using ratio = R;
388 
389  // -- BEGIN Conversion to string -----------------------------------------
397 
400  static auto symbol() { return std::string(prefix().symbol()) + baseUnit().symbol.data(); }
401 
404  static auto name() { return std::string(prefix().name()) + baseUnit().name.data(); }
405 
407  // -- END Conversion to string -------------------------------------------
408 
409  // -- BEGIN Representation conversions -----------------------------------
412 
414  template <typename T>
415  static constexpr T scale(T v)
416  {
417  return details::applyRatioToValue<details::invert_t<ratio>>(v);
418  }
419 
421  template <typename T>
422  static constexpr T unscale(T v)
423  {
424  return details::applyRatioToValue<ratio>(v);
425  }
426 
428  template <typename TargetRatio, typename T>
429  static constexpr T scaleTo(T v)
430  {
431  return details::applyRatioToValue<simplified_ratio_divide<ratio, TargetRatio>>(v);
432  }
433 
435  template <typename TargetRatio, typename T>
436  static constexpr T fromRepr(T v)
437  {
438  return details::applyRatioToValue<simplified_ratio_divide<TargetRatio, ratio>>(v);
439  }
440 
442  // -- END Representation conversions -------------------------------------
443 
444  // -- BEGIN Type features ------------------------------------------------
447 
449  static constexpr prefix_t prefix() { return {}; }
450 
452  static constexpr baseunit_t baseUnit() { return {}; }
453 
455  static constexpr unit_t unit() { return {}; }
456 
458  template <typename OU>
459  static constexpr bool sameBaseUnitAs()
460  {
461  return std::is_same<baseunit_t, typename OU::baseunit_t>();
462  }
463 
465  template <typename OU>
466  static constexpr bool sameUnitAs()
467  {
468  return std::is_same<unit_t, typename OU::unit_t>();
469  }
470 
472  // -- END Type features --------------------------------------------------
473 
474  }; // ScaledUnit<>
475 
476  template <typename U, typename R>
477  std::ostream& operator<<(std::ostream& out, ScaledUnit<U, R> const& unit)
478  {
479  using unit_t = ScaledUnit<U, R>;
480  return out << unit_t::prefix_t::symbol() << unit_t::baseunit_t::symbol;
481  }
482 
553  template <typename Unit, typename T = double>
554  struct Quantity {
555 
556  public:
557  using value_t = T;
558  using unit_t = Unit;
560 
562  using baseunit_t = typename unit_t::baseunit_t;
563 
565  // NOTE: this is not `constexpr` because using it n a constexpr would
566  // yield an uninitialized constant
567  explicit Quantity() = default;
568 
570  explicit constexpr Quantity(value_t v) : fValue(v) {}
571 
581  template <typename Q, typename std::enable_if_t<details::is_quantity_v<Q>>* = nullptr>
582  constexpr Quantity(Q q)
583  : fValue{unit_t::template fromRepr<typename Q::unit_t::ratio>(q.value())}
584  {
585  static_assert(sameBaseUnitAs<Q>(),
586  "Can't construct from quantity with different base unit");
587  }
588 
590  constexpr value_t value() const { return fValue; }
591 
593  explicit constexpr operator value_t() const { return value(); }
594 
595  // -- BEGIN Asymmetric arithmetic operations -----------------------------
625 
628  template <typename OU, typename OT>
629  constexpr quantity_t operator+(Quantity<OU, OT> const other) const;
630 
633  template <typename OU, typename OT>
634  constexpr quantity_t operator-(Quantity<OU, OT> const other) const;
635 
637  template <typename OU, typename OT>
638  constexpr quantity_t plus(Quantity<OU, OT> const other) const;
639 
641  template <typename OU, typename OT>
642  constexpr quantity_t minus(Quantity<OU, OT> const other) const;
643 
645  template <typename OU, typename OT>
646  constexpr value_t operator/(Quantity<OU, OT> q) const;
647 
649  template <typename OU, typename OT>
650  quantity_t& operator+=(Quantity<OU, OT> const other);
651 
653  template <typename OU, typename OT>
654  quantity_t& operator-=(Quantity<OU, OT> const other);
655 
657  template <typename OT>
658  std::enable_if_t<std::is_arithmetic_v<OT>, quantity_t&> operator*=(OT factor)
659  {
660  fValue *= factor;
661  return *this;
662  }
663 
665  template <typename OT>
666  std::enable_if_t<std::is_arithmetic_v<OT>, quantity_t&> operator/=(OT quot)
667  {
668  fValue /= quot;
669  return *this;
670  }
671 
673  constexpr quantity_t operator+() const { return quantity_t(value()); }
674 
676  constexpr quantity_t operator-() const { return quantity_t(-value()); }
677 
679  constexpr quantity_t abs() const { return quantity_t(util::abs(value())); }
680 
682  // -- END Asymmetric arithmetic operations -------------------------------
683 
684  // -- BEGIN Comparisons --------------------------------------------------
700 
702  template <typename OU, typename OT>
703  constexpr bool operator==(Quantity<OU, OT> const other) const;
704 
705  template <typename OU, typename OT>
706  constexpr bool operator!=(Quantity<OU, OT> const other) const;
707 
708  template <typename OU, typename OT>
709  constexpr bool operator>=(Quantity<OU, OT> const other) const;
710 
711  template <typename OU, typename OT>
712  constexpr bool operator>(Quantity<OU, OT> const other) const;
713 
714  template <typename OU, typename OT>
715  constexpr bool operator<=(Quantity<OU, OT> const other) const;
716 
717  template <typename OU, typename OT>
718  constexpr bool operator<(Quantity<OU, OT> const other) const;
719 
721  // -- END Asymmetric arithmetic operations -------------------------------
722 
723  // -- BEGIN Access to the scaled unit ------------------------------------
726 
728  static constexpr unit_t unit() { return {}; }
729 
731  static constexpr baseunit_t baseUnit() { return {}; }
732 
734  static auto unitName() { return unit_t::name(); }
735 
737  static auto unitSymbol() { return unit_t::symbol(); }
738 
744  template <typename OU>
745  static constexpr bool sameBaseUnitAs()
746  {
747  return unit_t::template sameBaseUnitAs<OU>();
748  }
749 
755  template <typename OU>
756  static constexpr bool sameUnitAs()
757  {
758  return unit_t::template sameUnitAs<OU>();
759  }
760 
762  template <typename U>
763  static constexpr bool is_compatible_value_v = details::is_value_compatible_with_v<U, value_t>;
764 
766  template <typename U>
767  static constexpr bool has_compatible_value_v =
768  details::has_value_compatible_with_v<U, value_t>;
769 
771  template <typename U>
772  static constexpr bool isCompatibleValue()
773  {
774  return quantity_t::is_compatible_value_v<U>;
775  }
776 
779  template <typename U>
780  static constexpr bool hasCompatibleValue()
781  {
782  return quantity_t::has_compatible_value_v<U>;
783  }
784 
786  // -- END Access to the scaled unit --------------------------------------
787 
789  template <typename OQ>
790  constexpr OQ convertInto() const
791  {
792  return OQ(*this);
793  }
794 
815  template <typename U>
816  static constexpr quantity_t castFrom(U value)
817  {
818  return quantity_t{static_cast<value_t>(value)};
819  }
820 
821  private:
822  value_t fValue{};
823 
824  }; // struct Quantity
825 
826  template <typename... Args>
827  std::ostream& operator<<(std::ostream& out, Quantity<Args...> const q)
828  {
829  return out << q.value() << " " << q.unit();
830  }
831 
832  // -- BEGIN Arithmetic operations ------------------------------------------
843 
846 
854  template <typename U, typename T>
855  constexpr Quantity<U, T> operator+(Quantity<U, T> const q, T shift) = delete;
856  template <typename U, typename T>
857  constexpr Quantity<U, T> operator+(T shift, Quantity<U, T> const q) = delete;
858  template <typename U, typename T>
859  constexpr Quantity<U, T> operator-(Quantity<U, T> const q, T shift) = delete;
860  template <typename U, typename T>
861  constexpr Quantity<U, T> operator-(T shift, Quantity<U, T> const q) = delete;
863 
865  template <typename U, typename T, typename OT>
867  constexpr std::enable_if_t<Quantity<U, T>::template is_compatible_value_v<OT>, Quantity<U, T>>
868  operator*(Quantity<U, T> const q, OT factor)
869  {
870  return Quantity<U, T>{q.value() * static_cast<T>(factor)};
871  }
872  template <typename U, typename T, typename OT>
873  constexpr std::enable_if_t<Quantity<U, T>::template is_compatible_value_v<OT>, Quantity<U, T>>
874  operator*(OT factor, Quantity<U, T> const q)
875  {
876  return q * factor;
877  }
879 
881  template <typename AU, typename AT, typename BU, typename BT>
884  -> decltype(std::declval<AT>() * std::declval<BT>()) = delete;
886 
888  // Division by a scalar.
889  template <typename U, typename T, typename OT>
890  constexpr std::enable_if_t<Quantity<U, T>::template is_compatible_value_v<OT>, Quantity<U, T>>
892  {
893  return Quantity<U, T>{q.value() / static_cast<T>(quot)};
894  }
896 
898  // -- END Arithmetic operations --------------------------------------------
899 
900  // -------------------------------------------------------------------------
907  template <typename Unit, typename Ratio, typename T>
909 
910  // -------------------------------------------------------------------------
912  template <typename Q, typename R, typename T = typename Q::value_t>
914 
915  // -------------------------------------------------------------------------
916 
917  //
918  // extensions STL-style
919  // (can't be put in `std` since they are not specializations)
920  //
921 
924  template <typename... Args>
925  std::string to_string(ScaledUnit<Args...> const& unit)
926  {
927  return std::string(unit.prefix().symbol()) + unit.baseUnit().symbol.data();
928  }
929 
932  template <typename... Args>
933  std::string to_string(Quantity<Args...> const& q)
934  {
935  return util::to_string(q.value()) + ' ' + util::to_string(q.unit());
936  }
937 
938  // -------------------------------------------------------------------------
939 
940  } // namespace concepts
941 
942  // ---------------------------------------------------------------------------
991  namespace prefixes { // we expect other libraries to fill it
992 
993  // ratios imported from C++ standard library:
994  using std::atto, std::femto, std::pico, std::nano, std::micro, std::milli, std::centi,
995  std::deci, std::deca, std::hecto, std::kilo, std::mega, std::giga, std::tera, std::peta,
996  std::exa;
997 
998  } // namespace prefixes
999 
1000  // ---------------------------------------------------------------------------
1026  namespace units {} // we expect other libraries to fill it
1027 
1028  // -------------------------------------------------------------------------
1029  // @{
1058  template <typename Quantity>
1059  Quantity makeQuantity(std::string_view s, bool unitOptional = false);
1060 
1061  template <typename Quantity>
1062  Quantity makeQuantity(std::string const& s, bool unitOptional = false);
1063 
1064  template <typename Quantity>
1065  Quantity makeQuantity(char const* s, bool unitOptional = false);
1066 
1068 
1069  // --- BEGIN -- Specific exceptions ------------------------------------------
1070 
1072  struct MissingUnit : std::runtime_error {
1073  using std::runtime_error::runtime_error;
1074  };
1075 
1077  struct InvalidUnitPrefix : std::runtime_error {
1078  using std::runtime_error::runtime_error;
1079  };
1080 
1082  struct MismatchingUnit : std::runtime_error {
1083  using std::runtime_error::runtime_error;
1084  };
1085 
1087  struct ValueError : std::runtime_error {
1088  using std::runtime_error::runtime_error;
1089  };
1090 
1092  struct ExtraCharactersError : std::runtime_error {
1093  using std::runtime_error::runtime_error;
1094  };
1095 
1096  // --- END -- Specific exceptions --------------------------------------------
1097 
1098  // ---------------------------------------------------------------------------
1099 
1100 } // namespace util::quantities
1101 
1102 //------------------------------------------------------------------------------
1103 //--- template implementation
1104 //------------------------------------------------------------------------------
1106 
1107  //----------------------------------------------------------------------------
1108  template <std::intmax_t Num, std::intmax_t Den>
1109  struct invert_ratio<std::ratio<Num, Den>> {
1110  using type = std::ratio<Den, Num>;
1111  };
1112 
1113  //----------------------------------------------------------------------------
1114  template <std::intmax_t Num, std::intmax_t Den>
1115  struct ratio_simplifier<std::ratio<Num, Den>> {
1116  static constexpr auto gcd = boost::integer::gcd(Num, Den);
1117  using type = std::ratio<Num / gcd, Den / gcd>;
1118  }; // ratio_simplifier
1119 
1120  //----------------------------------------------------------------------------
1121  template <typename U, typename Enable = void>
1122  struct has_unit_impl : public std::false_type {};
1123 
1124  template <typename U>
1125  struct has_unit_impl<U, std::enable_if_t<util::always_true_v<typename U::unit_t>>>
1126  : public std::true_type {};
1127 
1128  template <typename U>
1129  struct has_unit : public has_unit_impl<U> {};
1130 
1131  //----------------------------------------------------------------------------
1132  template <typename Q>
1133  struct is_quantity : public std::false_type {};
1134 
1135  template <typename... Args>
1136  struct is_quantity<Quantity<Args...>> : public std::true_type {};
1137 
1138  //----------------------------------------------------------------------------
1139  template <typename Q, typename Enable = void>
1140  struct has_quantity_impl : public std::false_type {};
1141 
1142  template <typename Q>
1143  struct has_quantity_impl<Q, std::enable_if_t<util::always_true_v<typename Q::quantity_t>>>
1144  : public std::true_type {};
1145 
1146  template <typename Q>
1147  struct has_quantity : public has_quantity_impl<Q> {};
1148 
1149  //----------------------------------------------------------------------------
1150  template <typename T, typename = void>
1152  using type = T;
1153  }; // quantity_value_type
1154 
1155  template <typename T>
1157  T,
1158  std::enable_if_t<(has_unit_v<T> && util::always_true_v<typename T::value_t>)>> {
1159  using type = typename T::value_t;
1160  }; // quantity_value_type_impl<unit>
1161 
1162  template <typename T>
1164 
1165  //----------------------------------------------------------------------------
1166  template <typename T, typename Q>
1168  : std::bool_constant<std::is_convertible_v<T, quantity_value_t<Q>>> {};
1169 
1170  //----------------------------------------------------------------------------
1171  template <typename T, typename U>
1172  struct has_value_compatible_with : is_value_compatible_with<quantity_value_t<T>, U> {};
1173 
1174  //----------------------------------------------------------------------------
1176  template <typename Q>
1177  class numeric_limits : public std::numeric_limits<typename Q::value_t> {
1178 
1179  using quantity_t = Q;
1180  using value_traits_t = std::numeric_limits<typename quantity_t::value_t>;
1181 
1182  public:
1183  static constexpr quantity_t min() noexcept { return quantity_t{value_traits_t::min()}; }
1184  static constexpr quantity_t max() noexcept { return quantity_t{value_traits_t::max()}; }
1185  static constexpr quantity_t lowest() noexcept { return quantity_t{value_traits_t::lowest()}; }
1186  static constexpr quantity_t epsilon() noexcept { return quantity_t{value_traits_t::epsilon()}; }
1187  static constexpr quantity_t round_error() noexcept
1188  {
1189  return quantity_t{value_traits_t::round_error()};
1190  }
1191  static constexpr quantity_t infinity() noexcept
1192  {
1193  return quantity_t{value_traits_t::infinity()};
1194  }
1195  static constexpr quantity_t quiet_NaN() noexcept
1196  {
1197  return quantity_t{value_traits_t::quiet_NaN()};
1198  }
1199  static constexpr quantity_t signaling_NaN() noexcept
1200  {
1201  return quantity_t{value_traits_t::signaling_NaN()};
1202  }
1203  static constexpr quantity_t denorm_min() noexcept
1204  {
1205  return quantity_t{value_traits_t::denorm_min()};
1206  }
1207 
1208  }; // numeric_limits<Quantity>
1209 
1210  //----------------------------------------------------------------------------
1211 
1212 } // namespace util::quantities::concepts::details
1213 
1214 //------------------------------------------------------------------------------
1215 //--- template implementation
1216 //------------------------------------------------------------------------------
1217 //--- util::quantities::concepts::Prefix
1218 //------------------------------------------------------------------------------
1219 template <typename R>
1220 constexpr auto util::quantities::concepts::Prefix<R>::names(bool Long /* = false */)
1221 {
1222  if constexpr (std::is_same<ratio, std::tera>()) return Long ? "tera"sv : "T"sv;
1223  if constexpr (std::is_same<ratio, std::giga>()) return Long ? "giga"sv : "G"sv;
1224  if constexpr (std::is_same<ratio, std::mega>()) return Long ? "mega"sv : "M"sv;
1225  if constexpr (std::is_same<ratio, std::kilo>()) return Long ? "kilo"sv : "k"sv;
1226  if constexpr (std::is_same<ratio, std::ratio<1>>()) return ""sv;
1227  if constexpr (std::is_same<ratio, std::deci>()) return Long ? "deci"sv : "d"sv;
1228  if constexpr (std::is_same<ratio, std::centi>()) return Long ? "centi"sv : "c"sv;
1229  if constexpr (std::is_same<ratio, std::milli>()) return Long ? "milli"sv : "m"sv;
1230  if constexpr (std::is_same<ratio, std::micro>()) return Long ? "micro"sv : "u"sv;
1231  if constexpr (std::is_same<ratio, std::nano>()) return Long ? "nano"sv : "n"sv;
1232  if constexpr (std::is_same<ratio, std::pico>()) return Long ? "pico"sv : "p"sv;
1233  if constexpr (std::is_same<ratio, std::femto>()) return Long ? "femto"sv : "f"sv;
1234  // TODO complete the long list of prefixes
1235 
1236  // backup; can't use `to_string()` because of `constexpr` requirement
1237  return Long ? "???"sv : "?"sv;
1238 } // util::quantities::concepts::Prefix<R>::names()
1239 
1240 //------------------------------------------------------------------------------
1241 //--- util::quantities::concepts::Quantity
1242 //------------------------------------------------------------------------------
1243 template <typename U, typename T>
1244 template <typename OU, typename OT>
1246  Quantity<OU, OT> const other) const -> quantity_t
1247 {
1248  static_assert(std::is_same<Quantity<OU, OT>, quantity_t>(),
1249  "Only quantities with exactly the same unit can be added.");
1250  return quantity_t(value() + other.value());
1251 } // util::quantities::concepts::Quantity<>::operator+
1252 
1253 //------------------------------------------------------------------------------
1254 template <typename U, typename T>
1255 template <typename OU, typename OT>
1257  Quantity<OU, OT> const other) const -> quantity_t
1258 {
1259  static_assert(std::is_same<Quantity<OU, OT>, quantity_t>(),
1260  "Only quantities with exactly the same unit can be subtracted.");
1261  return quantity_t(value() - other.value());
1262 } // util::quantities::concepts::Quantity<>::operator+
1263 
1264 //------------------------------------------------------------------------------
1265 template <typename U, typename T>
1266 template <typename OU, typename OT>
1268  -> quantity_t
1269 {
1270  static_assert(sameBaseUnitAs<OU>(), "Can't add quantities with different base unit");
1271 
1272  // if the two quantities have the same *scaled* unit, add
1273  if constexpr (sameUnitAs<OU>()) {
1274  return quantity_t(fValue + other.value());
1275  return *this;
1276  }
1277  else {
1278  // otherwise, they have same base unit but different scale: convert `other`
1279  return (*this + quantity_t(other));
1280  }
1281 } // util::quantities::concepts::Quantity<>::operator+()
1282 
1283 //------------------------------------------------------------------------------
1284 template <typename U, typename T>
1285 template <typename OU, typename OT>
1287  -> quantity_t
1288 {
1289  static_assert(sameBaseUnitAs<OU>(), "Can't subtract quantities with different base unit");
1290 
1291  // if the two quantities have the same *scaled* unit, add
1292  if constexpr (sameUnitAs<OU>()) {
1293  return quantity_t(fValue - other.value());
1294  return *this;
1295  }
1296  else {
1297  // otherwise, they have same base unit but different scale: convert `other`
1298  return (*this - quantity_t(other));
1299  }
1300 } // util::quantities::concepts::Quantity<>::minus()
1301 
1302 //------------------------------------------------------------------------------
1303 template <typename U, typename T>
1304 template <typename OU, typename OT>
1306  -> value_t
1307 {
1308  static_assert(sameBaseUnitAs<OU>(), "Can't divide quantities with different base unit");
1309 
1310  // if the two quantities have the same *scaled* unit, divide
1311  if constexpr (sameUnitAs<OU>()) { return value() / q.value(); }
1312  else {
1313  // otherwise, they have same base unit but different scale: convert `other`
1314  return (*this) / quantity_t(q);
1315  }
1316 } // util::quantities::concepts::Quantity<>::operator/(Quantity)
1317 
1318 //------------------------------------------------------------------------------
1319 template <typename U, typename T>
1320 template <typename OU, typename OT>
1322  -> quantity_t&
1323 {
1324  static_assert(sameBaseUnitAs<OU>(), "Can't add quantities with different base unit");
1325 
1326  // if the two quantities have the same *scaled* unit, add
1327  if constexpr (sameUnitAs<OU>()) {
1328  fValue += other.value();
1329  return *this;
1330  }
1331  else {
1332  // otherwise, they have same base unit but different scale: convert `other`
1333  return (*this += quantity_t(other));
1334  }
1335 } // util::quantities::concepts::Quantity<>::operator+=()
1336 
1337 //------------------------------------------------------------------------------
1338 template <typename U, typename T>
1339 template <typename OU, typename OT>
1341  -> quantity_t&
1342 {
1343  static_assert(sameBaseUnitAs<OU>(), "Can't subtract quantities with different base unit");
1344 
1345  // if the two quantities have the same *scaled* unit, add
1346  if constexpr (sameUnitAs<OU>()) {
1347  fValue -= other.value();
1348  return *this;
1349  }
1350  else {
1351  // otherwise, they have same base unit but different scale: convert `other`
1352  return (*this -= quantity_t(other));
1353  }
1354 } // util::quantities::concepts::Quantity<>::operator-=()
1355 
1356 //------------------------------------------------------------------------------
1357 template <typename U, typename T>
1358 template <typename OU, typename OT>
1360  Quantity<OU, OT> const other) const
1361 {
1362  static_assert(sameBaseUnitAs<OU>(), "Can't compare quantities with different base unit");
1363 
1364  // if the two quantities have the same *scaled* unit, just compare the values
1365  if constexpr (sameUnitAs<OU>()) { return value() == other.value(); }
1366  else {
1367  // otherwise, they have same base unit but different scale: convert `other`
1368  return *this == quantity_t(other);
1369  }
1370 } // util::quantities::concepts::Quantity<>::operator==()
1371 
1372 //------------------------------------------------------------------------------
1373 template <typename U, typename T>
1374 template <typename OU, typename OT>
1376  Quantity<OU, OT> const other) const
1377 {
1378  static_assert(sameBaseUnitAs<OU>(), "Can't compare quantities with different base unit");
1379 
1380  // if the two quantities have the same *scaled* unit, just compare the values
1381  if constexpr (sameUnitAs<OU>()) { return value() != other.value(); }
1382  else {
1383  // otherwise, they have same base unit but different scale: convert `other`
1384  return *this != quantity_t(other);
1385  }
1386 } // util::quantities::concepts::Quantity<>::operator!=()
1387 
1388 //------------------------------------------------------------------------------
1389 template <typename U, typename T>
1390 template <typename OU, typename OT>
1392  Quantity<OU, OT> const other) const
1393 {
1394  static_assert(sameBaseUnitAs<OU>(), "Can't compare quantities with different base unit");
1395 
1396  // if the two quantities have the same *scaled* unit, just compare the values
1397  if constexpr (sameUnitAs<OU>()) { return value() <= other.value(); }
1398  else {
1399  // otherwise, they have same base unit but different scale: convert `other`
1400  return *this <= quantity_t(other);
1401  }
1402 } // util::quantities::concepts::Quantity<>::operator<=()
1403 
1404 //------------------------------------------------------------------------------
1405 template <typename U, typename T>
1406 template <typename OU, typename OT>
1408  Quantity<OU, OT> const other) const
1409 {
1410  static_assert(sameBaseUnitAs<OU>(), "Can't compare quantities with different base unit");
1411 
1412  // if the two quantities have the same *scaled* unit, just compare the values
1413  if constexpr (sameUnitAs<OU>()) { return value() >= other.value(); }
1414  else {
1415  // otherwise, they have same base unit but different scale: convert `other`
1416  return *this >= quantity_t(other);
1417  }
1418 } // util::quantities::concepts::Quantity<>::operator>=()
1419 
1420 //------------------------------------------------------------------------------
1421 template <typename U, typename T>
1422 template <typename OU, typename OT>
1424  Quantity<OU, OT> const other) const
1425 {
1426  static_assert(sameBaseUnitAs<OU>(), "Can't compare quantities with different base unit");
1427 
1428  // if the two quantities have the same *scaled* unit, just compare the values
1429  if constexpr (sameUnitAs<OU>()) { return value() < other.value(); }
1430  else {
1431  // otherwise, they have same base unit but different scale: convert `other`
1432  return *this < quantity_t(other);
1433  }
1434 } // util::quantities::concepts::Quantity<>::operator<()
1435 
1436 //------------------------------------------------------------------------------
1437 template <typename U, typename T>
1438 template <typename OU, typename OT>
1440  Quantity<OU, OT> const other) const
1441 {
1442  static_assert(sameBaseUnitAs<OU>(), "Can't compare quantities with different base unit");
1443 
1444  // if the two quantities have the same *scaled* unit, just compare the values
1445  if constexpr (sameUnitAs<OU>()) { return value() > other.value(); }
1446  else {
1447  // otherwise, they have same base unit but different scale: convert `other`
1448  return *this > quantity_t(other);
1449  }
1450 } // util::quantities::concepts::Quantity<>::operator>()
1451 
1452 //------------------------------------------------------------------------------
1454 
1466  template <typename Quantity>
1467  std::pair<std::string, typename Quantity::value_t> readUnit(std::string const& str,
1468  bool unitOptional = false);
1469 
1470 } // util::quantities::details
1471 
1472 //------------------------------------------------------------------------------
1473 template <typename Quantity>
1474 std::pair<std::string, typename Quantity::value_t> util::quantities::details::readUnit(
1475  std::string const& str,
1476  bool unitOptional /* = false */)
1477 {
1478  using Quantity_t = Quantity;
1479  using value_t = typename Quantity_t::value_t;
1480  using unit_t = typename Quantity_t::unit_t;
1481  using baseunit_t = typename unit_t::baseunit_t;
1482 
1483  // --- BEGIN -- static initialization ----------------------------------------
1484  using namespace std::string_literals;
1485 
1486  using PrefixMap_t = std::map<std::string, value_t>;
1487  using PrefixValue_t = typename PrefixMap_t::value_type;
1488  static PrefixMap_t const factors{PrefixValue_t{"a"s, 1e-18},
1489  PrefixValue_t{"f"s, 1e-15},
1490  PrefixValue_t{"p"s, 1e-12},
1491  PrefixValue_t{"n"s, 1e-09},
1492  PrefixValue_t{"u"s, 1e-06},
1493  PrefixValue_t{"m"s, 1e-03},
1494  PrefixValue_t{"c"s, 1e-02},
1495  PrefixValue_t{"d"s, 1e-01},
1496  PrefixValue_t{""s, 1e+00},
1497  PrefixValue_t{"da"s, 1e+01},
1498  PrefixValue_t{"h"s, 1e+02},
1499  PrefixValue_t{"k"s, 1e+03},
1500  PrefixValue_t{"M"s, 1e+06},
1501  PrefixValue_t{"G"s, 1e+09},
1502  PrefixValue_t{"T"s, 1e+12},
1503  PrefixValue_t{"P"s, 1e+15},
1504  PrefixValue_t{"E"s, 1e+18}}; // factors
1505  static auto const composePrefixPattern = [](auto b, auto e) -> std::string {
1506  std::string pattern = "(";
1507  if (b != e) {
1508  pattern += b->first;
1509  while (++b != e) {
1510  pattern += '|';
1511  pattern += b->first;
1512  }
1513  }
1514  return pattern += ")";
1515  };
1516  static std::string const prefixPattern = composePrefixPattern(factors.begin(), factors.end());
1517  // --- END -- static initialization ------------------------------------------
1518 
1519  std::regex const unitPattern{"[[:blank:]]*(" + prefixPattern + "?" +
1520  util::to_string(baseunit_t::symbol) + ")[[:blank:]]*$"};
1521 
1522  std::smatch unitMatch;
1523  if (!std::regex_search(str, unitMatch, unitPattern)) {
1524  if (!unitOptional) {
1525  throw MissingUnit("Unit is mandatory and must derive from '" +
1526  util::to_string(baseunit_t::symbol) + "' (parsing: '" + str + "')");
1527  }
1528  return {str, value_t{1}};
1529  }
1530 
1531  //
1532  // we do have a unit:
1533  //
1534 
1535  // " 7 cm " => [0] full match (" cm ") [1] unit ("cm") [2] unit prefix ("c")
1536  auto const iFactor = factors.find(unitMatch.str(2U));
1537  if (iFactor == factors.end()) {
1538  throw InvalidUnitPrefix("Unit '" + unitMatch.str(1U) + "' has unsupported prefix '" +
1539  unitMatch.str(2U) + "' (parsing '" + str + "')");
1540  }
1541 
1542  return {str.substr(0U, str.length() - unitMatch.length()),
1543  static_cast<value_t>(unit_t::scale(iFactor->second))};
1544 
1545 } // util::quantities::details::readUnit()
1546 
1547 //------------------------------------------------------------------------------
1548 template <typename Quantity>
1549 Quantity util::quantities::makeQuantity(std::string const& s, bool unitOptional /* = false */)
1550 {
1551  //
1552  // all this function is horrible;
1553  // some redesign is needed...
1554  //
1555  using value_t = typename Quantity::value_t;
1556 
1557  auto const [num_s, factor] = details::readUnit<Quantity>(s, unitOptional);
1558 
1559  char* parseEnd = nullptr;
1560  auto const value = static_cast<value_t>(std::strtod(num_s.c_str(), &parseEnd));
1561  const char* send = num_s.c_str() + num_s.length();
1562  if (parseEnd == num_s.c_str()) {
1563  throw ValueError("Could not convert '" + num_s + "' into a number!");
1564  }
1565  while (parseEnd != send) {
1566  if (!std::isblank(static_cast<unsigned char>(*parseEnd))) {
1567  throw ExtraCharactersError("Spurious characters after value " + std::to_string(value) +
1568  " in '" + num_s + "' ('" + std::string(parseEnd, send - parseEnd) +
1569  "')\n");
1570  }
1571  ++parseEnd;
1572  } // while
1573 
1574  //
1575  // create and return the quantity
1576  //
1577  return Quantity{static_cast<value_t>(value * factor)};
1578 } // util::quantities::makeQuantity(string_view)
1579 
1580 //------------------------------------------------------------------------------
1581 template <typename Quantity>
1582 Quantity util::quantities::makeQuantity(std::string_view s, bool unitOptional /* = false */)
1583 {
1584  return util::quantities::makeQuantity<Quantity>(std::string{s.begin(), s.end()}, unitOptional);
1585 } // util::quantities::makeQuantity(string_view)
1586 
1587 //------------------------------------------------------------------------------
1588 template <typename Quantity>
1589 Quantity util::quantities::makeQuantity(char const* s, bool unitOptional /* = false */)
1590 {
1591  return util::quantities::makeQuantity<Quantity>(std::string_view{s}, unitOptional);
1592 } // util::quantities::makeQuantity(string)
1593 
1594 //------------------------------------------------------------------------------
1595 //--- Standard library extensions
1596 //------------------------------------------------------------------------------
1597 namespace std {
1598 
1599  // ---------------------------------------------------------------------------
1601  template <typename... Args>
1602  struct hash<util::quantities::concepts::Quantity<Args...>> {
1603  private:
1605  using value_t = typename quantity_t::value_t;
1606 
1607  public:
1608  constexpr auto operator()(quantity_t key) const
1609  noexcept(noexcept(std::hash<value_t>()(key.value())))
1610  {
1611  return std::hash<value_t>()(key.value());
1612  }
1613  };
1614 
1615  // ---------------------------------------------------------------------------
1617  template <typename Unit, typename T>
1618  class numeric_limits<util::quantities::concepts::Quantity<Unit, T>>
1620  util::quantities::concepts::Quantity<Unit, T>> {};
1621 
1622  template <typename Unit, typename T>
1623  class numeric_limits<util::quantities::concepts::Quantity<Unit, T> const>
1625  util::quantities::concepts::Quantity<Unit, T> const> {};
1626 
1627  template <typename Unit, typename T>
1628  class numeric_limits<util::quantities::concepts::Quantity<Unit, T> volatile>
1630  util::quantities::concepts::Quantity<Unit, T> volatile> {};
1631 
1632  template <typename Unit, typename T>
1633  class numeric_limits<util::quantities::concepts::Quantity<Unit, T> const volatile>
1635  util::quantities::concepts::Quantity<Unit, T> const volatile> {};
1636 
1637  // ---------------------------------------------------------------------------
1638 
1639 } // namespace std
1640 
1641 //------------------------------------------------------------------------------
1642 
1643 #endif // LARDATAALG_UTILITIES_QUANTITIES_H
constexpr bool operator>=(Quantity< OU, OT > const other) const
Definition: quantities.h:1407
constexpr quantity_t plus(Quantity< OU, OT > const other) const
Returns a quantity sum of this and other.
std::enable_if_t< std::is_arithmetic_v< OT >, quantity_t & > operator*=(OT factor)
Scale this quantity by a factor.
Definition: quantities.h:658
Namespace for general, non-LArSoft-specific utilities.
Definition: PIDAAlg.h:26
constexpr std::enable_if_t< std::is_arithmetic_v< T >, Interval< Q, Cat > > operator/(Interval< Q, Cat > const iv, T const quot)
Definition: intervals.h:560
static constexpr T unscale(T v)
Converts a value from this scaled unit to the base one.
Definition: quantities.h:422
constexpr bool is_value_compatible_with_v
Trait: true if the type T is compatible with the value of Q.
Definition: quantities.h:299
static constexpr T scaleTo(T v)
Converts a value from the scaled unit to a different TargetRatio.
Definition: quantities.h:429
Basic C++ metaprogramming utilities.
static constexpr baseunit_t baseUnit()
Returns an object with as type the base unit (baseunit_t).
Definition: quantities.h:731
Trait: true_type if U is a ScaledUnit-based object.
Definition: quantities.h:257
constexpr bool operator>(Interval< Q, Cat > const a, Quantity< Args... > const b) noexcept
Definition: intervals.h:507
constexpr bool operator==(Quantity< OU, OT > const other) const
Definition: quantities.h:1359
static constexpr auto applyRatioToValue(Value &&v)
Applies the specified Ratio to the value in v.
Definition: quantities.h:235
Float_t den
Definition: plot.C:35
static constexpr quantity_t lowest() noexcept
Definition: quantities.h:1185
static constexpr T scale(T v)
Converts a value from the base unit to this one.
Definition: quantities.h:415
constexpr Point< Q, Cat, IV > operator+(Interval< OQ, OC > const delta, Point< Q, Cat, IV > const p)=delete
Multiplication with a scalar.
static constexpr bool sameUnitAs()
Returns whether scaled unit U has the same base unit as this one.
Definition: quantities.h:466
constexpr auto abs(T v)
Returns the absolute value of the argument.
quantity_t & operator-=(Quantity< OU, OT > const other)
Subtract the other quantity (possibly concerted) to this one.
Unit unit_t
The unit and scale of this quantity.
Definition: quantities.h:558
STL namespace.
static constexpr quantity_t castFrom(U value)
Returns a new quantity initialized with the specified value.
Definition: quantities.h:816
quantity_t & operator+=(Quantity< OU, OT > const other)
Add the other quantity (possibly concerted) to this one.
static constexpr T fromRepr(T v)
Converts a value from TargetRatio scale to this scaled unit.
Definition: quantities.h:436
static constexpr quantity_t signaling_NaN() noexcept
Definition: quantities.h:1199
constexpr value_t value() const
Returns the value of the quantity.
Definition: quantities.h:590
static constexpr bool sameBaseUnitAs()
Returns whether scaled unit U has the same base unit as this one.
Definition: quantities.h:459
static auto symbol()
Returns short symbol of the unit (e.g. "ns") is a string-like object.
Definition: quantities.h:400
std::integral_constant< bool, Value > bool_constant
Definition: ProviderPack.h:298
std::string to_string(Interval< Q, Cat > const &iv)
Definition: intervals.h:578
constexpr Quantity(value_t v)
Constructor: takes a value in the intended representation.
Definition: quantities.h:570
constexpr bool is_quantity_v
Trait: true if Q is a Quantity specialization.
Definition: quantities.h:269
static constexpr bool isCompatibleValue()
Returns whether U is a value type compatible with value_t.
Definition: quantities.h:772
static auto unitSymbol()
Returns the symbol of the unit, in a string-like object.
Definition: quantities.h:737
std::enable_if_t< std::is_arithmetic_v< OT >, quantity_t & > operator/=(OT quot)
Scale the quantity dividing it by a quotient.
Definition: quantities.h:666
static constexpr baseunit_t baseUnit()
Returns an instance of the baseunit_t type.
Definition: quantities.h:452
static constexpr prefix_t prefix()
Returns an instance of the prefix_t type.
Definition: quantities.h:449
static constexpr quantity_t min() noexcept
Definition: quantities.h:1183
Trait: true_type if Q is a Quantity-based object.
Definition: quantities.h:273
Double_t scale
Definition: plot.C:24
String representing a quantity has spurious characters after the number.
Definition: quantities.h:1092
R ratio
The ratio to go from the base unit to this one.
Definition: quantities.h:386
constexpr OQ convertInto() const
Convert this quantity into the specified one.
Definition: quantities.h:790
constexpr bool has_value_compatible_with_v
Trait: true if the value type of T is compatible with U&#39;s.
Definition: quantities.h:314
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
constexpr bool operator>=(Interval< Q, Cat > const a, Quantity< Args... > const b) noexcept
Definition: intervals.h:495
constexpr bool operator>(Quantity< OU, OT > const other) const
Definition: quantities.h:1439
typename unit_t::baseunit_t baseunit_t
Description of the unscaled unit.
Definition: quantities.h:562
constexpr quantity_t minus(Quantity< OU, OT > const other) const
Returns a quantity difference of this and other.
U baseunit_t
Base, unscaled unit.
Definition: quantities.h:384
A value measured in the specified unit.
Definition: quantities.h:554
static constexpr quantity_t quiet_NaN() noexcept
Definition: quantities.h:1195
static constexpr unit_t unit()
Returns an object with as type the scaled unit (unit_t).
Definition: quantities.h:728
constexpr auto operator()(quantity_t key) const noexcept(noexcept(std::hash< value_t >()(key.value())))
Definition: quantities.h:1608
std::string to_string(Quantity< Args... > const &q)
Definition: quantities.h:933
constexpr bool operator==(Interval< Q, Cat > const a, Quantity< Args... > const b) noexcept
Definition: intervals.h:447
constexpr quantity_t operator-() const
Returns a quantity with same value but the sign flipped.
Definition: quantities.h:676
constexpr bool operator<=(Quantity< OU, OT > const other) const
Definition: quantities.h:1391
details::simplify_ratio< std::ratio_divide< NumRatio, DenRatio >> simplified_ratio_divide
A ratio division (like std::ratio_divide) with simplified terms.
Definition: quantities.h:339
static constexpr unit_t unit()
Returns an instance of the unit_t type.
Definition: quantities.h:455
String & operator+=(String &s, VectorDumper< Vector > const &manip)
Appends a string rendering of a vector to the specified string.
Definition: DumpUtils.h:463
static constexpr quantity_t epsilon() noexcept
Definition: quantities.h:1186
constexpr bool operator!=(Interval< Q, Cat > const a, Quantity< Args... > const b) noexcept
Definition: intervals.h:459
constexpr bool has_unit_v
Trait: true if U is a ScaledUnit-based object.
Definition: quantities.h:261
static constexpr quantity_t round_error() noexcept
Definition: quantities.h:1187
constexpr bool has_quantity_v
Trait: true if Q is a Quantity-based object.
Definition: quantities.h:277
double value
Definition: spectrum.C:18
T value_t
Type of the stored value.
Definition: quantities.h:557
Mathematical functions that C++ standard doesn&#39;t require constexpr.
typename quantity_value_type< T >::type quantity_value_t
Definition: quantities.h:288
String representing a quantity has unsupported unit prefix.
Definition: quantities.h:1077
static constexpr auto symbol()
Returns the symbol of the prefix.
Definition: quantities.h:374
static constexpr quantity_t max() noexcept
Definition: quantities.h:1184
String representing a quantity has incompatible unit.
Definition: quantities.h:1082
std::ostream & operator<<(std::ostream &out, Interval< Args... > const iv)
Definition: intervals.h:428
String representing a quantity has no unit.
Definition: quantities.h:1072
constexpr Point< Q, Cat, IV > operator-(Interval< OQ, OC > const delta, Point< Q, Cat, IV > const p)=delete
Multiplication with a scalar.
static constexpr bool sameUnitAs()
Returns whether this quantity has same unit and scale as OU.
Definition: quantities.h:756
Functions pulling in STL customization if available.
Quantity makeQuantity(std::string_view s, bool unitOptional=false)
Returns a quantity of the specified type parsed from a string.
Definition: quantities.h:1582
details::simplify_ratio< std::ratio_multiply< ARatio, BRatio >> simplified_ratio_multiply
A ratio product (like std::ratio_multiply) with simplified terms.
Definition: quantities.h:335
typename invert_ratio< R >::type invert_t
Definition: quantities.h:244
constexpr std::enable_if_t< std::is_arithmetic_v< T >, Interval< Q, Cat > > operator*(Interval< Q, Cat > const iv, T const factor)
Multiplication with a scalar.
Definition: intervals.h:537
constexpr bool operator!=(Quantity< OU, OT > const other) const
Definition: quantities.h:1375
Limits of a quantity are the same as the underlying type.
Definition: quantities.h:324
static constexpr auto name()
Returns the full name of the prefix.
Definition: quantities.h:377
typename ratio_simplifier< R >::type simplify_ratio
Definition: quantities.h:250
R ratio
The ratio this prefix is about.
Definition: quantities.h:368
static auto unitName()
Returns the full name of the unit, in a string-like object.
Definition: quantities.h:734
Types of variables with a unit.
Definition: intervals.h:20
static constexpr bool sameBaseUnitAs()
Returns whether this quantity has the same base unit as OU.
Definition: quantities.h:745
constexpr quantity_t abs() const
Returns a quantity with the absolute value of this one.
Definition: quantities.h:679
String representing a quantity has an invalid number.
Definition: quantities.h:1087
Float_t e
Definition: plot.C:35
static constexpr bool hasCompatibleValue()
Definition: quantities.h:780
constexpr quantity_t operator+() const
Returns a quantity with same value.
Definition: quantities.h:673
constexpr Quantity(Q q)
Constructor: converts from another quantity.
Definition: quantities.h:582
static constexpr auto names(bool Long=false)
Returns the unit symbol (Long false) or name (Long true).
Definition: quantities.h:1220
Trait: true_type if Q is a Quantity specialization.
Definition: quantities.h:265
static constexpr quantity_t denorm_min() noexcept
Definition: quantities.h:1203
std::pair< std::string, typename Quantity::value_t > readUnit(std::string const &str, bool unitOptional=false)
Parses the unit of a string representing a Quantity.
Definition: quantities.h:1474
constexpr value_t operator/(Quantity< OU, OT > q) const
Division by a quantity, returns a pure number.
constexpr bool operator<(Quantity< OU, OT > const other) const
Definition: quantities.h:1423
static constexpr quantity_t infinity() noexcept
Definition: quantities.h:1191