diff --git a/include/xo/unit/Quantity.hpp b/include/xo/unit/Quantity.hpp index 98d086d8..c94dbebe 100644 --- a/include/xo/unit/Quantity.hpp +++ b/include/xo/unit/Quantity.hpp @@ -44,13 +44,13 @@ namespace xo { constexpr Quantity reciprocal() const { return Quantity(1.0 / scale_, unit_.reciprocal()); } - template + template static constexpr - auto multiply(const Quantity & x, const OtherQuantity & y) { + auto multiply(const Quantity & x, const Quantity2 & y) { using r_repr_type = std::common_type_t; + typename Quantity2::repr_type>; using r_int_type = std::common_type_t; + typename Quantity2::ratio_int_type>; auto rr = detail::nu_product(x.unit(), y.unit()); @@ -85,6 +85,30 @@ namespace xo { rr.natural_unit_); } + template + static constexpr + auto add(const Quantity & x, const Quantity2 & y) { + using r_repr_type = std::common_type_t; + using r_int_type = std::common_type_t; + + /* conversion to get y in same units as x: multiply by y/x */ + auto rr = detail::nu_ratio(y.unit(), x.unit()); + + if (rr.natural_unit_.is_dimensionless()) { + r_repr_type r_scale = (static_cast(x.scale()) + + (::sqrt(rr.outer_scale_sq_) + * rr.outer_scale_exact_.template to() + * static_cast(y.scale()))); + + return Quantity(r_scale, x.unit_.template to_repr()); + } else { + /* units don't match! */ + return Quantity(std::numeric_limits::quiet_NaN(), + x.unit_.template to_repr()); + } + } private: /** @brief quantity represents this multiple of a unit amount **/ @@ -106,23 +130,47 @@ namespace xo { /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) **/ - template + template + inline constexpr Quantity + natural_unit_qty(const natural_unit & nu) { + return Quantity(1.0, nu); + } + + /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) + **/ + template + requires quantity2_concept && quantity2_concept constexpr auto - operator* (const Quantity & x, const OtherQuantity & y) + operator* (const Quantity & x, const Quantity2 & y) { return Quantity::multiply(x, y); } /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) **/ - template + template + requires quantity2_concept && quantity2_concept constexpr auto - operator/ (const Quantity & x, const OtherQuantity & y) + operator/ (const Quantity & x, const Quantity2 & y) { return Quantity::divide(x, y); } + + /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) + **/ + template + requires quantity2_concept && quantity2_concept + constexpr auto + operator+ (const Quantity & x, const Quantity2 & y) + { + return Quantity::add(x, y); + } + + namespace unit { + constexpr auto nanogram = natural_unit_qty(nu2::nanogram); + } } /*namespace qty*/ } /*namespace xo*/ - /** end Quantity.hpp **/ diff --git a/include/xo/unit/basis_unit.hpp b/include/xo/unit/basis_unit.hpp index cf0eb6aa..cd3325eb 100644 --- a/include/xo/unit/basis_unit.hpp +++ b/include/xo/unit/basis_unit.hpp @@ -1,4 +1,4 @@ -/** @file basis_unit2.hpp **/ +/** @file basis_unit.hpp **/ #pragma once @@ -211,5 +211,4 @@ namespace xo { } /*namespace qty*/ } /*namespace xo*/ - -/** end basis_unit2.hpp **/ +/** end basis_unit.hpp **/ diff --git a/include/xo/unit/bpu.hpp b/include/xo/unit/bpu.hpp index 97732279..3e394356 100644 --- a/include/xo/unit/bpu.hpp +++ b/include/xo/unit/bpu.hpp @@ -102,6 +102,13 @@ namespace xo { return bpu(native_dim(), scalefactor(), power_.negate()); } + template + constexpr bpu to_repr() const { + return bpu(this->native_dim(), + this->scalefactor(), + ratio::ratio(power_.num(), power_.den())); + } + /** @brief this unit represents native dimension taken to this power **/ power_ratio_type power_; }; diff --git a/include/xo/unit/natural_unit.hpp b/include/xo/unit/natural_unit.hpp index 15a7f65c..33f7b84d 100644 --- a/include/xo/unit/natural_unit.hpp +++ b/include/xo/unit/natural_unit.hpp @@ -33,6 +33,8 @@ namespace xo { constexpr natural_unit() : n_bpu_{0} {} constexpr std::size_t n_bpu() const { return n_bpu_; } + constexpr bool is_dimensionless() const { return n_bpu_ == 0; } + constexpr bpu * bpu_v() const { return bpu_v_; } constexpr nu_abbrev_type abbrev() const { @@ -63,6 +65,17 @@ namespace xo { constexpr bpu & operator[](std::size_t i) { return bpu_v_[i]; } constexpr const bpu & operator[](std::size_t i) const { return bpu_v_[i]; } + template + constexpr natural_unit to_repr() const { + natural_unit retval; + + std::size_t i = 0; + for (; i < n_bpu_; ++i) + retval.push_back(bpu_v_[i].template to_repr()); + + return retval; + } + private: /** @brief the number of occupied slots in @c bpu_v_ **/ std::size_t n_bpu_; diff --git a/include/xo/unit/quantity2_concept.hpp b/include/xo/unit/quantity2_concept.hpp index 3fbde14e..1449539f 100644 --- a/include/xo/unit/quantity2_concept.hpp +++ b/include/xo/unit/quantity2_concept.hpp @@ -13,7 +13,8 @@ namespace xo { typename Quantity::unit_type; typename Quantity::repr_type; - { qty.scale() } -> std::same_as; + { qty.scale() } -> std::same_as; + { qty.unit() } -> std::same_as; //{ Quantity::unit_cstr() } -> std::same_as; //{ Quantity::unit_quantity() } -> std::same_as; //{ Quantity::promote(repr) } -> std::same_as; diff --git a/utest/unit.test.cpp b/utest/unit.test.cpp index a0404279..80be39db 100644 --- a/utest/unit.test.cpp +++ b/utest/unit.test.cpp @@ -1119,6 +1119,45 @@ namespace xo { //REQUIRE(ng2.scale() == 1); } /*TEST_CASE(Quantity5)*/ + + TEST_CASE("Quantity6", "[Quantity]") { + constexpr bool c_debug_flag = true; + + // can get bits from /dev/random by uncommenting the 2nd line below + //uint64_t seed = xxx; + //rng::Seed seed; + + //auto rng = xo::rng::xoshiro256ss(seed); + + scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.Quantity6")); + //log && log("(A)", xtag("foo", foo)); + + /* not constexpr until c++26 */ + Quantity ng = unit_qty(su2::nanogram); + Quantity ug = unit_qty(su2::microgram); + + { + auto sum1 = ng + ug; + log && log(xtag("ng+ug", sum1)); + + /* units will be nanograms, since that's on lhs */ + REQUIRE(sum1.unit().n_bpu() == 1); + REQUIRE(sum1.unit()[0].scalefactor() == scalefactor_ratio_type(1, 1000000000)); + REQUIRE(sum1.scale() == 1001.0); + } + + { + auto sum2 = ug + ng; + log && log(xtag("ug+ng", sum2)); + + /* units will be micrograms, since that's on rhs */ + REQUIRE(sum2.unit().n_bpu() == 1); + REQUIRE(sum2.unit()[0].scalefactor() == scalefactor_ratio_type(1, 1000000)); + REQUIRE(sum2.scale() == 1.001); + } + + //REQUIRE(ng2.scale() == 1); + } /*TEST_CASE(Quantity6)*/ } /*namespace ut*/ } /*namespace xo*/