/** @file scaled_unit2.hpp * * Author: Roland Conybeare **/ #pragma once #include "natural_unit.hpp" namespace xo { namespace qty { /** @class scaled_unit * @brief Represents the product sqrt(outer_scale_sq) * outer_scale_exact * nat_unit **/ template < typename Int, typename OuterScale = ratio::ratio > struct scaled_unit { constexpr scaled_unit(const natural_unit & nat_unit, OuterScale outer_scale_factor, double outer_scale_sq) : natural_unit_{nat_unit}, outer_scale_factor_{outer_scale_factor}, outer_scale_sq_{outer_scale_sq} {} constexpr scaled_unit reciprocal() const { return scaled_unit(natural_unit_.reciprocal(), 1 / outer_scale_factor_, 1.0 / outer_scale_sq_); } public: /* need public members so that a scaled_unit instance can be a non-type template parameter (a structural type) */ natural_unit natural_unit_; OuterScale outer_scale_factor_; double outer_scale_sq_; }; namespace detail { template constexpr auto make_unit_rescale_result(const natural_unit & bpuv) { return scaled_unit(bpuv, ratio::ratio(1, 1), 1.0); } } namespace su { /* note: probably retire these */ constexpr auto nanogram = detail::make_unit_rescale_result(nu::nanogram); constexpr auto microgram = detail::make_unit_rescale_result(nu::microgram); constexpr auto millisecond = detail::make_unit_rescale_result(nu::millisecond); } namespace detail { template struct width2x; template <> struct width2x { using type = std::int32_t; }; template <> struct width2x { using type = std::int64_t; }; template <> struct width2x { using type = __int128_t; }; template using width2x_t = width2x::type; template , typename OuterScale = ratio::ratio> constexpr scaled_unit su_product(const natural_unit & lhs_bpu_array, const natural_unit & rhs_bpu_array) { natural_unit prod = lhs_bpu_array.template to_repr(); /* accumulate product of scalefactors spun off by rescaling * any basis-units in rhs_bpu_array that conflict with the same dimension * in lh_bpu_array */ auto sfr = (detail::outer_scalefactor_result (OuterScale(1) /*outer_scale_factor*/, 1.0 /*outer_scale_sq*/)); for (std::size_t i = 0; i < rhs_bpu_array.n_bpu(); ++i) { auto sfr2 = nu_product_inplace(&prod, rhs_bpu_array[i].template to_repr()); sfr.outer_scale_factor_ = sfr.outer_scale_factor_ * sfr2.outer_scale_factor_; sfr.outer_scale_sq_ *= sfr2.outer_scale_sq_; } return scaled_unit(prod.template to_repr(), sfr.outer_scale_factor_, sfr.outer_scale_sq_); } /* use Int2x to accumulate scalefactor */ template < typename Int, typename Int2x = width2x, typename OuterScale = ratio::ratio > constexpr scaled_unit su_ratio(const natural_unit & nu_lhs, const natural_unit & nu_rhs) { natural_unit ratio = nu_lhs.template to_repr(); /* accumulate product of scalefactors spun off by rescaling * any basis-units in rhs_bpu_array that conflict with the same dimension * in lh_bpu_array */ auto sfr = (detail::outer_scalefactor_result (OuterScale(1) /*outer_scale_factor*/, 1.0 /*outer_scale_sq*/)); for (std::size_t i = 0; i < nu_rhs.n_bpu(); ++i) { auto sfr2 = nu_ratio_inplace(&ratio, nu_rhs[i].template to_repr()); /* note: nu_ratio_inplace() reports multiplicative outer scaling factors, * so multiply is correct here */ sfr.outer_scale_factor_ = (sfr.outer_scale_factor_ * sfr2.outer_scale_factor_); sfr.outer_scale_sq_ *= sfr2.outer_scale_sq_; } return scaled_unit(ratio.template to_repr(), sfr.outer_scale_factor_, sfr.outer_scale_sq_); } } template > inline constexpr scaled_unit operator* (const scaled_unit & x_unit, const scaled_unit & y_unit) { auto rr = detail::su_product(x_unit.natural_unit_, y_unit.natural_unit_); return (scaled_unit (rr.natural_unit_, (ratio::ratio(rr.outer_scale_factor_) * ratio::ratio(x_unit.outer_scale_factor_) * ratio::ratio(y_unit.outer_scale_factor_)), rr.outer_scale_sq_ * x_unit.outer_scale_sq_ * y_unit.outer_scale_sq_)); } template > inline constexpr scaled_unit operator/ (const scaled_unit & x_unit, const scaled_unit & y_unit) { auto rr = detail::su_ratio(x_unit.natural_unit_, y_unit.natural_unit_); return (scaled_unit (rr.natural_unit_, (ratio::ratio(rr.outer_scale_factor_) * ratio::ratio(x_unit.outer_scale_factor_) * ratio::ratio(y_unit.outer_scale_factor_)), rr.outer_scale_sq_ * x_unit.outer_scale_sq_ * y_unit.outer_scale_sq_)); } } /*namespace qty*/ } /*namespace xo*/ /** end scaled_unit2.hpp **/