diff --git a/include/xo/unit/basis_unit2.hpp b/include/xo/unit/basis_unit2.hpp new file mode 100644 index 00000000..37724236 --- /dev/null +++ b/include/xo/unit/basis_unit2.hpp @@ -0,0 +1,345 @@ +/** @file basis_unit2.hpp **/ + +#pragma once + +#include "dim_util2.hpp" +#include "xo/ratio/ratio.hpp" +#include +#include + +namespace xo { + namespace unit { + using basis_unit2_abbrev_type = flatstring<16>; + + using scalefactor_ratio_type = xo::ratio::ratio; + + /** @class basis_unit2 + * @brief A dimensionless multiple of a single natively-specified basis dimension + * + * For example "3600 minutes" or "1e-6 grams" + **/ + struct basis_unit2 { + public: + constexpr basis_unit2(dim native_dim, const scalefactor_ratio_type & scalefactor) + : native_dim_{native_dim}, + scalefactor_{scalefactor} + {} + + constexpr dim native_dim() const { return native_dim_; } + constexpr const scalefactor_ratio_type & scalefactor() const { return scalefactor_; } + + /** @brief identifies a native unit, e.g. time (in seconds) **/ + const dim native_dim_; + /** @brief this unit defined as multiple scalefactor times native unit **/ + const scalefactor_ratio_type scalefactor_; + }; + + namespace units { + /** for runtime work, would like to be able to promptly find special abbreviation + * keyed by (native_dim, scalefactor). + * + * Also want to support compile-time-only unit computation. + * Since constexpr unordered containers aren't obviously feasible (as of c++23). + * + * Solution adopted here is to support introduction of scaled native units + * only at compile time, at least for now + **/ + + // ----- scaled_native_unit_abbrev_helper ----- + + /* Require: InnerScale is ratio type; InnerScale >= 1 + * + * NOTE: clang 18 doesn't accept that scalefactor_ratio_type is a 'structural type' + */ + template + struct scaled_native_unit2_abbrev; + + template + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value + = (basis_unit2_abbrev_type::from_flatstring + (native_unit2_v[static_cast(BasisDim)] + .abbrev_str())); + }; + + inline + constexpr basis_unit2_abbrev_type + bu_fallback_abbrev(dim basis_dim, + const scalefactor_ratio_type & scalefactor) + { + return (basis_unit2_abbrev_type::from_flatstring + (flatstring_concat + (scalefactor.to_str(), + native_unit2_v[static_cast(basis_dim)].abbrev_str()))); + } + + template + struct scaled_native_unit2_abbrev { + /* e.g. unit of '1000 grams' will have abbrev '1000g' in absence + * of a specialization for scaled_native_unit_abbrev + */ + static constexpr const basis_unit2_abbrev_type value + = (basis_unit2_abbrev_type::from_flatstring + (flatstring_concat + (xo::ratio::ratio(InnerScaleNum, + InnerScaleDen) + .to_str(), + native_unit2_v[static_cast(BasisDim)].abbrev_str()))); + + // = bu_fallback_abbrev(BasisDim, + // xo::ratio::ratio(InnerScaleNum, InnerScaleDen)); + }; + + // ----- units for dim::mass ----- + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("ng"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("ug"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("mg"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("kg"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("t"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("kt"); + }; + + // ----- units for dim::distance ----- + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("nm"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("um"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("mm"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("km"); + }; + + // ----- units for dim::time ----- + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("ns"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("us"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("ms"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("min"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("hr"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("dy"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("yr250"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("yr360"); + }; + + template <> + struct scaled_native_unit2_abbrev { + static constexpr const basis_unit2_abbrev_type value = basis_unit2_abbrev_type::from_chars("yr365"); + }; + + // ----- native unit abbrev api ----- + + template + constexpr auto scaled_native_unit2_abbrev_v = scaled_native_unit2_abbrev::value; + } + + /** @class basis_unit2_store + * @brief Store known basis units for runtime + **/ + template + struct basis_unit2_store { + basis_unit2_store() : bu_abbrev_vv_(static_cast(dim::n_dim)) { + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + this->bu_establish_abbrev_for(); + + this->bu_establish_abbrev_for(); + + this->bu_establish_abbrev_for(); + } + + /* e.g. + * [(1/1000000000, "nm"), (1/1000000, "um"), (1/1000, "mm"), (1/1, "m"), (1000/1, "km")] + */ + using native_scale_v = std::vector>; + + /** @brief get basis-unit abbreviation at runtime **/ + basis_unit2_abbrev_type bu_abbrev(dim basis_dim, + const scalefactor_ratio_type & scalefactor) const { + const auto & bu_abbrev_v = bu_abbrev_vv_[static_cast(basis_dim)]; + + std::size_t i_abbrev = bu_abbrev_lub_ix(basis_dim, scalefactor, bu_abbrev_v); + + if ((i_abbrev < bu_abbrev_v.size()) + && (bu_abbrev_v[i_abbrev].first == scalefactor)) + { + return bu_abbrev_v[i_abbrev].second; + } else { + return units::bu_fallback_abbrev(basis_dim, scalefactor); + } + } + + template + void bu_establish_abbrev_for() { + this->bu_establish_abbrev(basis_unit2(BasisDim, + scalefactor_ratio_type(InnerScaleNum, InnerScaleDen)), + units::scaled_native_unit2_abbrev_v); + } + + /** @brief establish abbreviation @p abbrev for basis unit @p bu + **/ + void bu_establish_abbrev(const basis_unit2 & bu, + const basis_unit2_abbrev_type & abbrev) { + + auto & bu_abbrev_v = bu_abbrev_vv_[static_cast(bu.native_dim())]; + + std::int32_t i_abbrev = 0; + + if (!bu_abbrev_v.empty()) { + i_abbrev = bu_abbrev_lub_ix(bu.native_dim(), + bu.scalefactor(), + bu_abbrev_v); + } + + auto entry = std::make_pair(bu.scalefactor(), abbrev); + + if ((i_abbrev < bu_abbrev_v.size()) + && (bu_abbrev_v[i_abbrev].first == bu.scalefactor())) + { + bu_abbrev_v[i_abbrev] = entry; + } else { + bu_abbrev_v.insert(bu_abbrev_v.begin() + i_abbrev, entry); + } + } + + private: + /** @brief get least-upper-bound index position in bu_abbrev_v[] + * + * return value in [0, n] where n = bu_abbrev_v.size() + **/ + static std::size_t bu_abbrev_lub_ix(dim basis_dim, + const scalefactor_ratio_type & scalefactor, + const native_scale_v & bu_abbrev_v) + { + std::size_t n = bu_abbrev_v.size(); + + if (n == 0) + return 0; + + std::size_t lo = 0; + std::size_t hi = n-1; + + if (scalefactor <= bu_abbrev_v[lo].first) + return 0; + + auto cmp = (scalefactor <=> bu_abbrev_v[hi].first); + + if (cmp > 0) + return n; + + if (cmp == 0) + return hi; + + while (hi-lo > 1) { + /* inv: + * bu_abbrev_v[lo].first < scalefactor <= bu_abbrev_v[hi].first + */ + + std::size_t mid = lo + (hi - lo)/2; + + if (scalefactor > bu_abbrev_v[mid].first) + lo = mid; + else + hi = mid; + } + + return hi; + } + + private: + /* bu_abbrev_v[dim] holds known units for native unit dim */ + std::vector bu_abbrev_vv_; + }; + + } /*namespace unit*/ +} /*namespace xo*/ + + +/** end basis_unit2.hpp **/ diff --git a/include/xo/unit/dim_util2.hpp b/include/xo/unit/dim_util2.hpp new file mode 100644 index 00000000..c49706fd --- /dev/null +++ b/include/xo/unit/dim_util2.hpp @@ -0,0 +1,51 @@ +/** @file dim_util2.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "dim_util.hpp" +#include "xo/flatstring/flatstring.hpp" + +namespace xo { + namespace unit { + using native_unit2_abbrev_type = flatstring<8>; + + /** @class native_unit2 + * + * @brief Represent a native (built-in) unit. + * + * A basis_unit2 is expressed as a multiple of a native_unit2 + * + **/ + struct native_unit2 { + public: + constexpr native_unit2(dim native_dim, + const native_unit2_abbrev_type & abbrev_str) + : native_dim_{native_dim}, + abbrev_str_{abbrev_str} + {} + + constexpr dim native_dim() const { return native_dim_; } + constexpr const native_unit2_abbrev_type & abbrev_str() const { return abbrev_str_; } + + private: + dim native_dim_; + native_unit2_abbrev_type abbrev_str_; + }; + + static constexpr native_unit2 native_unit2_v[n_dim] = { + native_unit2(dim::mass, native_unit2_abbrev_type::from_chars("g")), + native_unit2(dim::distance, native_unit2_abbrev_type::from_chars("m")), + native_unit2(dim::time, native_unit2_abbrev_type::from_chars("s")), + native_unit2(dim::currency, native_unit2_abbrev_type::from_chars("ccy")), + native_unit2(dim::price, native_unit2_abbrev_type::from_chars("px")), + }; + + } /*namespace unit*/ +} /*namespace xo*/ + + + +/** end dim_util2.hpp **/ diff --git a/include/xo/unit/native_bpu2.hpp b/include/xo/unit/native_bpu2.hpp new file mode 100644 index 00000000..2db88bdf --- /dev/null +++ b/include/xo/unit/native_bpu2.hpp @@ -0,0 +1,65 @@ +/** @file native_bpu2.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "basis_unit2.hpp" + +namespace xo { + namespace unit { + using bpu2_abbrev_type = flatstring<24>; + + using power_ratio_type = xo::ratio::ratio; + + /** @class native_bpu2 + * + * @brief represent product of a compile-time scale-factor with a rational power of a native unit + * + * Example: + * native_bpu, ratio<-1,2>> represents unit of 1/sqrt(t) + **/ + template + struct bpu2 : basis_unit2 { + public: + constexpr bpu2(power_ratio_type power, + dim native_dim, + scalefactor_ratio_type scalefactor) + : basis_unit2(native_dim, scalefactor), + power_{power} + {} + + constexpr const power_ratio_type & power() const { return power_; } + + /** @brief this unit represents native dimension taken to this power **/ + power_ratio_type power_; + }; + + template < + dim BasisDim, + std::int64_t InnerScaleNum, std::int64_t InnerScaleDen, + std::int64_t PowerNum, std::int64_t PowerDen + > + constexpr bpu2_abbrev_type + bpu2_assemble_abbrev_helper() + { + return flatstring_concat + (units::scaled_native_unit2_abbrev_v, + flatstring_from_exponent()); + }; + + template < typename BPU > + constexpr auto bpu2_assemble_abbrev(const BPU & bpu) { + // bpu.power(), bpu.native_dim(), bpu.scalefactor() + + return bpu2_assemble_abbrev_helper< + bpu.native_dim(), + bpu.scalefactor().num(), bpu.scalefactor().den(), + bpu.power().num(), bpu.power().den()>; + }; + } /*namespace unit*/ +} /*namespace xo*/ + + +/** end native_bpu2.hpp **/