Add 'xo-unit/' from commit 'b531e382c2'
git-subtree-dir: xo-unit git-subtree-mainline:e9ee6992cagit-subtree-split:b531e382c2
This commit is contained in:
commit
d1fa15f248
105 changed files with 11790 additions and 0 deletions
240
xo-unit/include/xo/unit/basis_unit.hpp
Normal file
240
xo-unit/include/xo/unit/basis_unit.hpp
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/** @file basis_unit.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "native_unit.hpp"
|
||||
#include "dimension.hpp"
|
||||
//#include "basis_unit_abbrev.hpp"
|
||||
#include "xo/ratio/ratio.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
using bu_abbrev_type = flatstring<16>;
|
||||
using scalefactor_ratio_type = xo::ratio::ratio<std::int64_t>;
|
||||
using scalefactor2x_ratio_type = xo::ratio::ratio<__int128>;
|
||||
|
||||
/** @class basis_unit
|
||||
* @brief A dimensionless multiple of a single natively-specified basis dimension
|
||||
*
|
||||
* For example "3600 minutes" or "1e-6 grams".
|
||||
*
|
||||
* Members are public so that a @c basis_unit instance qualifies as a 'structural type',
|
||||
* and therefore may be used as a non-type template parameter.
|
||||
**/
|
||||
struct basis_unit {
|
||||
public:
|
||||
/** @defgroup basis-unit-constructors basis_unit constructors **/
|
||||
///@{
|
||||
/** sentinel basis unit: invalid dimension and zero scalefactor **/
|
||||
constexpr basis_unit() = default;
|
||||
/** basis unit representing multiple @p scalefactor of native dimension @p **/
|
||||
constexpr basis_unit(dimension native_dim,
|
||||
const scalefactor_ratio_type & scalefactor)
|
||||
: native_dim_{native_dim},
|
||||
scalefactor_{scalefactor}
|
||||
{}
|
||||
///@}
|
||||
|
||||
/** @defgroup basis-unit-access-methods basis_unit access methods **/
|
||||
///@{
|
||||
/** get @c native_dim member **/
|
||||
constexpr dimension native_dim() const { return native_dim_; }
|
||||
/** get @c scalefactor member **/
|
||||
constexpr const scalefactor_ratio_type & scalefactor() const { return scalefactor_; }
|
||||
///@}
|
||||
|
||||
public: /* public so instance can be a non-type template parameter (a structural type) */
|
||||
/** @defgroup basis-unit-instance-vars basis_unit instance variables **/
|
||||
///@{
|
||||
/** @brief identifies a native unit, e.g. time **/
|
||||
dimension native_dim_ = dimension::invalid;
|
||||
/** @brief this unit defined as multiple scalefactor times native unit **/
|
||||
scalefactor_ratio_type scalefactor_ = {};
|
||||
///@}
|
||||
};
|
||||
|
||||
/** @defgroup basis-unit-comparison-support basis_unit comparisons **/
|
||||
///@{
|
||||
/** @c true iff basis units are equal;
|
||||
* both native dimension and scalefactor must be equal
|
||||
**/
|
||||
inline constexpr bool
|
||||
operator==(const basis_unit & x, const basis_unit & y)
|
||||
{
|
||||
return ((x.native_dim_ == y.native_dim_)
|
||||
&& (x.scalefactor_ == y.scalefactor_));
|
||||
}
|
||||
|
||||
/** @c true iff bass units are not equal **/
|
||||
inline constexpr bool
|
||||
operator!=(const basis_unit & x, const basis_unit & y)
|
||||
{
|
||||
return ((x.native_dim_ != y.native_dim_)
|
||||
|| (x.scalefactor_ != y.scalefactor_));
|
||||
}
|
||||
///@}
|
||||
|
||||
namespace detail {
|
||||
/** @brief namespace for basis-unit constants and helpers **/
|
||||
namespace bu {
|
||||
// ----- mass -----
|
||||
|
||||
constexpr basis_unit mass_unit(std::int64_t num, std::int64_t den) {
|
||||
return basis_unit(dimension::mass, scalefactor_ratio_type(num, den));
|
||||
}
|
||||
|
||||
/** @defgroup basis-unit-mass-units basis_unit mass units **/
|
||||
///@{
|
||||
/** basis unit of 10^-12 grams **/
|
||||
constexpr basis_unit picogram = mass_unit( 1, 1000000000000);
|
||||
/** basis unit of 10^-9 grams **/
|
||||
constexpr basis_unit nanogram = mass_unit( 1, 1000000000);
|
||||
/** basis unit of 10^-6 grams **/
|
||||
constexpr basis_unit microgram = mass_unit( 1, 1000000);
|
||||
/** basis unit of 10^-3 grams **/
|
||||
constexpr basis_unit milligram = mass_unit( 1, 1000);
|
||||
/** basis unit of 1 gram **/
|
||||
constexpr basis_unit gram = mass_unit( 1, 1);
|
||||
/** basis unit of 10^3 grams **/
|
||||
constexpr basis_unit kilogram = mass_unit( 1000, 1);
|
||||
/** basis unit of 10^6 grams = 10^3 kilograms **/
|
||||
constexpr basis_unit tonne = mass_unit( 1000000, 1);
|
||||
/** basis unit of 10^9 grams = 10^6 kilograms = 10^3 tonnes **/
|
||||
constexpr basis_unit kilotonne = mass_unit( 1000000000, 1);
|
||||
/** basis unit of 10^12 grams = 10^9 kilograms = 10^6 tonnes **/
|
||||
constexpr basis_unit megatonne = mass_unit( 1000000000000, 1);
|
||||
/** basis unit of 10^15 grams = 10^12 kilograms = 10^9 tonnes **/
|
||||
constexpr basis_unit gigatonne = mass_unit(1000000000000000, 1);
|
||||
///@}
|
||||
|
||||
// ----- distance -----
|
||||
|
||||
constexpr basis_unit distance_unit(std::int64_t num, std::int64_t den) {
|
||||
return basis_unit(dimension::distance, scalefactor_ratio_type(num, den));
|
||||
}
|
||||
|
||||
/** @defgroup basis-unit-distance-units basis_unit distance units **/
|
||||
///@{
|
||||
/* US spelling */
|
||||
/** basis unit of 10^-12 meters **/
|
||||
constexpr basis_unit picometer = distance_unit( 1, 1000000000000);
|
||||
/** basis unit of 10^-9 meters **/
|
||||
constexpr basis_unit nanometer = distance_unit( 1, 1000000000);
|
||||
/** basis unit of 10^-6 meters **/
|
||||
constexpr basis_unit micrometer = distance_unit( 1, 1000000);
|
||||
/** basis unit of 10^-3 meters **/
|
||||
constexpr basis_unit millimeter = distance_unit( 1, 1000);
|
||||
/** basis unit of 1 meter **/
|
||||
constexpr basis_unit meter = distance_unit( 1, 1);
|
||||
/** basis unit of 10^3 meters **/
|
||||
constexpr basis_unit kilometer = distance_unit( 1000, 1);
|
||||
/** basis unit of 10^6 meters (for form's sake -- not commonly used) **/
|
||||
constexpr basis_unit megameter = distance_unit( 1000000, 1);
|
||||
/** basis unit of 10^9 meters (for form's sake -- not commonly used) **/
|
||||
constexpr basis_unit gigameter = distance_unit( 1000000000, 1);
|
||||
|
||||
/** basis unit of 1 light-second = distance light travels in a vacuum in 1 second **/
|
||||
constexpr basis_unit lightsecond = distance_unit( 299792458, 1);
|
||||
/** basis unit of 1 astronomical unit, representing approximate radius of earth orbit. **/
|
||||
constexpr basis_unit astronomicalunit = distance_unit( 149597870700, 1);
|
||||
|
||||
/* Int'l spelling */
|
||||
/** international spelling for picometer **/
|
||||
constexpr basis_unit picometre = picometer;
|
||||
/** international spelling for nanometer **/
|
||||
constexpr basis_unit nanometre = nanometer;
|
||||
/** international spelling for micrometer **/
|
||||
constexpr basis_unit micrometre = micrometer;
|
||||
/** international spelling for millimeter **/
|
||||
constexpr basis_unit millimetre = millimeter;
|
||||
/** international spelling for meter **/
|
||||
constexpr basis_unit metre = meter;
|
||||
/** international spelling for kilometer **/
|
||||
constexpr basis_unit kilometre = kilometer;
|
||||
/** international spelling for megameter **/
|
||||
constexpr basis_unit megametre = megameter;
|
||||
/** international spelling for gigameter **/
|
||||
constexpr basis_unit gigametre = gigameter;
|
||||
|
||||
/** @brief basis-unit representing 1 inch; defined as exactly 1/12 feet **/
|
||||
constexpr basis_unit inch = distance_unit( 3048, 120000);
|
||||
/** @brief basis-unit representing 1 foot; defined as exactly 0.3048 meters **/
|
||||
constexpr basis_unit foot = distance_unit( 3048, 10000);
|
||||
/** @brief basis-unit representing 1 yard; defined as exactly 3 feet **/
|
||||
constexpr basis_unit yard = distance_unit( 3*3048, 10000);
|
||||
/** @brief basis-unit representing 1 mile; defined as exactly 1760 yards = 5280 feet **/
|
||||
constexpr basis_unit mile = distance_unit( 5280*3048, 10000);
|
||||
///@}
|
||||
|
||||
// ----- time -----
|
||||
|
||||
constexpr basis_unit time_unit(std::int64_t num, std::int64_t den) {
|
||||
return basis_unit(dimension::time, scalefactor_ratio_type(num, den));
|
||||
}
|
||||
|
||||
/** @defgroup basis-unit-time-units basis_unit time units **/
|
||||
///@{
|
||||
/** basis unit of 10^-12 seconds **/
|
||||
constexpr basis_unit picosecond = time_unit( 1, 1000000000000);
|
||||
/** basis unit of 10^-9 seconds **/
|
||||
constexpr basis_unit nanosecond = time_unit( 1, 1000000000);
|
||||
/** basis unit of 10^-6 seconds **/
|
||||
constexpr basis_unit microsecond = time_unit( 1, 1000000);
|
||||
/** basis unit of 10^-3 seconds **/
|
||||
constexpr basis_unit millisecond = time_unit( 1, 1000);
|
||||
/** basis unit of 1 second **/
|
||||
constexpr basis_unit second = time_unit( 1, 1);
|
||||
/** basis unit of 1 minute = 60 seconds **/
|
||||
constexpr basis_unit minute = time_unit( 60, 1);
|
||||
/** basis unit of 1 hour = 3600 seconds **/
|
||||
constexpr basis_unit hour = time_unit( 3600, 1);
|
||||
/** basis unit of 1 day = exactly 24 hours **/
|
||||
constexpr basis_unit day = time_unit( 24*3600, 1);
|
||||
/** basis unit of 1 week = exactly 7 days **/
|
||||
constexpr basis_unit week = time_unit( 7*24*3600, 1);
|
||||
/** basis unit of 1 month = exactly 30 days **/
|
||||
constexpr basis_unit month = time_unit( 30*24*3600, 1);
|
||||
/** basis unit of 1 year, defined as 365.25 days **/
|
||||
constexpr basis_unit year = time_unit( (365*24+6)*3600, 1);
|
||||
|
||||
/* alt conventions used in finance */
|
||||
/** basis unit of 1 year365 = exactly 365 days **/
|
||||
constexpr basis_unit year365 = time_unit( 365*24*3600, 1);
|
||||
/** basis unit of 1 year360 = exactly 360 days **/
|
||||
constexpr basis_unit year360 = time_unit( 360*24*3600, 1);
|
||||
/** basis unit of 1 year250 = exactly 250 days.
|
||||
* Approximate number of business days in one year
|
||||
**/
|
||||
constexpr basis_unit year250 = time_unit( 250*24*3600, 1);
|
||||
|
||||
//constexpr basis_unit century = time_unit( 100L*(365*24+6)*3600, 1);
|
||||
//constexpr basis_unit millenium = time_unit(1000L*(365*24+6)*3600, 1);
|
||||
///@}
|
||||
|
||||
// ----- currency -----
|
||||
|
||||
/** @defgroup basis-unit-misc-units basis_unit miscellaneous units**/
|
||||
///@{
|
||||
|
||||
constexpr basis_unit currency_unit(std::int64_t num, std::int64_t den) {
|
||||
return basis_unit(dimension::currency, scalefactor_ratio_type(num, den));
|
||||
}
|
||||
|
||||
/** pseudounit -- placeholder for any actual currency amount **/
|
||||
constexpr basis_unit currency = currency_unit(1, 1);
|
||||
|
||||
// ----- price -----
|
||||
|
||||
constexpr basis_unit price_unit(std::int64_t num, std::int64_t den) {
|
||||
return basis_unit(dimension::price, scalefactor_ratio_type(num, den));
|
||||
}
|
||||
|
||||
/** psuedounit -- context-dependent interpretation for a screen price **/
|
||||
constexpr basis_unit price = price_unit(1, 1);
|
||||
///@}
|
||||
} /*namespace bu*/
|
||||
} /*namespace detail*/
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end basis_unit.hpp **/
|
||||
188
xo-unit/include/xo/unit/bpu.hpp
Normal file
188
xo-unit/include/xo/unit/bpu.hpp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/** @file bpu.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "basis_unit.hpp"
|
||||
#include "bu_store.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
namespace abbrev {
|
||||
/** fixed-size string representation for exponent of a basis-power-unit **/
|
||||
using power_abbrev_type = flatstring<16>;
|
||||
|
||||
/** @defgroup bpu-abbrev-helpers bpu abbrev helpers **/
|
||||
///@{
|
||||
/** @brief construct prefix string for unit exponent
|
||||
*
|
||||
* Auxiliary function for @ref bpu_abbrev
|
||||
**/
|
||||
constexpr power_abbrev_type
|
||||
flatstring_from_exponent(const power_ratio_type & power)
|
||||
{
|
||||
if (power.den() == 1) {
|
||||
if (power.num() == 1) {
|
||||
/* for no exponent annotation for power ^1 */
|
||||
return power_abbrev_type::from_chars("");
|
||||
} else {
|
||||
/* e.g. "^-1", "^2" */
|
||||
return (power_abbrev_type::from_flatstring
|
||||
(flatstring_concat(flatstring("^"),
|
||||
power_abbrev_type::from_int(power.num()))));
|
||||
}
|
||||
} else {
|
||||
/* e.g. "^1/2", "^-1/2" */
|
||||
return (power_abbrev_type::from_flatstring
|
||||
(flatstring_concat(flatstring("^"),
|
||||
power.to_str<power_abbrev_type::fixed_capacity>())));
|
||||
}
|
||||
}
|
||||
|
||||
/** construct suffix abbreviation for a basis-power-unit **/
|
||||
static constexpr bpu_abbrev_type
|
||||
bpu_abbrev(dim native_dim,
|
||||
const scalefactor_ratio_type & scalefactor,
|
||||
const power_ratio_type & power)
|
||||
{
|
||||
return (bpu_abbrev_type::from_flatstring
|
||||
(flatstring_concat
|
||||
(bu_abbrev(basis_unit(native_dim, scalefactor)),
|
||||
flatstring_from_exponent(power))));
|
||||
}
|
||||
///@}
|
||||
}
|
||||
|
||||
/** @class bpu
|
||||
*
|
||||
* @brief represent product of a compile-time scale-factor with a rational power of a native unit
|
||||
**/
|
||||
template<typename Int>
|
||||
struct bpu {
|
||||
public:
|
||||
using ratio_int_type = Int;
|
||||
|
||||
public:
|
||||
/** @defgroup bpu-ctors bpu constructors **/
|
||||
///@{
|
||||
/** default constructor. creates dimensionless bpu,
|
||||
* representing zero'th power of sentinel basis unit
|
||||
**/
|
||||
constexpr bpu() = default;
|
||||
/** construct @c bpu representing exponent @p power of basis unit @p bu **/
|
||||
constexpr bpu(const basis_unit & bu,
|
||||
const power_ratio_type & power)
|
||||
: bu_{bu},
|
||||
power_{power}
|
||||
{}
|
||||
/** construct @c bpu representing exponent @p power of @c basis_unit(native_dim,scalefactor) **/
|
||||
constexpr bpu(dim native_dim,
|
||||
const scalefactor_ratio_type & scalefactor,
|
||||
const power_ratio_type & power)
|
||||
: bu_(native_dim, scalefactor),
|
||||
power_{power}
|
||||
{}
|
||||
|
||||
/** construct bpu representing basis unit @p bu, i.e. with unit exponent **/
|
||||
static constexpr bpu<Int> unit_power(const basis_unit & bu) {
|
||||
return bpu<Int>(bu, power_ratio_type(1,1));
|
||||
}
|
||||
///@}
|
||||
|
||||
/** @defgroup bpu-access-methods bpu access methods **/
|
||||
///@{
|
||||
/** @brief report this bpu's @ref basis_unit, e.g. @c detail::bu::minute **/
|
||||
constexpr const basis_unit & bu() const { return bu_; }
|
||||
/** @brief report this bpu's @ref dimension, e.g. @c dimension::time **/
|
||||
constexpr dimension native_dim() const { return bu_.native_dim(); }
|
||||
/** @brief report this bpu's scale factor, e.g. @c 60/1 for @c detail::bu::minute **/
|
||||
constexpr const scalefactor_ratio_type & scalefactor() const { return bu_.scalefactor(); }
|
||||
/** @brief report this bpu's exponent, e.g. @c 3/1 for bpu representing cubic meters **/
|
||||
constexpr const power_ratio_type & power() const { return power_; }
|
||||
///@}
|
||||
|
||||
/** @defgroup bpu-methods **/
|
||||
///@{
|
||||
/** abbreviation for this dimension
|
||||
*
|
||||
* @code
|
||||
* bpu<int64_t>(dim::time,
|
||||
* scalefactor_ratio_type(60,1),
|
||||
* power_ratio_type(-2,1)).abbrev() => "min^-2"
|
||||
* @endcode
|
||||
**/
|
||||
constexpr bpu_abbrev_type abbrev() const
|
||||
{
|
||||
return abbrev::bpu_abbrev(bu_.native_dim_,
|
||||
bu_.scalefactor_,
|
||||
power_);
|
||||
}
|
||||
|
||||
/** for bpu @c x, @c x.reciprocal() represents dimension of @c 1/x
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* constexpr auto x = bpu<int64_t>(dim::time,
|
||||
* scalefactor_ratio_type(60,1),
|
||||
* power_ratio_type(1));
|
||||
* x.abbrev() => "min"
|
||||
* x.reciprocal().abbrev() => "min^-1"
|
||||
* @endcode
|
||||
**/
|
||||
constexpr bpu<Int> reciprocal() const {
|
||||
return bpu<Int>(bu_.native_dim(), bu_.scalefactor(), power_.negate());
|
||||
}
|
||||
|
||||
/** construct bpu representing the same unit, but using @c Int2 to represent exponenct **/
|
||||
template <typename Int2>
|
||||
constexpr bpu<Int2> to_repr() const {
|
||||
return bpu<Int2>(this->native_dim(),
|
||||
this->scalefactor(),
|
||||
ratio::ratio<Int2>(power_.num(), power_.den()));
|
||||
}
|
||||
///@}
|
||||
|
||||
public: /* need public members so that a basis_unit instance can be a non-type template parameter (a structural type) */
|
||||
/** @defgroup bpu-instance-vars **/
|
||||
///@{
|
||||
/** this @c bpu represent a power of basis unit @c bu.
|
||||
*
|
||||
* Public to avoid disqualifying @c bpu as a 'structural type'.
|
||||
**/
|
||||
struct basis_unit bu_;
|
||||
/** this unit represents basis dimension (bu) taken to this power
|
||||
*
|
||||
* Public to avoid disqualifying @c bpu as a 'structural type'.
|
||||
**/
|
||||
power_ratio_type power_ = {};
|
||||
///@}
|
||||
};
|
||||
|
||||
/** @defgroup bpu-comparison **/
|
||||
///@{
|
||||
/** @brief compare bpus @p x and @p y for equality
|
||||
*
|
||||
* Equality requires that both basis unit and power are equal
|
||||
**/
|
||||
template <typename Int>
|
||||
inline constexpr bool
|
||||
operator==(const bpu<Int> & x, const bpu<Int> & y) {
|
||||
return ((x.bu() == y.bu())
|
||||
&& (x.power_ == y.power_));
|
||||
}
|
||||
|
||||
/** @brief compare bpus @p x and @p y for inequality **/
|
||||
template <typename Int>
|
||||
inline constexpr bool
|
||||
operator!=(const bpu<Int> & x, const bpu<Int> & y) {
|
||||
return ((x.bu() != y.bu())
|
||||
|| (x.power_ != y.power_));
|
||||
}
|
||||
///@}
|
||||
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end bpu.hpp **/
|
||||
30
xo-unit/include/xo/unit/bpu_iostream.hpp
Normal file
30
xo-unit/include/xo/unit/bpu_iostream.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/** @file bpu_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bpu.hpp"
|
||||
#include "dim_iostream.hpp"
|
||||
#include "xo/ratio/ratio_iostream.hpp"
|
||||
#include "xo/indentlog/print/tag.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
template <typename Int>
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, const bpu<Int> & x) {
|
||||
os << "<bpu"
|
||||
<< xtag("dim", x.native_dim())
|
||||
<< xtag("mult", x.scalefactor())
|
||||
<< xtag("pwr", x.power())
|
||||
<< ">";
|
||||
|
||||
return os;
|
||||
}
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end bpu_iostream.hpp **/
|
||||
279
xo-unit/include/xo/unit/bu_store.hpp
Normal file
279
xo-unit/include/xo/unit/bu_store.hpp
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
/** @file bu_store.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
//#include "bpu.hpp"
|
||||
#include "basis_unit.hpp"
|
||||
#include "xo/ratio/ratio.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
using bpu_abbrev_type = flatstring<24>;
|
||||
|
||||
using power_ratio_type = xo::ratio::ratio<std::int64_t>;
|
||||
|
||||
namespace detail {
|
||||
/** @class bu_dim_store
|
||||
* @brief store basis-unit abbreviations for a particular dimension
|
||||
**/
|
||||
struct bu_dim_store {
|
||||
/** max number of basis-units per dimension **/
|
||||
static constexpr std::size_t max_bu_per_dim = 25;
|
||||
|
||||
/** @defgroup bu-dim-store-type-traits bu-dim-store type traits **/
|
||||
///@{
|
||||
using entry_type = std::pair<scalefactor2x_ratio_type, bu_abbrev_type>;
|
||||
|
||||
/* e.g.
|
||||
* [(1/1000000000, "nm"), (1/1000000, "um"), (1/1000, "mm"), (1/1, "m"), (1000/1, "km")]
|
||||
*/
|
||||
using native_scale_v = std::array<entry_type, max_bu_per_dim>;
|
||||
///@}
|
||||
|
||||
public:
|
||||
constexpr bu_dim_store() = default;
|
||||
|
||||
constexpr bool empty() const { return n_bu_ == 0; }
|
||||
constexpr std::size_t size() const { return n_bu_; }
|
||||
|
||||
constexpr const entry_type & operator[](std::size_t i) const { return bu_abbrev_v_[i]; }
|
||||
|
||||
/** @brief get least-upper-bound index position in bu_abbrev_v[]
|
||||
*
|
||||
* return value in [0, n] where n = .size()
|
||||
**/
|
||||
constexpr std::size_t abbrev_lub_ix(const scalefactor_ratio_type & scalefactor) const
|
||||
{
|
||||
if (n_bu_ == 0)
|
||||
return 0;
|
||||
|
||||
std::size_t lo = 0;
|
||||
std::size_t hi = n_bu_-1;
|
||||
|
||||
if (scalefactor <= bu_abbrev_v_[lo].first)
|
||||
return 0;
|
||||
|
||||
auto cmp = (scalefactor <=> bu_abbrev_v_[hi].first);
|
||||
|
||||
if (cmp > 0)
|
||||
return n_bu_;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
constexpr void insert_aux(std::size_t ix,
|
||||
const entry_type & entry)
|
||||
{
|
||||
|
||||
if (n_bu_ >= max_bu_per_dim)
|
||||
return;
|
||||
|
||||
++n_bu_;
|
||||
|
||||
for (std::size_t dest_ix = n_bu_; dest_ix > ix; --dest_ix)
|
||||
bu_abbrev_v_[dest_ix] = bu_abbrev_v_[dest_ix - 1];
|
||||
|
||||
bu_abbrev_v_[ix] = entry;
|
||||
}
|
||||
|
||||
/** @brief establish abbreviation @p abbrev for basis unit @p bu
|
||||
**/
|
||||
constexpr void bu_establish_abbrev(const scalefactor_ratio_type & scalefactor,
|
||||
const bu_abbrev_type & abbrev)
|
||||
{
|
||||
|
||||
std::int32_t i_abbrev = this->abbrev_lub_ix(scalefactor);
|
||||
|
||||
auto entry = std::make_pair(scalefactor, abbrev);
|
||||
|
||||
if ((i_abbrev < bu_abbrev_v_.size())
|
||||
&& (bu_abbrev_v_[i_abbrev].first == scalefactor))
|
||||
{
|
||||
bu_abbrev_v_[i_abbrev] = entry;
|
||||
} else {
|
||||
this->insert_aux(i_abbrev, entry);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/** @defgroup bu-dim-store-instance-vars bu-dim-store instance vars **/
|
||||
///@{
|
||||
std::size_t n_bu_ = 0;
|
||||
std::array<entry_type, max_bu_per_dim> bu_abbrev_v_;
|
||||
///@}
|
||||
}; /*bu_dim_store*/
|
||||
|
||||
/** @class bu_store
|
||||
* @brief associate basis units with abbreviations
|
||||
**/
|
||||
struct bu_store {
|
||||
/** @defgroup bu-store-constructors bu-store constructors **/
|
||||
///@{
|
||||
/** construct canonical instance containing all known basis units **/
|
||||
constexpr bu_store() {
|
||||
// ----- mass -----
|
||||
|
||||
this->bu_establish_abbrev(detail::bu::picogram, bu_abbrev_type::from_chars("pg"));
|
||||
this->bu_establish_abbrev(detail::bu::nanogram, bu_abbrev_type::from_chars("ng"));
|
||||
this->bu_establish_abbrev(detail::bu::microgram, bu_abbrev_type::from_chars("ug"));
|
||||
this->bu_establish_abbrev(detail::bu::milligram, bu_abbrev_type::from_chars("mg"));
|
||||
this->bu_establish_abbrev(detail::bu::gram, bu_abbrev_type::from_chars("g"));
|
||||
this->bu_establish_abbrev(detail::bu::kilogram, bu_abbrev_type::from_chars("kg"));
|
||||
this->bu_establish_abbrev(detail::bu::tonne, bu_abbrev_type::from_chars("t"));
|
||||
this->bu_establish_abbrev(detail::bu::kilotonne, bu_abbrev_type::from_chars("kt"));
|
||||
this->bu_establish_abbrev(detail::bu::megatonne, bu_abbrev_type::from_chars("Mt"));
|
||||
this->bu_establish_abbrev(detail::bu::gigatonne, bu_abbrev_type::from_chars("Gt"));
|
||||
|
||||
// ----- distance -----
|
||||
|
||||
this->bu_establish_abbrev(detail::bu::picometer, bu_abbrev_type::from_chars("pm"));
|
||||
this->bu_establish_abbrev(detail::bu::nanometer, bu_abbrev_type::from_chars("nm"));
|
||||
this->bu_establish_abbrev(detail::bu::micrometer, bu_abbrev_type::from_chars("um"));
|
||||
this->bu_establish_abbrev(detail::bu::millimeter, bu_abbrev_type::from_chars("mm"));
|
||||
this->bu_establish_abbrev(detail::bu::meter, bu_abbrev_type::from_chars("m"));
|
||||
this->bu_establish_abbrev(detail::bu::kilometer, bu_abbrev_type::from_chars("km"));
|
||||
this->bu_establish_abbrev(detail::bu::megameter, bu_abbrev_type::from_chars("Mm"));
|
||||
this->bu_establish_abbrev(detail::bu::gigameter, bu_abbrev_type::from_chars("Gm"));
|
||||
|
||||
this->bu_establish_abbrev(detail::bu::lightsecond, bu_abbrev_type::from_chars("lsec"));
|
||||
this->bu_establish_abbrev(detail::bu::astronomicalunit, bu_abbrev_type::from_chars("AU"));
|
||||
|
||||
this->bu_establish_abbrev(detail::bu::inch, bu_abbrev_type::from_chars("in"));
|
||||
this->bu_establish_abbrev(detail::bu::foot, bu_abbrev_type::from_chars("ft"));
|
||||
this->bu_establish_abbrev(detail::bu::yard, bu_abbrev_type::from_chars("yd"));
|
||||
this->bu_establish_abbrev(detail::bu::mile, bu_abbrev_type::from_chars("mi"));
|
||||
|
||||
// ----- time -----
|
||||
|
||||
this->bu_establish_abbrev(detail::bu::picosecond, bu_abbrev_type::from_chars("ps"));
|
||||
this->bu_establish_abbrev(detail::bu::nanosecond, bu_abbrev_type::from_chars("ns"));
|
||||
this->bu_establish_abbrev(detail::bu::microsecond, bu_abbrev_type::from_chars("us"));
|
||||
this->bu_establish_abbrev(detail::bu::millisecond, bu_abbrev_type::from_chars("ms"));
|
||||
this->bu_establish_abbrev(detail::bu::second, bu_abbrev_type::from_chars("s"));
|
||||
this->bu_establish_abbrev(detail::bu::minute, bu_abbrev_type::from_chars("min"));
|
||||
this->bu_establish_abbrev(detail::bu::hour, bu_abbrev_type::from_chars("hr"));
|
||||
this->bu_establish_abbrev(detail::bu::day, bu_abbrev_type::from_chars("dy"));
|
||||
this->bu_establish_abbrev(detail::bu::week, bu_abbrev_type::from_chars("wk"));
|
||||
this->bu_establish_abbrev(detail::bu::month, bu_abbrev_type::from_chars("mo"));
|
||||
this->bu_establish_abbrev(detail::bu::year250, bu_abbrev_type::from_chars("yr250"));
|
||||
this->bu_establish_abbrev(detail::bu::year, bu_abbrev_type::from_chars("yr"));
|
||||
this->bu_establish_abbrev(detail::bu::year360, bu_abbrev_type::from_chars("yr360"));
|
||||
this->bu_establish_abbrev(detail::bu::year365, bu_abbrev_type::from_chars("yr365"));
|
||||
|
||||
// ----- misc (currency, price) -----
|
||||
|
||||
this->bu_establish_abbrev(detail::bu::currency, bu_abbrev_type::from_chars("ccy"));
|
||||
this->bu_establish_abbrev(detail::bu::price, bu_abbrev_type::from_chars("px"));
|
||||
}
|
||||
///@}
|
||||
|
||||
/** @defgroup bu-store-implementation-methods **/
|
||||
///@{
|
||||
/** report fallback abbreviation for a basis unit.
|
||||
*
|
||||
* Typically unused. Will be invoked only if a basis unit exists for which
|
||||
* @ref bu_store::bu_establish_abbrev was not called by bu_store's constructor.
|
||||
*
|
||||
* Tries to produce something unambiguous,
|
||||
* while still supplying enough information
|
||||
* to indicate what's needed to resolve the problem.
|
||||
*
|
||||
* Example
|
||||
* @code
|
||||
* bu_store.bu_fallback_abbrev(dim::mass, scalefactor_ratio_type(1234,1))
|
||||
* => "1234g"
|
||||
* @endcode
|
||||
**/
|
||||
static constexpr bu_abbrev_type
|
||||
bu_fallback_abbrev(dim basis_dim,
|
||||
const scalefactor_ratio_type & scalefactor)
|
||||
{
|
||||
return (bu_abbrev_type::from_flatstring
|
||||
(flatstring_concat
|
||||
(scalefactor.to_str<bu_abbrev_type::fixed_capacity>(),
|
||||
native_unit2_v[static_cast<std::uint32_t>(basis_dim)].abbrev_str())));
|
||||
}
|
||||
///@}
|
||||
|
||||
/** @defgroup bu-store-access-methods **/
|
||||
///@{
|
||||
/** @brief get basis-unit abbreviation at runtime **/
|
||||
constexpr bu_abbrev_type bu_abbrev(const basis_unit & bu) const
|
||||
{
|
||||
const auto & bu_abbrev_v = bu_abbrev_vv_[static_cast<std::size_t>(bu.native_dim())];
|
||||
|
||||
std::size_t i_abbrev = bu_abbrev_v.abbrev_lub_ix(bu.scalefactor());
|
||||
|
||||
if ((i_abbrev < bu_abbrev_v.size())
|
||||
&& (bu_abbrev_v[i_abbrev].first == bu.scalefactor()))
|
||||
{
|
||||
return bu_abbrev_v[i_abbrev].second;
|
||||
} else {
|
||||
return bu_fallback_abbrev(bu.native_dim(), bu.scalefactor());
|
||||
}
|
||||
}
|
||||
///@}
|
||||
|
||||
/** @addtogroup bu-store-implementation-methods **/
|
||||
///@{
|
||||
/** associate abbreviation @p abbrev with basis unit @p bu
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* // femtograms
|
||||
* bu_store.bu_establish_abbrev(detail::bu::mass_unit(1, 1000000000000000), "fg")
|
||||
* @endcode
|
||||
**/
|
||||
constexpr void bu_establish_abbrev(const basis_unit & bu,
|
||||
const bu_abbrev_type & abbrev) {
|
||||
auto & dim_store = bu_abbrev_vv_[static_cast<std::size_t>(bu.native_dim_)];
|
||||
|
||||
dim_store.bu_establish_abbrev(bu.scalefactor_, abbrev);
|
||||
}
|
||||
///@}
|
||||
|
||||
public: /* ntoe: public members required so bu_dim_store can be a structural type */
|
||||
/** @defgroup bu-store-instance-vars **/
|
||||
///@{
|
||||
/** bu-store contents, indexed by native dimension **/
|
||||
std::array<bu_dim_store, n_dim> bu_abbrev_vv_;
|
||||
///@}
|
||||
};
|
||||
} /*namespace detail*/
|
||||
|
||||
/** @brief global abbreviation store.
|
||||
*
|
||||
* @note
|
||||
* Extending the contents of this store at runtime is not supported,
|
||||
* in favor of preserving constexpr abbreviations.
|
||||
**/
|
||||
static constexpr detail::bu_store bu_abbrev_store;
|
||||
|
||||
/** @brief get abbreviation for basis-unit @p bu **/
|
||||
constexpr bu_abbrev_type
|
||||
bu_abbrev(const basis_unit & bu)
|
||||
{
|
||||
return bu_abbrev_store.bu_abbrev(bu);
|
||||
}
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end bu_store.hpp **/
|
||||
21
xo-unit/include/xo/unit/dim_iostream.hpp
Normal file
21
xo-unit/include/xo/unit/dim_iostream.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/** @file dim_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dimension.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, dim x) {
|
||||
os << dim2str(x);
|
||||
return os;
|
||||
}
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end dim_iostream.hpp **/
|
||||
65
xo-unit/include/xo/unit/dimension.hpp
Normal file
65
xo-unit/include/xo/unit/dimension.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* @file dimension.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
/** @enum dimension
|
||||
* @brief represent an abstract dimension.
|
||||
*
|
||||
* *xo-unit* units are expressed as a cartesian product
|
||||
* of powers of these dimensions.
|
||||
**/
|
||||
enum class dimension {
|
||||
/** sentinel value. not a dimension **/
|
||||
invalid = -1,
|
||||
|
||||
/** weight. native unit = 1 gram **/
|
||||
mass,
|
||||
/** distance. native unit = 1 meter **/
|
||||
distance,
|
||||
/** time. native unit = 1 second **/
|
||||
time,
|
||||
/** a currency amount. native unit depends on actual currency.
|
||||
* For USD: one US dollar.
|
||||
*
|
||||
* NOTE: multicurrency work not supported by *xo-unit*.
|
||||
* - (1usd + 1eur) is well-defined.
|
||||
* - (1sec + 1m) is not.
|
||||
**/
|
||||
currency,
|
||||
/** A screen price.
|
||||
* The interpretation of prices is highly context dependent;
|
||||
* expect useful to bucket separately from currenty amounts.
|
||||
**/
|
||||
price,
|
||||
|
||||
/** not a dimension. comes last, counts entries **/
|
||||
n_dim
|
||||
};
|
||||
|
||||
using dim = dimension;
|
||||
|
||||
/** @brief string value for a dimension enum **/
|
||||
inline const char *
|
||||
dim2str(dimension x)
|
||||
{
|
||||
switch(x) {
|
||||
case dimension::mass: return "mass";
|
||||
case dimension::distance: return "distance";
|
||||
case dimension::time: return "time";
|
||||
case dimension::currency: return "currency";
|
||||
case dimension::price: return "price";
|
||||
default: break;
|
||||
}
|
||||
return "?dim";
|
||||
}
|
||||
|
||||
/** @brief number of built-in dimensions, convenient for array sizing **/
|
||||
static constexpr std::size_t n_dim = static_cast<std::size_t>(dimension::n_dim);
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end dimension.hpp */
|
||||
49
xo-unit/include/xo/unit/native_unit.hpp
Normal file
49
xo-unit/include/xo/unit/native_unit.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/** @file native_unit.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dimension.hpp"
|
||||
#include "xo/flatstring/flatstring.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
using native_unit2_abbrev_type = flatstring<8>;
|
||||
|
||||
/** @class native_unit
|
||||
*
|
||||
* @brief Represent a native (built-in) unit.
|
||||
*
|
||||
* A basis_unit is expressed as a multiple of a native_unit
|
||||
*
|
||||
**/
|
||||
struct native_unit {
|
||||
public:
|
||||
constexpr native_unit(dimension native_dim,
|
||||
const native_unit2_abbrev_type & abbrev_str)
|
||||
: native_dim_{native_dim},
|
||||
abbrev_str_{abbrev_str}
|
||||
{}
|
||||
|
||||
constexpr dimension native_dim() const { return native_dim_; }
|
||||
constexpr const native_unit2_abbrev_type & abbrev_str() const { return abbrev_str_; }
|
||||
|
||||
private:
|
||||
dimension native_dim_;
|
||||
native_unit2_abbrev_type abbrev_str_;
|
||||
};
|
||||
|
||||
static constexpr native_unit native_unit2_v[n_dim] = {
|
||||
native_unit(dimension::mass, native_unit2_abbrev_type::from_chars("g")),
|
||||
native_unit(dimension::distance, native_unit2_abbrev_type::from_chars("m")),
|
||||
native_unit(dimension::time, native_unit2_abbrev_type::from_chars("s")),
|
||||
native_unit(dimension::currency, native_unit2_abbrev_type::from_chars("ccy")),
|
||||
native_unit(dimension::price, native_unit2_abbrev_type::from_chars("px")),
|
||||
};
|
||||
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end native_unit.hpp **/
|
||||
527
xo-unit/include/xo/unit/natural_unit.hpp
Normal file
527
xo-unit/include/xo/unit/natural_unit.hpp
Normal file
|
|
@ -0,0 +1,527 @@
|
|||
/** @file natural_unit.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bpu.hpp"
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
using nu_abbrev_type = flatstring<32>;
|
||||
|
||||
template <typename Int>
|
||||
class natural_unit;
|
||||
|
||||
namespace detail {
|
||||
template <typename Int, typename... Ts>
|
||||
constexpr void
|
||||
push_bpu_array(natural_unit<Int> * p_target, Ts... args);
|
||||
|
||||
template <typename Int>
|
||||
constexpr void
|
||||
push_bpu_array(natural_unit<Int> * p_target) {}
|
||||
|
||||
template <typename Int, typename T0, typename... Ts>
|
||||
constexpr void
|
||||
push_bpu_array(natural_unit<Int> * p_target, T0 && bpu0, Ts... args) {
|
||||
p_target->push_back(bpu0);
|
||||
push_bpu_array(p_target, args...);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
struct nu_maker {
|
||||
template <typename... Ts>
|
||||
static constexpr natural_unit<Int>
|
||||
make_nu(Ts... args) {
|
||||
natural_unit<Int> bpu_array;
|
||||
detail::push_bpu_array(&bpu_array, args...);
|
||||
return bpu_array;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @class natural_unit
|
||||
* @brief an array representing the cartesian product of distinct basis-power-units
|
||||
*
|
||||
* 1. Quantities are represented as a multiple of a natural unit
|
||||
* 2. Each bpu in the array represents a power of a basis dimension, e.g. "meter" or "second^2".
|
||||
* 3. Each bpu in an array has a different dimension id.
|
||||
* For example @c dim::time, if present, appears once.
|
||||
* 4. Basis dimensions can appear in any order.
|
||||
* Order used for constructing abbreviations: will get @c "kg.m" or @c "m.kg"
|
||||
* depending on the ordering of @c dim::distance and @c dim::mass in @c bpu_v_
|
||||
*
|
||||
* @c Int supplies representation for numerator and denominator in basis-unit scale factors.
|
||||
**/
|
||||
template <typename Int>
|
||||
class natural_unit {
|
||||
public:
|
||||
/** @defgroup natural-unit-type-traits natural unit type traits **/
|
||||
///@{
|
||||
/** @brief representation for numerator and denominator of scalefactor ratios **/
|
||||
using ratio_int_type = Int;
|
||||
///@}
|
||||
|
||||
public:
|
||||
/** @addtogroup natural-unit-ctors **/
|
||||
///@{
|
||||
|
||||
/** construct dimensionless unit **/
|
||||
constexpr natural_unit() : n_bpu_{0} {}
|
||||
|
||||
/** construct unit representing basis unit @p bu with exponent @p power **/
|
||||
static constexpr natural_unit from_bu(basis_unit bu,
|
||||
power_ratio_type power = power_ratio_type(1)) {
|
||||
return detail::nu_maker<Int>::make_nu(bpu<Int>(bu, power));
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** @addtogroup natural-unit-access-methods **/
|
||||
///@{
|
||||
|
||||
/** always true. Provided for symmetry with @c xo::qty::scaled_unit::is_natural **/
|
||||
constexpr bool is_natural() const { return true; }
|
||||
|
||||
/** get member @c n_bpu **/
|
||||
constexpr std::size_t n_bpu() const { return n_bpu_; }
|
||||
/** true if this unit has no dimension **/
|
||||
constexpr bool is_dimensionless() const { return n_bpu_ == 0; }
|
||||
|
||||
/** get address of member @c bpu_v **/
|
||||
constexpr bpu<Int> * bpu_v() const { return bpu_v_; }
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup natural-unit-methods **/
|
||||
///@{
|
||||
|
||||
/** construct reciprocal of this unit.
|
||||
*
|
||||
* For example reciprocal of a newton (abbreviation @c "kg.m.s^-2") is
|
||||
* a unit with abbreviation @c "kg^-1.m^-1.s^2"
|
||||
**/
|
||||
constexpr natural_unit reciprocal() const {
|
||||
natural_unit retval;
|
||||
|
||||
for (std::size_t i = 0; i < this->n_bpu(); ++i)
|
||||
retval.push_back((*this)[i].reciprocal());
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** abbreviation for this unit.
|
||||
*
|
||||
* Apply as suffix when printing quantities involving this unit.
|
||||
*
|
||||
* For example @c "mm" for millimeters, or @c "ns" for nanoseconds
|
||||
**/
|
||||
constexpr nu_abbrev_type abbrev() const {
|
||||
nu_abbrev_type retval;
|
||||
|
||||
for (std::size_t i = 0; i < n_bpu_; ++i) {
|
||||
if (i > 0)
|
||||
retval.append(".");
|
||||
retval.append(bpu_v_[i].abbrev(), 0, -1);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** remove bpu at position @p p **/
|
||||
constexpr void remove_bpu(size_t p) {
|
||||
for (std::size_t i = p; i+1 < n_bpu_; ++i)
|
||||
bpu_v_[i] = bpu_v_[i+1];
|
||||
|
||||
--n_bpu_;
|
||||
}
|
||||
|
||||
/** append @p bpu to this unit in-place
|
||||
*
|
||||
* Require @c bpu.native_dim does not match any existing member of @ref bpu_v_
|
||||
**/
|
||||
constexpr void push_back(const bpu<Int> & bpu) {
|
||||
if (n_bpu_ < n_dim)
|
||||
bpu_v_[n_bpu_++] = bpu;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** @addtogroup natural-unit-access-methods **/
|
||||
///@{
|
||||
|
||||
/** get bpu for dimension @p d. if d isn't present, construct bpu with 0 power **/
|
||||
constexpr bpu<Int> lookup_dim(dimension d) const {
|
||||
for (std::size_t i = 0, n = n_bpu(); i<n; ++i) {
|
||||
if (d == bpu_v_[i].native_dim())
|
||||
return bpu_v_[i];
|
||||
}
|
||||
|
||||
/** not found, return sentinel **/
|
||||
return bpu<Int>(d, scalefactor_ratio_type(0), power_ratio_type(0));
|
||||
}
|
||||
|
||||
/** get element @p i of @ref bpu_v_ **/
|
||||
constexpr bpu<Int> & operator[](std::size_t i) { return bpu_v_[i]; }
|
||||
/** get element @p i of @ref bpu_v_ (const version) **/
|
||||
constexpr const bpu<Int> & operator[](std::size_t i) const { return bpu_v_[i]; }
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup natural-unit-conversion-methods **/
|
||||
///@{
|
||||
|
||||
/** convert to equivalent unit using scalefactor representation @p Int2 instead of
|
||||
* @ref ratio_int_type
|
||||
**/
|
||||
template <typename Int2>
|
||||
constexpr natural_unit<Int2> to_repr() const {
|
||||
natural_unit<Int2> retval;
|
||||
|
||||
std::size_t i = 0;
|
||||
for (; i < n_bpu_; ++i)
|
||||
retval.push_back(bpu_v_[i].template to_repr<Int2>());
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
public: /* public members so instance can be non-type template parameter (is a structural type) */
|
||||
/** @defgroup natural-unit-instance-vars **/
|
||||
///@{
|
||||
|
||||
/** the number of occupied slots in @c bpu_v_ **/
|
||||
std::size_t n_bpu_;
|
||||
|
||||
/** storage for basis power units **/
|
||||
bpu<Int> bpu_v_[n_dim];
|
||||
|
||||
///@}
|
||||
};
|
||||
|
||||
/** @defgroup natural-unit-comparison-functions natural-unit comparison functions **/
|
||||
///@{
|
||||
|
||||
/** compare natural units @p x, @p y for equality. **/
|
||||
template <typename Int>
|
||||
constexpr bool
|
||||
operator==(const natural_unit<Int> & x,
|
||||
const natural_unit<Int> & y)
|
||||
{
|
||||
if (x.n_bpu() != y.n_bpu())
|
||||
return false;
|
||||
|
||||
/* does x contain any dimension that isn't present in y? */
|
||||
for (std::size_t i = 0, n = x.n_bpu(); i<n; ++i) {
|
||||
const bpu<Int> & xi = x[i];
|
||||
if (xi != y.lookup_dim(xi.native_dim()))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* if all bpu's x[i] match something from y, then x,y must be equal
|
||||
* since they each have the same number of bpu's
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** compare natural units @p x, @p y for inequality **/
|
||||
template <typename Int>
|
||||
constexpr bool
|
||||
operator!=(const natural_unit<Int> & x,
|
||||
const natural_unit<Int> & y) {
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
namespace detail {
|
||||
/**
|
||||
* Given bpu ~ (b.u)^p:
|
||||
* - b = bpu.scalefactor
|
||||
* - u = bpu.native_dim
|
||||
* - p = bpu.power
|
||||
*
|
||||
* want to rewrite in the form a'.(b'.u)^p
|
||||
*
|
||||
* Can compute a' exactly iff p is integral.
|
||||
* In that case:
|
||||
* (b.u)^p = ((b/b').b'.u)^p
|
||||
* = (b/b')^p.(b'.u)^p
|
||||
* = a'.(b'.u)^p with a' = (b/b')^p
|
||||
*
|
||||
* Can write p = p0 + q, with p0 = floor(p) integral, q = frac(p) in [0,1)
|
||||
*
|
||||
* Then
|
||||
* (b/b')^p = (b/b')^p0 * (b/b')^q
|
||||
*
|
||||
* we'll compute:
|
||||
* - (b/b')^p0 exactly (as a ratio)
|
||||
* - (b/b')^q inexactly (as a double)
|
||||
**/
|
||||
|
||||
template <typename Int,
|
||||
typename OuterScale = ratio::ratio<Int> >
|
||||
struct outer_scalefactor_result {
|
||||
constexpr outer_scalefactor_result(const OuterScale & outer_scale_factor,
|
||||
double outer_scale_sq)
|
||||
: outer_scale_factor_{outer_scale_factor},
|
||||
outer_scale_sq_{outer_scale_sq} {}
|
||||
|
||||
/* (b/b')^p0 */
|
||||
OuterScale outer_scale_factor_;
|
||||
/* (b/b')^q -- until c++26 only allow q=0 or q=1/2 */
|
||||
double outer_scale_sq_;
|
||||
};
|
||||
|
||||
template <typename Int,
|
||||
typename OuterScale = ratio::ratio<Int> >
|
||||
struct bpu2_rescale_result {
|
||||
constexpr bpu2_rescale_result(const bpu<Int> & bpu_rescaled,
|
||||
const OuterScale & outer_scale_factor,
|
||||
double outer_scale_sq)
|
||||
: bpu_rescaled_{bpu_rescaled},
|
||||
outer_scale_factor_{outer_scale_factor},
|
||||
outer_scale_sq_{outer_scale_sq}
|
||||
{}
|
||||
|
||||
/* (b'.u)^p */
|
||||
bpu<Int> bpu_rescaled_;
|
||||
/* (b/b')^p0 */
|
||||
OuterScale outer_scale_factor_;
|
||||
/* [(b/b')^q]^2 -- until c++26 only allow q=0 or q=1/2 */
|
||||
double outer_scale_sq_;
|
||||
};
|
||||
|
||||
template < typename Int,
|
||||
typename OuterScale = ratio::ratio<Int> >
|
||||
constexpr
|
||||
bpu2_rescale_result<Int, OuterScale>
|
||||
bpu2_rescale(const bpu<Int> & orig,
|
||||
const scalefactor_ratio_type & new_scalefactor)
|
||||
{
|
||||
/* we have orig, representing qty (b.u)^p,
|
||||
* with b=orig.scalefactor, u=native dimension, p=orig.power
|
||||
*/
|
||||
|
||||
ratio::ratio<Int> mult = (orig.scalefactor() / new_scalefactor);
|
||||
|
||||
/* inv: p_frac in (-1, 1) */
|
||||
auto p_frac = orig.power().frac();
|
||||
|
||||
/* asof c++26: replace mult_sq with ::pow(mult, p_frac) */
|
||||
double mult_sq = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
/* pre-c++26 workaround */
|
||||
{
|
||||
if (p_frac.den() == 1) {
|
||||
mult_sq = 1.0;
|
||||
} else if(p_frac.num() == 1 && p_frac.den() == 2) {
|
||||
mult_sq = mult.template convert_to<double>();
|
||||
} else if(p_frac.num() == -1 && p_frac.den() == 2) {
|
||||
mult_sq = 1.0 / mult.template convert_to<double>();
|
||||
} else {
|
||||
// remaining possibilities not supported until c++26
|
||||
}
|
||||
}
|
||||
|
||||
ratio::ratio<Int> mult_p = mult.power(orig.power().floor());
|
||||
|
||||
return bpu2_rescale_result<Int, OuterScale>(bpu<Int>(orig.native_dim(),
|
||||
new_scalefactor,
|
||||
orig.power()),
|
||||
mult_p.template convert_to<OuterScale>(),
|
||||
mult_sq);
|
||||
}
|
||||
|
||||
template < typename Int,
|
||||
typename OuterScale >
|
||||
constexpr
|
||||
outer_scalefactor_result<Int, OuterScale>
|
||||
bpu_product_inplace(bpu<Int> * p_target_bpu,
|
||||
const bpu<Int> & rhs_bpu_orig)
|
||||
{
|
||||
assert(rhs_bpu_orig.native_dim() == p_target_bpu->native_dim());
|
||||
|
||||
bpu2_rescale_result<Int, OuterScale> rhs_bpu_rr
|
||||
= bpu2_rescale<Int, OuterScale>(rhs_bpu_orig,
|
||||
p_target_bpu->scalefactor().template convert_to<OuterScale>());
|
||||
|
||||
*p_target_bpu = bpu<Int>(p_target_bpu->native_dim(),
|
||||
p_target_bpu->scalefactor(),
|
||||
p_target_bpu->power() + rhs_bpu_orig.power());
|
||||
|
||||
return outer_scalefactor_result<Int>(rhs_bpu_rr.outer_scale_factor_,
|
||||
rhs_bpu_rr.outer_scale_sq_);
|
||||
}
|
||||
|
||||
template < typename Int,
|
||||
typename OuterScale >
|
||||
constexpr
|
||||
outer_scalefactor_result<Int, OuterScale>
|
||||
bpu_ratio_inplace(bpu<Int> * p_target_bpu,
|
||||
const bpu<Int> & rhs_bpu_orig)
|
||||
{
|
||||
assert(rhs_bpu_orig.native_dim() == p_target_bpu->native_dim());
|
||||
|
||||
bpu2_rescale_result<Int, OuterScale> rhs_bpu_rr
|
||||
= bpu2_rescale<Int, OuterScale>(rhs_bpu_orig,
|
||||
p_target_bpu->scalefactor());
|
||||
|
||||
*p_target_bpu = bpu<Int>(p_target_bpu->native_dim(),
|
||||
p_target_bpu->scalefactor(),
|
||||
p_target_bpu->power() - rhs_bpu_orig.power());
|
||||
|
||||
return outer_scalefactor_result<Int, OuterScale>
|
||||
(OuterScale(1) / rhs_bpu_rr.outer_scale_factor_,
|
||||
1.0 / rhs_bpu_rr.outer_scale_sq_);
|
||||
}
|
||||
|
||||
template < typename Int,
|
||||
typename OuterScale >
|
||||
constexpr
|
||||
outer_scalefactor_result<Int, OuterScale>
|
||||
nu_product_inplace(natural_unit<Int> * p_target,
|
||||
const bpu<Int> & bpu)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
for (; i < p_target->n_bpu(); ++i) {
|
||||
auto * p_target_bpu = &((*p_target)[i]);
|
||||
|
||||
if (p_target_bpu->native_dim() == bpu.native_dim()) {
|
||||
outer_scalefactor_result<Int, OuterScale> retval
|
||||
= bpu_product_inplace<Int, OuterScale>(p_target_bpu, bpu);
|
||||
|
||||
if (p_target_bpu->power().is_zero()) {
|
||||
/* dimension assoc'd with *p_target_bpu has been cancelled */
|
||||
p_target->remove_bpu(i);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/* control here: i=p_target->n_bpu()
|
||||
* Dimension represented by bpu does not already appear in *p_target.
|
||||
* Adopt bpu's scalefactor
|
||||
*/
|
||||
|
||||
p_target->push_back(bpu);
|
||||
|
||||
return outer_scalefactor_result<Int, OuterScale>
|
||||
(OuterScale(1) /*outer_scale_factor*/,
|
||||
1.0 /*outer_scale_sq*/);
|
||||
}
|
||||
|
||||
template < typename Int,
|
||||
typename OuterScale = ratio::ratio<Int> >
|
||||
constexpr
|
||||
outer_scalefactor_result<Int, OuterScale>
|
||||
nu_ratio_inplace(natural_unit<Int> * p_target,
|
||||
const bpu<Int> & bpu)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
for (; i < p_target->n_bpu(); ++i) {
|
||||
auto * p_target_bpu = &((*p_target)[i]);
|
||||
|
||||
if (p_target_bpu->native_dim() == bpu.native_dim()) {
|
||||
outer_scalefactor_result<Int, OuterScale> retval
|
||||
= bpu_ratio_inplace<Int, OuterScale>(p_target_bpu, bpu);
|
||||
|
||||
if (p_target_bpu->power().is_zero()) {
|
||||
/* dimension assoc'd with *p_target_bpu has been cancelled */
|
||||
p_target->remove_bpu(i);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/* here: i=p_target->n_bpu()
|
||||
* Dimension represented by bpu does not already appear in *p_target.
|
||||
* Adopt bpu's scalefactor
|
||||
*/
|
||||
|
||||
p_target->push_back(bpu.reciprocal());
|
||||
|
||||
return outer_scalefactor_result<Int, OuterScale>
|
||||
(OuterScale(1) /*outer_scale_factor*/,
|
||||
1.0 /*outer_scale_sq*/);
|
||||
}
|
||||
|
||||
} /*namespace detail*/
|
||||
|
||||
/** @brief namespace for constants representing basis natural units
|
||||
*
|
||||
* Application code will typically use instead parallel scaled-unit constants
|
||||
* (see the 'u' namespace in 'scaled_unit.hpp')
|
||||
**/
|
||||
namespace nu {
|
||||
constexpr auto dimensionless = natural_unit<std::int64_t>();
|
||||
|
||||
// ----- mass -----
|
||||
|
||||
constexpr auto picogram = natural_unit<std::int64_t>::from_bu(detail::bu::picogram);
|
||||
constexpr auto nanogram = natural_unit<std::int64_t>::from_bu(detail::bu::nanogram);
|
||||
constexpr auto microgram = natural_unit<std::int64_t>::from_bu(detail::bu::microgram);
|
||||
constexpr auto milligram = natural_unit<std::int64_t>::from_bu(detail::bu::milligram);
|
||||
constexpr auto gram = natural_unit<std::int64_t>::from_bu(detail::bu::gram);
|
||||
constexpr auto kilogram = natural_unit<std::int64_t>::from_bu(detail::bu::kilogram);
|
||||
constexpr auto tonne = natural_unit<std::int64_t>::from_bu(detail::bu::tonne);
|
||||
constexpr auto kilotonne = natural_unit<std::int64_t>::from_bu(detail::bu::kilotonne);
|
||||
constexpr auto megatonne = natural_unit<std::int64_t>::from_bu(detail::bu::megatonne);
|
||||
constexpr auto gigatonne = natural_unit<std::int64_t>::from_bu(detail::bu::gigatonne);
|
||||
|
||||
// ----- distance -----
|
||||
|
||||
constexpr auto picometer = natural_unit<std::int64_t>::from_bu(detail::bu::picometer);
|
||||
constexpr auto nanometer = natural_unit<std::int64_t>::from_bu(detail::bu::nanometer);
|
||||
constexpr auto micrometer = natural_unit<std::int64_t>::from_bu(detail::bu::micrometer);
|
||||
constexpr auto millimeter = natural_unit<std::int64_t>::from_bu(detail::bu::millimeter);
|
||||
constexpr auto meter = natural_unit<std::int64_t>::from_bu(detail::bu::meter);
|
||||
constexpr auto kilometer = natural_unit<std::int64_t>::from_bu(detail::bu::kilometer);
|
||||
constexpr auto megameter = natural_unit<std::int64_t>::from_bu(detail::bu::megameter);
|
||||
constexpr auto gigameter = natural_unit<std::int64_t>::from_bu(detail::bu::gigameter);
|
||||
constexpr auto lightsecond = natural_unit<std::int64_t>::from_bu(detail::bu::lightsecond);
|
||||
constexpr auto astronomicalunit = natural_unit<std::int64_t>::from_bu(detail::bu::astronomicalunit);
|
||||
|
||||
constexpr auto inch = natural_unit<std::int64_t>::from_bu(detail::bu::inch);
|
||||
constexpr auto foot = natural_unit<std::int64_t>::from_bu(detail::bu::foot);
|
||||
constexpr auto yard = natural_unit<std::int64_t>::from_bu(detail::bu::yard);
|
||||
constexpr auto mile = natural_unit<std::int64_t>::from_bu(detail::bu::mile);
|
||||
|
||||
// ----- time -----
|
||||
|
||||
constexpr auto picosecond = natural_unit<std::int64_t>::from_bu(detail::bu::picosecond);
|
||||
constexpr auto nanosecond = natural_unit<std::int64_t>::from_bu(detail::bu::nanosecond);
|
||||
constexpr auto microsecond = natural_unit<std::int64_t>::from_bu(detail::bu::microsecond);
|
||||
constexpr auto millisecond = natural_unit<std::int64_t>::from_bu(detail::bu::millisecond);
|
||||
constexpr auto second = natural_unit<std::int64_t>::from_bu(detail::bu::second);
|
||||
constexpr auto minute = natural_unit<std::int64_t>::from_bu(detail::bu::minute);
|
||||
constexpr auto hour = natural_unit<std::int64_t>::from_bu(detail::bu::hour);
|
||||
constexpr auto day = natural_unit<std::int64_t>::from_bu(detail::bu::day);
|
||||
constexpr auto week = natural_unit<std::int64_t>::from_bu(detail::bu::week);
|
||||
constexpr auto month = natural_unit<std::int64_t>::from_bu(detail::bu::month);
|
||||
constexpr auto year = natural_unit<std::int64_t>::from_bu(detail::bu::year);
|
||||
constexpr auto year250 = natural_unit<std::int64_t>::from_bu(detail::bu::year250);
|
||||
constexpr auto year360 = natural_unit<std::int64_t>::from_bu(detail::bu::year360);
|
||||
constexpr auto year365 = natural_unit<std::int64_t>::from_bu(detail::bu::year365);
|
||||
|
||||
constexpr auto currency = natural_unit<std::int64_t>::from_bu(detail::bu::currency);
|
||||
|
||||
constexpr auto price = natural_unit<std::int64_t>::from_bu(detail::bu::price);
|
||||
|
||||
constexpr auto volatility_30d = natural_unit<std::int64_t>::from_bu(detail::bu::month, power_ratio_type(-1,2));
|
||||
constexpr auto volatility_250d = natural_unit<std::int64_t>::from_bu(detail::bu::year250, power_ratio_type(-1,2));
|
||||
constexpr auto volatility_360d = natural_unit<std::int64_t>::from_bu(detail::bu::year360, power_ratio_type(-1,2));
|
||||
constexpr auto volatility_365d = natural_unit<std::int64_t>::from_bu(detail::bu::year365, power_ratio_type(-1,2));
|
||||
} /*namespace nu*/
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end natural_unit.hpp **/
|
||||
29
xo-unit/include/xo/unit/natural_unit_iostream.hpp
Normal file
29
xo-unit/include/xo/unit/natural_unit_iostream.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/** @file natural_unit_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "natural_unit.hpp"
|
||||
#include "bpu_iostream.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
template <typename Int>
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, const natural_unit<Int> & x) {
|
||||
os << "<natural-unit [";
|
||||
for (std::size_t i=0; i<x.n_bpu(); ++i) {
|
||||
if (i > 0)
|
||||
os << ", ";
|
||||
os << x[i];
|
||||
}
|
||||
os << "]>";
|
||||
return os;
|
||||
}
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end natural_unit_iostream.hpp **/
|
||||
34
xo-unit/include/xo/unit/numeric_concept.hpp
Normal file
34
xo-unit/include/xo/unit/numeric_concept.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* @file numeric_concept.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
/** @concept numeric_concept
|
||||
* @brief Concept for values that participate in arithmetic operations (+,-,*,/) and comparisons
|
||||
*
|
||||
* Intended to include at least:
|
||||
* - built-in integral and floating-point types
|
||||
* - xo::raio<U>
|
||||
* - xo::unit::quantity<U,R>
|
||||
*
|
||||
* Intend numeric_concept to apply to types suitable for
|
||||
* xo::unit::quantity::repr_type.
|
||||
**/
|
||||
template <typename T, typename U = T>
|
||||
concept numeric_concept = requires(T x, U y)
|
||||
{
|
||||
{ -x };
|
||||
{ x - y };
|
||||
{ x + y };
|
||||
{ x * y };
|
||||
{ x / y };
|
||||
{ x == y };
|
||||
{ x != y };
|
||||
};
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end numeric_concept.hpp */
|
||||
1018
xo-unit/include/xo/unit/quantity.hpp
Normal file
1018
xo-unit/include/xo/unit/quantity.hpp
Normal file
File diff suppressed because it is too large
Load diff
28
xo-unit/include/xo/unit/quantity_concept.hpp
Normal file
28
xo-unit/include/xo/unit/quantity_concept.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/** @file quantity_concept.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
//#include "unit_concept.hpp"
|
||||
#include "numeric_concept.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
template <typename Quantity>
|
||||
concept quantity_concept = requires(Quantity qty, typename Quantity::repr_type repr)
|
||||
{
|
||||
typename Quantity::unit_type;
|
||||
typename Quantity::repr_type;
|
||||
|
||||
//{ Quantity::multiply(qty, qty) };
|
||||
|
||||
{ qty.scale() } -> std::same_as<const typename Quantity::repr_type &>;
|
||||
{ qty.unit() } -> std::same_as<const typename Quantity::unit_type &>;
|
||||
//{ Quantity::unit_cstr() } -> std::same_as<char const *>;
|
||||
//{ Quantity::unit_quantity() } -> std::same_as<Quantity>;
|
||||
//{ Quantity::promote(repr) } -> std::same_as<Quantity>;
|
||||
} && (true //unit_concept<typename Quantity::unit_type>
|
||||
&& numeric_concept<typename Quantity::repr_type>);
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end quantity_concept.hpp */
|
||||
26
xo-unit/include/xo/unit/quantity_iostream.hpp
Normal file
26
xo-unit/include/xo/unit/quantity_iostream.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/** @file quantity_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "quantity.hpp"
|
||||
#include "natural_unit_iostream.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
template < auto NaturalUnit, typename Repr >
|
||||
inline std::ostream &
|
||||
operator<< (std::ostream & os,
|
||||
const quantity<NaturalUnit, Repr> & x)
|
||||
{
|
||||
os << x.scale() << x.abbrev();
|
||||
return os;
|
||||
}
|
||||
|
||||
} /*namespace qty*/
|
||||
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end quantity_iostream.hpp **/
|
||||
37
xo-unit/include/xo/unit/quantity_ops.hpp
Normal file
37
xo-unit/include/xo/unit/quantity_ops.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/** @file quantity_ops.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "quantity_concept.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
/** note: won't have constexpr result until c++26 (when @c sqrt(), @c pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity, typename Quantity2>
|
||||
requires quantity_concept<Quantity> && quantity_concept<Quantity2>
|
||||
constexpr auto
|
||||
operator== (const Quantity & x, const Quantity2 & y)
|
||||
{
|
||||
return (Quantity::compare(x, y) == 0);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when @c sqrt(), @c pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity, typename Quantity2>
|
||||
requires quantity_concept<Quantity> && quantity_concept<Quantity2>
|
||||
constexpr auto
|
||||
operator<=> (const Quantity & x, const Quantity2 & y)
|
||||
{
|
||||
return Quantity::compare(x, y);
|
||||
}
|
||||
|
||||
} /*namespace qty*/
|
||||
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end quantity_ops.hpp **/
|
||||
401
xo-unit/include/xo/unit/scaled_unit.hpp
Normal file
401
xo-unit/include/xo/unit/scaled_unit.hpp
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
/** @file scaled_unit.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "width2x.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<Int> >
|
||||
struct scaled_unit {
|
||||
/** @defgroup scaled-unit-type-traits scaled-unit type traits **/
|
||||
///@{
|
||||
|
||||
/** type for representing individual basis-unit scalefactors **/
|
||||
using ratio_int_type = typename natural_unit<Int>::ratio_int_type;
|
||||
|
||||
///@}
|
||||
|
||||
public:
|
||||
/** @defgroup scaled-unit-ctors scaled-unit constructors **/
|
||||
///@{
|
||||
|
||||
/** create scaled unit representing a multiple
|
||||
* @p outer_scale_factor * @p sqrt(outer_scale_sq)
|
||||
* of natural unit @p nat_unit
|
||||
**/
|
||||
constexpr scaled_unit(const natural_unit<Int> & 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}
|
||||
{}
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup scaled-unit-access-methods scaled-unit access methods **/
|
||||
///@{
|
||||
|
||||
/** always true for scaled_unit **/
|
||||
static constexpr bool is_scaled_unit_type_v = true;
|
||||
|
||||
/** always true for scaled_unit **/
|
||||
constexpr bool is_scaled_unit_type() const { return true; }
|
||||
|
||||
/** true iff scaled unit can be faithfully represented by a @ref natural_unit **/
|
||||
constexpr bool is_natural() const {
|
||||
return (outer_scale_factor_ == OuterScale(1) && (outer_scale_sq_ == 1.0));
|
||||
}
|
||||
|
||||
/** true if this scaled unit has no dimension **/
|
||||
constexpr bool is_dimensionless() const { return natural_unit_.is_dimensionless(); }
|
||||
|
||||
/** get number of distinct native dimensions present.
|
||||
* e.g. for unit Newton = 1 kg.m.s^-2, n_bpu would be 3,
|
||||
* with {mass, distance, time} present.
|
||||
* Note that this value does not count exponents
|
||||
**/
|
||||
constexpr std::size_t n_bpu() const { return natural_unit_.n_bpu(); }
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup scaled-unit-general-methods scaled-unit access methods **/
|
||||
///@{
|
||||
|
||||
/** return reciprocal of this unit. **/
|
||||
constexpr scaled_unit reciprocal() const {
|
||||
return scaled_unit(natural_unit_.reciprocal(),
|
||||
1 / outer_scale_factor_,
|
||||
1.0 / outer_scale_sq_);
|
||||
}
|
||||
|
||||
/** get bpu for dimension @p d. if d isn't present, construct bpu with 0 power **/
|
||||
constexpr bpu<Int> lookup_dim(dimension d) const {
|
||||
return natural_unit_.lookup_dim(d);
|
||||
}
|
||||
|
||||
/** return @p i'th bpu associated with this unit **/
|
||||
constexpr bpu<Int> & operator[](std::size_t i) { return natural_unit_[i]; }
|
||||
/** return @p i'th bpu associated with this unit (const version) **/
|
||||
constexpr const bpu<Int> & operator[](std::size_t i) const { return natural_unit_[i]; }
|
||||
|
||||
///@}
|
||||
|
||||
public: /* public members so scaled_unit instance can be a non-type template parameter (a structural type) */
|
||||
|
||||
/** @defgroup scaled-unit-instance-vars **/
|
||||
///@{
|
||||
|
||||
/** scale factor multiplying @ref natural_unit_ **/
|
||||
OuterScale outer_scale_factor_;
|
||||
|
||||
/** squared scale factor multiplying @ref natural_unit_ **/
|
||||
double outer_scale_sq_;
|
||||
|
||||
/** natural unit term in this scaled unit **/
|
||||
natural_unit<Int> natural_unit_;
|
||||
|
||||
///@}
|
||||
};
|
||||
|
||||
// TODO: comparison operators
|
||||
|
||||
namespace detail {
|
||||
/** promote natural unit to scaled unit (with unit outer scalefactors) **/
|
||||
template <typename Int>
|
||||
constexpr auto su_promote(const natural_unit<Int> & bpuv) {
|
||||
return scaled_unit<Int>(bpuv,
|
||||
ratio::ratio<Int>(1, 1),
|
||||
1.0);
|
||||
}
|
||||
}
|
||||
|
||||
namespace u {
|
||||
/* values here can be used as template arguments to quantity:
|
||||
* e.g.
|
||||
* quantity<u:picogram> qty1;
|
||||
* quantity<u:meter/u:second> velocity;
|
||||
*/
|
||||
|
||||
constexpr auto
|
||||
su_from_bu(const basis_unit & bu,
|
||||
const power_ratio_type & power = power_ratio_type(1))
|
||||
{
|
||||
return detail::su_promote(natural_unit<std::int64_t>::from_bu(bu, power));
|
||||
}
|
||||
|
||||
/** @defgroup scaled-unit-dimensionless scaled-unit dimensionless constant **/
|
||||
///@{
|
||||
|
||||
/** dimensionless unit; equivalent to 1 **/
|
||||
constexpr auto dimensionless = detail::su_promote(natural_unit<std::int64_t>());
|
||||
|
||||
///@}
|
||||
|
||||
// ----- mass units -----
|
||||
|
||||
/** @defgroup scaled-unit-mass scaled-unit mass units **/
|
||||
///@{
|
||||
|
||||
/** unit of 10^-12 grams **/
|
||||
constexpr auto picogram = su_from_bu(detail::bu::picogram);
|
||||
/** unit of 10^-9 grams **/
|
||||
constexpr auto nanogram = su_from_bu(detail::bu::nanogram);
|
||||
/** unit of 10^-6 grams **/
|
||||
constexpr auto microgram = su_from_bu(detail::bu::microgram);
|
||||
/** unit of 10^-3 grams **/
|
||||
constexpr auto milligram = su_from_bu(detail::bu::milligram);
|
||||
/** unit of 1 gram **/
|
||||
constexpr auto gram = su_from_bu(detail::bu::gram);
|
||||
/** unit of 10^3 grams **/
|
||||
constexpr auto kilogram = su_from_bu(detail::bu::kilogram);
|
||||
/** unit of 1 metric tonne = 10^3 kg **/
|
||||
constexpr auto tonne = su_from_bu(detail::bu::tonne);
|
||||
/** unit of 10^3 tonnes = 10^6 kg **/
|
||||
constexpr auto kilotonne = su_from_bu(detail::bu::kilotonne);
|
||||
/** unit of 10^6 tonnes = 10^9 kg **/
|
||||
constexpr auto megatonne = su_from_bu(detail::bu::megatonne);
|
||||
/** unit of 10^9 tonnes = 10^12 kg **/
|
||||
constexpr auto gigatonne = su_from_bu(detail::bu::gigatonne);
|
||||
|
||||
///@}
|
||||
|
||||
// ----- distance units -----
|
||||
|
||||
/** @defgroup scaled-unit-distance scaled-unit distance units **/
|
||||
///@{
|
||||
|
||||
/** unit of 10^-12 meters **/
|
||||
constexpr auto picometer = su_from_bu(detail::bu::picometer);
|
||||
/** unit of 10^-9 meters **/
|
||||
constexpr auto nanometer = su_from_bu(detail::bu::nanometer);
|
||||
/** unit of 10^-6 meters **/
|
||||
constexpr auto micrometer = su_from_bu(detail::bu::micrometer);
|
||||
/** unit of 10^-3 meters **/
|
||||
constexpr auto millimeter = su_from_bu(detail::bu::millimeter);
|
||||
/** unit of 1 meter **/
|
||||
constexpr auto meter = su_from_bu(detail::bu::meter);
|
||||
/** unit of 10^3 meters **/
|
||||
constexpr auto kilometer = su_from_bu(detail::bu::kilometer);
|
||||
/** unit of 10^6 meters (not commonly used) **/
|
||||
constexpr auto megameter = su_from_bu(detail::bu::megameter);
|
||||
/** unit of 10^9 meters (not commonly used) **/
|
||||
constexpr auto gigameter = su_from_bu(detail::bu::gigameter);
|
||||
|
||||
/** unit of 1 light-second = distance light travels in a vacuum in 1 second **/
|
||||
constexpr auto lightsecond = su_from_bu(detail::bu::lightsecond);
|
||||
/** unit of 1 astronomical unit, for approximate radius of earth orbit **/
|
||||
constexpr auto astronomicalunit = su_from_bu(detail::bu::astronomicalunit);
|
||||
|
||||
/** unit of 1 inch = 1/12 feet **/
|
||||
constexpr auto inch = su_from_bu(detail::bu::inch);
|
||||
/** unit of 1 foot = 0.3048 meters **/
|
||||
constexpr auto foot = su_from_bu(detail::bu::foot);
|
||||
/** unit of 1 yard = 3 feet **/
|
||||
constexpr auto yard = su_from_bu(detail::bu::yard);
|
||||
/** unit of 1 mile = 1760 yards **/
|
||||
constexpr auto mile = su_from_bu(detail::bu::mile);
|
||||
|
||||
///@}
|
||||
|
||||
// ----- time units -----
|
||||
|
||||
/** @defgroup scaled-unit-time scaled-unit time units **/
|
||||
///@{
|
||||
|
||||
/** unit of 1 picosecond = 10^-12 seconds **/
|
||||
constexpr auto picosecond = su_from_bu(detail::bu::picosecond);
|
||||
/** unit of 1 nanosecond = 10^-9 seconds **/
|
||||
constexpr auto nanosecond = su_from_bu(detail::bu::nanosecond);
|
||||
/** unit of 1 microseccond = 10^-6 seconds **/
|
||||
constexpr auto microsecond = su_from_bu(detail::bu::microsecond);
|
||||
/** unit of 1 millisecond = 10^-3 seconds **/
|
||||
constexpr auto millisecond = su_from_bu(detail::bu::millisecond);
|
||||
/** unit of 1 second **/
|
||||
constexpr auto second = su_from_bu(detail::bu::second);
|
||||
/** unit of 1 minute **/
|
||||
constexpr auto minute = su_from_bu(detail::bu::minute);
|
||||
/** unit of 1 hour **/
|
||||
constexpr auto hour = su_from_bu(detail::bu::hour);
|
||||
/** unit for a 24-hour day **/
|
||||
constexpr auto day = su_from_bu(detail::bu::day);
|
||||
/** unit for a week comprising exactly 7 24-hour days **/
|
||||
constexpr auto week = su_from_bu(detail::bu::week);
|
||||
/** unit for a 30-day month **/
|
||||
constexpr auto month = su_from_bu(detail::bu::month);
|
||||
/** unit for a year containing exactly 365.25 24-hour days **/
|
||||
constexpr auto year = su_from_bu(detail::bu::year);
|
||||
/** unit for a 'year' containing exactly 250 24-hour days.
|
||||
* (approximates the number of business days in a year)
|
||||
**/
|
||||
constexpr auto year250 = su_from_bu(detail::bu::year250);
|
||||
/** unit for a 'year' containing exactly 360 24-hour days **/
|
||||
constexpr auto year360 = su_from_bu(detail::bu::year360);
|
||||
/** unit for a 'year' containing exactly 365 24-hour days **/
|
||||
constexpr auto year365 = su_from_bu(detail::bu::year365);
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup scaled-unit-misc scaled-unit miscellaneous units **/
|
||||
///@{
|
||||
|
||||
// ----- currency -----
|
||||
|
||||
/** generic currency unit **/
|
||||
constexpr auto currency = su_from_bu(detail::bu::currency);
|
||||
|
||||
// ----- price - ---
|
||||
|
||||
/** generic price unit **/
|
||||
constexpr auto price = su_from_bu(detail::bu::price);
|
||||
|
||||
///@}
|
||||
|
||||
// ----- volatility units -----
|
||||
|
||||
/** @defgroup scaled-unit-volatility scaled-unit volatility units **/
|
||||
///@{
|
||||
|
||||
/** volatility, in 30-day units **/
|
||||
constexpr auto volatility_30d = su_from_bu(detail::bu::month,
|
||||
power_ratio_type(-1,2));
|
||||
/** volatility, in 250-day 'annual' units **/
|
||||
constexpr auto volatility_250d = su_from_bu(detail::bu::year250,
|
||||
power_ratio_type(-1,2));
|
||||
/** volatility, in 360-day 'annual' units **/
|
||||
constexpr auto volatility_360d = su_from_bu(detail::bu::year360,
|
||||
power_ratio_type(-1,2));
|
||||
/** volatility, in 365-day 'annual' units **/
|
||||
constexpr auto volatility_365d = su_from_bu(detail::bu::year365,
|
||||
power_ratio_type(-1,2));
|
||||
///@}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename Int,
|
||||
typename Int2x = width2x<Int>,
|
||||
typename OuterScale = ratio::ratio<Int2x>>
|
||||
constexpr
|
||||
scaled_unit<Int, OuterScale>
|
||||
su_product(const natural_unit<Int> & lhs_bpu_array,
|
||||
const natural_unit<Int> & rhs_bpu_array)
|
||||
{
|
||||
natural_unit<Int2x> prod = lhs_bpu_array.template to_repr<Int2x>();
|
||||
|
||||
/* 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<Int2x>
|
||||
(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<Int2x, OuterScale>(&prod,
|
||||
rhs_bpu_array[i].template to_repr<Int2x>());
|
||||
|
||||
sfr.outer_scale_factor_ = sfr.outer_scale_factor_ * sfr2.outer_scale_factor_;
|
||||
sfr.outer_scale_sq_ *= sfr2.outer_scale_sq_;
|
||||
}
|
||||
|
||||
return scaled_unit<Int, OuterScale>(prod.template to_repr<Int>(),
|
||||
sfr.outer_scale_factor_,
|
||||
sfr.outer_scale_sq_);
|
||||
}
|
||||
|
||||
/* use Int2x to accumulate scalefactor
|
||||
*/
|
||||
template < typename Int,
|
||||
typename Int2x = width2x<Int>,
|
||||
typename OuterScale = ratio::ratio<Int2x> >
|
||||
constexpr
|
||||
scaled_unit<Int, OuterScale>
|
||||
su_ratio(const natural_unit<Int> & nu_lhs,
|
||||
const natural_unit<Int> & nu_rhs)
|
||||
{
|
||||
natural_unit<Int2x> ratio = nu_lhs.template to_repr<Int2x>();
|
||||
|
||||
/* 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<Int2x, OuterScale>
|
||||
(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<Int2x, OuterScale>(&ratio,
|
||||
nu_rhs[i].template to_repr<Int2x>());
|
||||
|
||||
/* 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<Int, OuterScale>(ratio.template to_repr<Int>(),
|
||||
sfr.outer_scale_factor_,
|
||||
sfr.outer_scale_sq_);
|
||||
}
|
||||
}
|
||||
|
||||
/** @defgroup scaled-unit-operators **/
|
||||
///@{
|
||||
|
||||
/** Multiply scaled_unit instances @p x_unit and @p y_unit.
|
||||
* Result is a scaled_unit for the product dimension.
|
||||
* For each basis dimension, result will prioritize scale from @p x_unit ahead of @p y_unit.
|
||||
**/
|
||||
template <typename Int,
|
||||
typename Int2x = detail::width2x_t<Int>>
|
||||
inline constexpr scaled_unit<Int>
|
||||
operator* (const scaled_unit<Int> & x_unit,
|
||||
const scaled_unit<Int> & y_unit)
|
||||
{
|
||||
auto rr = detail::su_product<Int, Int2x>(x_unit.natural_unit_,
|
||||
y_unit.natural_unit_);
|
||||
|
||||
return (scaled_unit<Int>
|
||||
(rr.natural_unit_,
|
||||
(ratio::ratio<Int2x>(rr.outer_scale_factor_)
|
||||
* ratio::ratio<Int2x>(x_unit.outer_scale_factor_)
|
||||
* ratio::ratio<Int2x>(y_unit.outer_scale_factor_)),
|
||||
rr.outer_scale_sq_ * x_unit.outer_scale_sq_ * y_unit.outer_scale_sq_));
|
||||
}
|
||||
|
||||
/** Divide scaled_unit instances @p x_unit by @p y_unit.
|
||||
* Result is a scaled_unit for the quotient dimension.
|
||||
* For each basis dimension, result will prioritize scale from @p x_unit ahead of @p y_unit.
|
||||
**/
|
||||
template <typename Int,
|
||||
typename Int2x = detail::width2x_t<Int>>
|
||||
inline constexpr scaled_unit<Int>
|
||||
operator/ (const scaled_unit<Int> & x_unit,
|
||||
const scaled_unit<Int> & y_unit)
|
||||
{
|
||||
auto rr = detail::su_ratio<Int, Int2x>(x_unit.natural_unit_,
|
||||
y_unit.natural_unit_);
|
||||
|
||||
return (scaled_unit<Int>
|
||||
(rr.natural_unit_,
|
||||
(ratio::ratio<Int2x>(rr.outer_scale_factor_)
|
||||
* ratio::ratio<Int2x>(x_unit.outer_scale_factor_)
|
||||
* ratio::ratio<Int2x>(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_unit.hpp **/
|
||||
23
xo-unit/include/xo/unit/scaled_unit_concept.hpp
Normal file
23
xo-unit/include/xo/unit/scaled_unit_concept.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/** @file scaled_unit_concept.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
template <typename ScaledUnit>
|
||||
concept scaled_unit_concept = requires(ScaledUnit su)
|
||||
{
|
||||
typename ScaledUnit::ratio_int_type;
|
||||
|
||||
{ su.is_scaled_unit_type() } -> std::same_as<bool>;
|
||||
{ su.is_natural() } -> std::same_as<bool>;
|
||||
{ su.is_dimensionless() } -> std::same_as<bool>;
|
||||
|
||||
} && ScaledUnit::is_scaled_unit_type_v;
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end scaled_unit_concept.hpp **/
|
||||
31
xo-unit/include/xo/unit/scaled_unit_iostream.hpp
Normal file
31
xo-unit/include/xo/unit/scaled_unit_iostream.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/** @file scaled_unit_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scaled_unit.hpp"
|
||||
#include "natural_unit_iostream.hpp"
|
||||
#include "xo/flatstring/int128_iostream.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
template <typename Int, typename OuterScale>
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os,
|
||||
const scaled_unit<Int, OuterScale> & x)
|
||||
{
|
||||
os << "<scaled-unit"
|
||||
<< xtag("outer_scale_factor", x.outer_scale_factor_)
|
||||
<< xtag("outer_scale_sq", x.outer_scale_sq_)
|
||||
<< xtag("bpuv", x.natural_unit_)
|
||||
<< ">";
|
||||
|
||||
return os;
|
||||
};
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end scaled_unit_iostream.hpp **/
|
||||
24
xo-unit/include/xo/unit/unit2.hpp
Normal file
24
xo-unit/include/xo/unit/unit2.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/** @file unit2.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "natural_unit.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
/** @class unit2
|
||||
* @brief represent an arbitrary unit along with dimension details
|
||||
*
|
||||
* For example,
|
||||
* kg.m.s^-2 or
|
||||
* (kilogram * meter) / (second * second)
|
||||
**/
|
||||
template <typename Int>
|
||||
using unit2 = natural_unit<Int>;
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end unit2.hpp **/
|
||||
39
xo-unit/include/xo/unit/width2x.hpp
Normal file
39
xo-unit/include/xo/unit/width2x.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/** @file width2x.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "natural_unit.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
namespace detail {
|
||||
template <typename Int>
|
||||
struct width2x;
|
||||
|
||||
template <>
|
||||
struct width2x<std::int16_t> {
|
||||
using type = std::int32_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct width2x<std::int32_t> {
|
||||
using type = std::int64_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct width2x<std::int64_t> {
|
||||
using type = __int128_t;
|
||||
};
|
||||
|
||||
template <typename Int>
|
||||
using width2x_t = width2x<Int>::type;
|
||||
}
|
||||
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end width2x.hpp **/
|
||||
562
xo-unit/include/xo/unit/xquantity.hpp
Normal file
562
xo-unit/include/xo/unit/xquantity.hpp
Normal file
|
|
@ -0,0 +1,562 @@
|
|||
/** @file xquantity.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "quantity_ops.hpp"
|
||||
#include "scaled_unit.hpp"
|
||||
#include "natural_unit.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
/** @class xquantity
|
||||
* @brief represent a scalar quantity with polymorphic units.
|
||||
*
|
||||
* - @p Repr type used represent a dimensionless multiple of a natural unit.
|
||||
*
|
||||
* Constexpr implementation, but units are explicitly represented:
|
||||
* @code
|
||||
* sizeof(xquantity) > sizeof(xquantity::repr_type)
|
||||
* @endcode
|
||||
*
|
||||
* Explicit unit representation allows introducing units at runtime,
|
||||
* for example in python bindings.
|
||||
* See for example <a href="https://github.com/rconybea/xo-pyutil">xo-pyutil</a>
|
||||
*
|
||||
* See @ref quantity for implementation with units established at compile time
|
||||
*
|
||||
* Require:
|
||||
* - Repr supports numeric operations (+, -, *, /)
|
||||
* - Repr supports conversion from double.
|
||||
**/
|
||||
template <typename Repr = double,
|
||||
typename Int = std::int64_t>
|
||||
class xquantity {
|
||||
public:
|
||||
/** @defgroup xquantity-type-traits xquantity type traits **/
|
||||
///@{
|
||||
/** @brief runtime representation for this value's scale **/
|
||||
using repr_type = Repr;
|
||||
/** @brief runtime representation for this value's unit **/
|
||||
using unit_type = natural_unit<Int>;
|
||||
/** @brief type used for numerator and denominator in basis-unit scalefactor ratios **/
|
||||
using ratio_int_type = Int;
|
||||
/** @brief double-width type for numerator and denominator of intermediate
|
||||
* scalefactor ratios. Use to mitigate loss of precision during computation
|
||||
* of conversion factors between units with widely-differing magnitude
|
||||
**/
|
||||
using ratio_int2x_type = detail::width2x_t<typename unit_type::ratio_int_type>;
|
||||
///@}
|
||||
|
||||
public:
|
||||
/** @defgroup xquantity-ctors xquantity constructors **/
|
||||
///@{
|
||||
|
||||
/** create dimensionless, zero quantity **/
|
||||
constexpr xquantity()
|
||||
: scale_{0}, unit_{natural_unit<Int>()} {}
|
||||
/** create quantity representing multiple of @p scale times @p unit **/
|
||||
constexpr xquantity(Repr scale,
|
||||
const natural_unit<Int> & unit)
|
||||
: scale_{scale}, unit_{unit} {}
|
||||
/** create quantity representing multiple of @p scale times @p unit.
|
||||
*
|
||||
* Collects outer scalefactors (if any) from @p unit,
|
||||
* so for example:
|
||||
*
|
||||
* @code
|
||||
* using namespace xo::qty;
|
||||
* xquantity q(123, u::meter * u::millimeter);
|
||||
*
|
||||
* q.scale() --> 0.123
|
||||
* @endcode
|
||||
**/
|
||||
constexpr xquantity(Repr scale,
|
||||
const scaled_unit<Int> & unit)
|
||||
:
|
||||
scale_(scale
|
||||
* unit.outer_scale_factor_.template convert_to<double>()
|
||||
* ((unit.outer_scale_sq_ == 1.0)
|
||||
? 1.0
|
||||
: ::sqrt(unit.outer_scale_sq_))
|
||||
),
|
||||
unit_{unit.natural_unit_} {}
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup xquantity-constants static xquantity constants **/
|
||||
///@{
|
||||
|
||||
/** false since unit information may be unknown at compile time.
|
||||
* Coordinates with @c quantity::always_constexpr_unit
|
||||
**/
|
||||
static constexpr bool always_constexpr_unit = false;
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup xquantity-access-methods xquantity access methods **/
|
||||
///@{
|
||||
|
||||
/** get member @ref scale_ **/
|
||||
constexpr const repr_type & scale() const { return scale_; }
|
||||
/** get member @ref unit_ **/
|
||||
constexpr const unit_type & unit() const { return unit_; }
|
||||
|
||||
/** true iff this quantity has no dimension **/
|
||||
constexpr bool is_dimensionless() const { return unit_.is_dimensionless(); }
|
||||
|
||||
/** return abbreviation for quantities with this unit **/
|
||||
constexpr nu_abbrev_type abbrev() const { return unit_.abbrev(); }
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup xquantity-arithmetic-support**/
|
||||
///@{
|
||||
|
||||
/** create unit quantity with same unit as @c this **/
|
||||
constexpr xquantity unit_qty() const { return xquantity(1, unit_); }
|
||||
|
||||
/** create zero quantity with same unit as @c this **/
|
||||
constexpr xquantity zero_qty() const { return xquantity(0, unit_); }
|
||||
|
||||
/** create quantity representing reciprocal of @c this **/
|
||||
constexpr xquantity reciprocal() const { return xquantity(1.0 / scale_, unit_.reciprocal()); }
|
||||
|
||||
/** create quantity representing this value scaled by dimensionless mutliplier @p x **/
|
||||
template <typename Dimensionless>
|
||||
requires std::is_arithmetic_v<Dimensionless>
|
||||
constexpr auto scale_by(Dimensionless x) const {
|
||||
return xquantity(x * this->scale_, this->unit_);
|
||||
}
|
||||
|
||||
/** create quantity representing this value scaled by dimensionless multiplier @p 1/x **/
|
||||
template <typename Dimensionless>
|
||||
requires std::is_arithmetic_v<Dimensionless>
|
||||
constexpr auto divide_by(Dimensionless x) const {
|
||||
return xquantity(this->scale_ / x, this->unit_);
|
||||
}
|
||||
|
||||
/** create quantity representing dimensionless numerator @p x divided by this value **/
|
||||
template <typename Dimensionless>
|
||||
requires std::is_arithmetic_v<Dimensionless>
|
||||
constexpr auto divide_into(Dimensionless x) const {
|
||||
return xquantity(x / this->scale_, this->unit_.reciprocal());
|
||||
}
|
||||
|
||||
/** multiply two @c xquantity values, or a mixed (@c xquantity, @c quantity) pair **/
|
||||
template <typename Quantity2>
|
||||
static constexpr
|
||||
auto multiply(const xquantity & x, const Quantity2 & y) {
|
||||
using r_repr_type = std::common_type_t<typename xquantity::repr_type,
|
||||
typename Quantity2::repr_type>;
|
||||
using r_int_type = std::common_type_t<typename xquantity::ratio_int_type,
|
||||
typename Quantity2::ratio_int_type>;
|
||||
using r_int2x_type = std::common_type_t<typename xquantity::ratio_int2x_type,
|
||||
typename Quantity2::ratio_int2x_type>;
|
||||
|
||||
auto rr = detail::su_product<r_int_type, r_int2x_type>(x.unit(), y.unit());
|
||||
|
||||
r_repr_type r_scale = (::sqrt(rr.outer_scale_sq_)
|
||||
* rr.outer_scale_factor_.template convert_to<r_repr_type>()
|
||||
* static_cast<r_repr_type>(x.scale())
|
||||
* static_cast<r_repr_type>(y.scale()));
|
||||
|
||||
return xquantity<r_repr_type, r_int_type>(r_scale,
|
||||
rr.natural_unit_);
|
||||
}
|
||||
|
||||
/** compute quotient @p x / @p y, where @p x and @p y are xquantities **/
|
||||
template <typename Quantity2>
|
||||
static constexpr
|
||||
auto divide(const xquantity & x, const Quantity2 & y) {
|
||||
using r_repr_type = std::common_type_t<typename xquantity::repr_type,
|
||||
typename Quantity2::repr_type>;
|
||||
using r_int_type = std::common_type_t<typename xquantity::ratio_int_type,
|
||||
typename Quantity2::ratio_int_type>;
|
||||
using r_int2x_type = std::common_type_t<typename xquantity::ratio_int2x_type,
|
||||
typename Quantity2::ratio_int2x_type>;
|
||||
|
||||
auto rr = detail::su_ratio<r_int_type, r_int2x_type>(x.unit(), y.unit());
|
||||
|
||||
/* note: su_ratio() reports multiplicative outer scaling factors,
|
||||
* so multiply is correct here
|
||||
*/
|
||||
r_repr_type r_scale = (::sqrt(rr.outer_scale_sq_)
|
||||
* rr.outer_scale_factor_.template convert_to<r_repr_type>()
|
||||
* static_cast<r_repr_type>(x.scale())
|
||||
/ static_cast<r_repr_type>(y.scale()));
|
||||
|
||||
return xquantity<r_repr_type, r_int_type>(r_scale,
|
||||
rr.natural_unit_);
|
||||
}
|
||||
|
||||
/** compute sum @p x + @p y, where @p x and @p y are xquantities **/
|
||||
template <typename Quantity2>
|
||||
static constexpr
|
||||
auto add(const xquantity & x, const Quantity2 & y) {
|
||||
using r_repr_type = std::common_type_t<typename xquantity::repr_type,
|
||||
typename Quantity2::repr_type>;
|
||||
using r_int_type = std::common_type_t<typename xquantity::ratio_int_type,
|
||||
typename Quantity2::ratio_int_type>;
|
||||
using r_int2x_type = std::common_type_t<typename xquantity::ratio_int2x_type,
|
||||
typename Quantity2::ratio_int2x_type>;
|
||||
|
||||
/* conversion to get y in same units as x: multiply by y/x */
|
||||
auto rr = detail::su_ratio<r_int_type, r_int2x_type>(y.unit(), x.unit());
|
||||
|
||||
if (rr.natural_unit_.is_dimensionless()) {
|
||||
r_repr_type r_scale = (static_cast<r_repr_type>(x.scale())
|
||||
+ (::sqrt(rr.outer_scale_sq_)
|
||||
* rr.outer_scale_factor_.template convert_to<r_repr_type>()
|
||||
* static_cast<r_repr_type>(y.scale())));
|
||||
|
||||
return xquantity<r_repr_type, r_int_type>(r_scale, x.unit_.template to_repr<r_int_type>());
|
||||
} else {
|
||||
/* units don't match! */
|
||||
return xquantity<r_repr_type, r_int_type>(std::numeric_limits<r_repr_type>::quiet_NaN(),
|
||||
x.unit_.template to_repr<r_int_type>());
|
||||
}
|
||||
}
|
||||
|
||||
/** compute difference @p x - @p y, where @p x and @p y are xquantities **/
|
||||
template <typename Quantity2>
|
||||
static constexpr
|
||||
auto subtract(const xquantity & x, const Quantity2 & y) {
|
||||
using r_repr_type = std::common_type_t<typename xquantity::repr_type,
|
||||
typename Quantity2::repr_type>;
|
||||
using r_int_type = std::common_type_t<typename xquantity::ratio_int_type,
|
||||
typename Quantity2::ratio_int_type>;
|
||||
using r_int2x_type = std::common_type_t<typename xquantity::ratio_int2x_type,
|
||||
typename Quantity2::ratio_int2x_type>;
|
||||
|
||||
/* conversion to get y in same units as x: multiply by y/x */
|
||||
auto rr = detail::su_ratio<r_int_type, r_int2x_type>(y.unit(), x.unit());
|
||||
|
||||
if (rr.natural_unit_.is_dimensionless()) {
|
||||
r_repr_type r_scale = (static_cast<r_repr_type>(x.scale())
|
||||
- (::sqrt(rr.outer_scale_sq_)
|
||||
* rr.outer_scale_factor_.template convert_to<r_repr_type>()
|
||||
* static_cast<r_repr_type>(y.scale())));
|
||||
|
||||
return xquantity<r_repr_type, r_int_type>(r_scale, x.unit_.template to_repr<r_int_type>());
|
||||
} else {
|
||||
/* units don't match! */
|
||||
return xquantity<r_repr_type, r_int_type>(std::numeric_limits<r_repr_type>::quiet_NaN(),
|
||||
x.unit_.template to_repr<r_int_type>());
|
||||
}
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup xquantity-unit-conversion **/
|
||||
///@{
|
||||
|
||||
/** create quantity representing the same value, but in units of @p unit2 **/
|
||||
constexpr
|
||||
auto rescale(const natural_unit<Int> & unit2) const {
|
||||
/* conversion factor from .unit -> unit2*/
|
||||
auto rr = detail::su_ratio<ratio_int_type,
|
||||
ratio_int2x_type>(this->unit_, unit2);
|
||||
|
||||
if (rr.natural_unit_.is_dimensionless()) {
|
||||
repr_type r_scale = (::sqrt(rr.outer_scale_sq_)
|
||||
* rr.outer_scale_factor_.template convert_to<repr_type>()
|
||||
* this->scale_);
|
||||
return xquantity(r_scale, unit2);
|
||||
} else {
|
||||
return xquantity(std::numeric_limits<repr_type>::quiet_NaN(), unit2);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr
|
||||
auto rescale_ext(const scaled_unit<Int> & unit2) const {
|
||||
/* conversion factor from .unit -> unit2*/
|
||||
auto rr = detail::su_ratio<ratio_int_type,
|
||||
ratio_int2x_type>(unit_,
|
||||
unit2.natural_unit_);
|
||||
|
||||
if (rr.natural_unit_.is_dimensionless()) {
|
||||
/* NOTE: test for unit .outer_scale_sq values to get constexpr result with c++23
|
||||
* and integer dimension powers.
|
||||
*
|
||||
* NOTE: we don't intend to support mixed-unit quantities.
|
||||
* If we change intention, will need to take into account
|
||||
* (s_scaled_unit.outer_scale_factor_, s_scaled_unit.outer_scale_sq_)
|
||||
*/
|
||||
repr_type r_scale
|
||||
= ((((rr.outer_scale_sq_ == 1.0)
|
||||
&& (unit2.outer_scale_sq_ == 1.0))
|
||||
? 1.0
|
||||
: ::sqrt(rr.outer_scale_sq_ / unit2.outer_scale_sq_))
|
||||
* rr.outer_scale_factor_.template convert_to<repr_type>()
|
||||
* this->scale_
|
||||
/ unit2.outer_scale_factor_.template convert_to<repr_type>());
|
||||
return xquantity(r_scale, unit2);
|
||||
} else {
|
||||
return xquantity(std::numeric_limits<repr_type>::quiet_NaN(), unit2);
|
||||
}
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup xquantity-comparison-support xquantity comparison support methods **/
|
||||
///@{
|
||||
|
||||
/** perform 3-way comparison between @c xquantity values @p x and @p y **/
|
||||
template <typename Quantity2>
|
||||
static constexpr
|
||||
auto compare(const xquantity & x, const Quantity2 & y) {
|
||||
xquantity y2 = y.rescale(x.unit_);
|
||||
|
||||
return x.scale() <=> y2.scale();
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** @defgroup xquantity-operators xquantity operators **/
|
||||
///@{
|
||||
|
||||
/** add @p x in-place, converting units if necessary **/
|
||||
template <typename Quantity2>
|
||||
xquantity & operator+= (const Quantity2 & x) {
|
||||
*this = *this + x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** unary negation; preserves unit information **/
|
||||
xquantity operator-() const {
|
||||
return xquantity(-scale_, unit_);
|
||||
}
|
||||
|
||||
/** subtract @p x in-place, converting units if necessary **/
|
||||
template <typename Quantity2>
|
||||
xquantity & operator-= (const Quantity2 & x) {
|
||||
*this = *this - x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** multiply @p x in-place, converting units if necessary
|
||||
*
|
||||
* @note: unlike @c quantity::operator*=, may change dimension of lhs
|
||||
**/
|
||||
template <typename Quantity2>
|
||||
xquantity & operator*= (const Quantity2 & x) {
|
||||
*this = *this * x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** divide @p x in-place, converting units if necessary
|
||||
*
|
||||
* @note: unlike @c quantity::operator/=, may change dimension of lhs
|
||||
**/
|
||||
template <typename Quantity2>
|
||||
xquantity & operator/= (const Quantity2 & x) {
|
||||
*this = *this / x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
/** @defgroup xquantity-instance-vars **/
|
||||
///@{
|
||||
|
||||
/** quantity represents this multiple of a unit amount **/
|
||||
Repr scale_ = Repr{};
|
||||
/** unit for this quantity **/
|
||||
natural_unit<Int> unit_;
|
||||
|
||||
///@}
|
||||
}; /*xquantity*/
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when @c sqrt(), @c pow() are constexpr)
|
||||
**/
|
||||
template <typename Repr = double,
|
||||
typename Int = std::int64_t>
|
||||
inline constexpr xquantity<Repr, Int>
|
||||
unit_qty(const scaled_unit<Int> & u)
|
||||
{
|
||||
return xquantity<Repr, Int>
|
||||
(u.outer_scale_factor_.template convert_to<double>() * ::sqrt(u.outer_scale_sq_),
|
||||
u.natural_unit_);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when @c sqrt(), @c pow() are constexpr)
|
||||
**/
|
||||
template <typename Repr = double,
|
||||
typename Int = std::int64_t>
|
||||
inline constexpr xquantity<Repr, Int>
|
||||
natural_unit_qty(const natural_unit<Int> & nu) {
|
||||
return xquantity<Repr, Int>(1.0, nu);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when @c sqrt(), @c pow() are constexpr)
|
||||
**/
|
||||
template <typename Q1, typename Q2>
|
||||
requires (quantity_concept<Q1>
|
||||
&& quantity_concept<Q2>
|
||||
&& (!Q1::always_constexpr_unit || !Q2::always_constexpr_unit))
|
||||
constexpr auto
|
||||
operator* (const Q1 & x, const Q2 & y)
|
||||
{
|
||||
return Q1::multiply(x, y);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator* (double x, const Quantity & y)
|
||||
{
|
||||
return y.scale_by(x);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator* (const Quantity & x, double y)
|
||||
{
|
||||
return x.scale_by(y);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity, typename Quantity2>
|
||||
requires quantity_concept<Quantity> && quantity_concept<Quantity2>
|
||||
constexpr auto
|
||||
operator/ (const Quantity & x, const Quantity2 & y)
|
||||
{
|
||||
return Quantity::divide(x, y);
|
||||
}
|
||||
|
||||
/** note: doesn not require unit scaling, so constexpr with c++23 **/
|
||||
template <typename Quantity, typename Dimensionless>
|
||||
requires quantity_concept<Quantity> && std::is_arithmetic_v<Dimensionless>
|
||||
constexpr auto
|
||||
operator/ (const Quantity & x, Dimensionless y)
|
||||
{
|
||||
return x.divide_by(y);
|
||||
}
|
||||
|
||||
/** note: doesn not require unit scaling, so constexpr with c++23 **/
|
||||
template <typename Dimensionless, typename Quantity>
|
||||
requires std::is_arithmetic_v<Dimensionless> && quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator/ (Dimensionless x, const Quantity & y)
|
||||
{
|
||||
return y.divide_into(x);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity, typename Quantity2>
|
||||
requires quantity_concept<Quantity> && quantity_concept<Quantity2>
|
||||
constexpr auto
|
||||
operator+ (const Quantity & x, const Quantity2 & y)
|
||||
{
|
||||
return Quantity::add(x, y);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator+ (const Quantity & x, double y)
|
||||
{
|
||||
return x + Quantity(y, u::dimensionless);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator+ (double x, const Quantity & y)
|
||||
{
|
||||
return Quantity(x, u::dimensionless) + y;
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity, typename Quantity2>
|
||||
requires (quantity_concept<Quantity>
|
||||
&& quantity_concept<Quantity2>)
|
||||
constexpr auto
|
||||
operator- (const Quantity & x, const Quantity2 & y)
|
||||
{
|
||||
return Quantity::subtract(x, y);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator- (const Quantity & x, double y)
|
||||
{
|
||||
return x - Quantity(y, u::dimensionless);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator- (double x, const Quantity & y)
|
||||
{
|
||||
return Quantity(x, u::dimensionless) - y;
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator== (const Quantity & x, double y)
|
||||
{
|
||||
return (x == Quantity(y, u::dimensionless));
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator== (double x, const Quantity & y)
|
||||
{
|
||||
return (Quantity(x, u::dimensionless) == y);
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity, double>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator<=> (const Quantity & x, double y)
|
||||
{
|
||||
return Quantity::compare(x, Quantity(y, u::dimensionless));
|
||||
}
|
||||
|
||||
/** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr)
|
||||
**/
|
||||
template <typename Quantity, double>
|
||||
requires quantity_concept<Quantity>
|
||||
constexpr auto
|
||||
operator<=> (double x, const Quantity & y)
|
||||
{
|
||||
return Quantity::compare(Quantity(x, u::dimensionless), y);
|
||||
}
|
||||
|
||||
namespace xu {
|
||||
constexpr auto nanogram = xquantity(1.0, u::nanogram);
|
||||
}
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end xquantity.hpp **/
|
||||
27
xo-unit/include/xo/unit/xquantity_iostream.hpp
Normal file
27
xo-unit/include/xo/unit/xquantity_iostream.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/** @file xquantity_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xquantity.hpp"
|
||||
#include "natural_unit_iostream.hpp"
|
||||
//#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
namespace qty {
|
||||
template <typename Repr = double,
|
||||
typename Int = std::int64_t>
|
||||
inline std::ostream &
|
||||
operator<< (std::ostream & os,
|
||||
const xquantity<Repr, Int> & x)
|
||||
{
|
||||
os << x.scale() << x.abbrev();
|
||||
|
||||
return os;
|
||||
}
|
||||
} /*namespace qty*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end xquantity_iostream.hpp **/
|
||||
Loading…
Add table
Add a link
Reference in a new issue