build: core library and each system separated to dedicated CMake targets

Refers to #249
This commit is contained in:
Mateusz Pusz committed 2021-03-16 23:05:45 +01:00
1 parent 7ee0e3ff90
commit 221883bb25
206 files changed
+547 -121

No files matched your search

+64
View File
@@ -0,0 +1,64 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/fixed_string.h>
#include <units/bits/basic_concepts.h>
#include <type_traits>
namespace units {
/**
* @brief A dimension of a base quantity
*
* Base quantity is a quantity in a conventionally chosen subset of a given system of quantities, where no quantity
* in the subset can be expressed in terms of the other quantities within that subset. They are referred to as
* being mutually independent since a base quantity cannot be expressed as a product of powers of the other base
* quantities.
*
* Base unit is a measurement unit that is adopted by convention for a base quantity in a specific system of units.
*
* Pair of Symbol and Unit template parameters form an unique identifier of the base dimension. The same identifiers can
* be multiplied and divided which will result with an adjustment of its factor in an Exponent of a DerivedDimension
* (in case of zero the dimension will be simplified and removed from further analysis of current expresion). In case
* the Symbol is the same but the Unit differs (i.e. mixing SI and CGS length), there is no automatic simplification but
* is possible to force it with a quantity_cast.
*
* @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support
* @tparam U a base unit to be used for this base dimension
*/
template<basic_fixed_string Symbol, Unit U>
requires U::is_named
struct base_dimension {
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier
using base_unit = U; ///< Base unit adopted for this dimension
};
// base_dimension_less
template<BaseDimension D1, BaseDimension D2>
struct base_dimension_less :
std::bool_constant<(D1::symbol < D2::symbol) ||
(D1::symbol == D2::symbol && D1::base_unit::symbol < D1::base_unit::symbol)> {
};
} // namespace units
@@ -0,0 +1,51 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/base_dimension.h>
#include <units/exponent.h>
#include <units/ratio.h>
namespace units::detail {
template<Exponent E>
requires (E::den == 1 || E::den == 2) // TODO provide support for any den
constexpr ratio exp_ratio()
{
const ratio base_ratio = E::dimension::base_unit::ratio;
const ratio positive_ratio = E::num * E::den < 0 ? ratio(base_ratio.den, base_ratio.num, -base_ratio.exp) : base_ratio;
const std::intmax_t N = E::num * E::den < 0 ? -E::num : E::num;
const ratio ratio_pow = pow<N>(positive_ratio);
return E::den == 2 ? sqrt(ratio_pow) : ratio_pow;
}
/**
* @brief Calculates the common ratio of all the references of base units in the derived dimension
*/
template<typename... Es>
constexpr ratio base_units_ratio(exponent_list<Es...>)
{
return (exp_ratio<Es>() * ... * ratio(1));
}
} // namespace units::detail
@@ -0,0 +1,419 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/downcasting.h>
#include <units/bits/external/fixed_string.h>
#include <units/bits/external/hacks.h>
#include <units/customization_points.h>
#include <units/ratio.h>
#include <units/bits/external/type_traits.h>
#include <functional>
#include <cstdint>
#include <utility>
namespace units {
// PrefixFamily
struct prefix_family;
/**
* @brief A concept matching a prefix family
*
* Satisfied by all types derived from `prefix_family`
*/
template<typename T>
concept PrefixFamily = std::derived_from<T, prefix_family>;
// Prefix
namespace detail {
template<PrefixFamily PF, ratio R>
struct prefix_base;
template<PrefixFamily PF, ratio R>
void to_prefix_base(const volatile prefix_base<PF, R>*);
} // namespace detail
/**
* @brief A concept matching a symbol prefix
*
* Satisfied by all specializations of `prefix`.
*/
template<typename T>
concept Prefix = requires(T* t) { detail::to_prefix_base(t); };
/**
* @brief A concept matching unit's ratio
*
* Satisfied by all ratio values for which `R.num > 0` and `R.den > 0`.
*/
template<ratio R>
concept UnitRatio = R.num > 0 && R.den > 0;
// Unit
template<ratio R, typename U>
requires UnitRatio<R>
struct scaled_unit;
// TODO: Remove when P1985 accepted
namespace detail {
template<ratio R, typename U>
void to_base_scaled_unit(const volatile scaled_unit<R, U>*);
} // namespace detail
/**
* @brief A concept matching all unit types in the library
*
* Satisfied by all unit types derived from an specialization of :class:`scaled_unit`.
*/
template<typename T>
concept Unit = requires(T* t) { detail::to_base_scaled_unit(t); };
// BaseDimension
template<basic_fixed_string Symbol, Unit U>
requires U::is_named
struct base_dimension;
namespace detail {
template<basic_fixed_string Symbol, typename U>
void to_base_base_dimension(const volatile base_dimension<Symbol, U>*);
} // namespace detail
/**
* @brief A concept matching all base dimensions in the library.
*
* Satisfied by all dimension types derived from an specialization of `base_dimension`.
*/
template<typename T>
concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); };
// Exponent
namespace detail {
template<typename T>
inline constexpr bool is_exponent = false;
} // namespace detail
/**
* @brief A concept matching dimension's exponents.
*
* Satisfied by all specializations of :class:`exponent`.
*/
template<typename T>
concept Exponent = detail::is_exponent<T>;
// DerivedDimension
namespace detail {
template<Exponent... Es>
requires (BaseDimension<typename Es::dimension> && ...)
struct derived_dimension_base;
} // namespace detail
/**
* @brief A concept matching all derived dimensions in the library.
*
* Satisfied by all dimension types derived from an specialization of `detail::derived_dimension_base`.
*/
template<typename T>
concept DerivedDimension = is_derived_from_specialization_of<T, detail::derived_dimension_base>;
// Dimension
/**
* @brief A concept matching all dimensions in the library.
*
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
*/
template<typename T>
concept Dimension = BaseDimension<T> || DerivedDimension<T>;
// UnitOf
namespace detail {
template<Dimension D>
auto default_unit()
{
if constexpr (BaseDimension<D>)
return typename D::base_unit{};
else
return typename D::coherent_unit{};
}
} // namespace detail
/**
* @brief Returns a 'default' unit of the dimension
*
* Depending on the dimension type it returns a base unit (for base dimensions)
* or a coherent unit (in case of derived dimensions).
*
* @tparam D Dimension type to get the unit from.
*/
template<Dimension D>
using dimension_unit = decltype(detail::default_unit<D>());
// TODO: replace with the below code when VS catches up
// decltype([]{
// if constexpr (BaseDimension<D>)
// return TYPENAME D::base_unit{};
// else
// return TYPENAME D::coherent_unit{};
// }());
/**
* @brief A concept matching only units of a specified dimension.
*
* Satisfied by all unit types that satisfy `Unit<U>`, `Dimension<D>`, and for which
* `U::reference` and @c dimension_unit<D>::reference denote the same unit type.
*
* @tparam U Type to verify.
* @tparam D Dimension type to use for verification.
*/
template<typename U, typename D>
concept UnitOf =
Unit<U> &&
Dimension<D> &&
std::same_as<typename U::reference, typename dimension_unit<D>::reference>;
// Kind
namespace detail {
template<typename, Dimension>
struct _kind_base;
} // namespace detail
template<typename T, template<typename...> typename Base>
concept kind_impl_ =
is_derived_from_specialization_of<T, Base> &&
requires(T* t) {
typename T::base_kind;
typename T::dimension;
requires Dimension<typename T::dimension>;
};
/**
* @brief A concept matching all kind types
*
* Satisfied by all kind types derived from an specialization of @c kind.
*/
template<typename T>
concept Kind =
kind_impl_<T, detail::_kind_base> &&
kind_impl_<typename T::base_kind, detail::_kind_base> &&
std::same_as<typename T::base_kind, typename T::base_kind::base_kind>;
// PointKind
namespace detail {
template<Kind>
struct _point_kind_base;
} // namespace detail
/**
* @brief A concept matching all point kind types
*
* Satisfied by all point kind types derived from an specialization of @c point_kind.
*/
template<typename T>
concept PointKind = kind_impl_<T, detail::_point_kind_base>;
// Quantity, QuantityPoint, QuantityKind, QuantityPointKind
namespace detail {
template<typename T>
inline constexpr bool is_quantity = false;
template<typename T>
inline constexpr bool is_quantity_point = false;
template<typename T>
inline constexpr bool is_quantity_kind = false;
template<typename T>
inline constexpr bool is_quantity_point_kind = false;
template<typename T>
inline constexpr bool is_quantity_like = false;
template<typename T>
inline constexpr bool is_quantity_point_like = false;
} // namespace detail
/**
* @brief A concept matching all quantities in the library.
*
* Satisfied by all specializations of @c quantity.
*/
template<typename T>
concept Quantity = detail::is_quantity<T>;
/**
* @brief A concept matching all quantity points in the library.
*
* Satisfied by all specializations of @c quantity_point.
*/
template<typename T>
concept QuantityPoint = detail::is_quantity_point<T>;
/**
* @brief A concept matching all quantity kinds in the library.
*
* Satisfied by all specializations of @c quantity_kind.
*/
template<typename T>
concept QuantityKind = detail::is_quantity_kind<T>;
/**
* @brief A concept matching all quantity point kinds in the library.
*
* Satisfied by all specializations of @c quantity_point_kind.
*/
template<typename T>
concept QuantityPointKind = detail::is_quantity_point_kind<T>;
// QuantityLike, QuantityPointLike
/**
* @brief A concept matching all quantity-like types (other than specialization of @c quantity)
*
* Satisfied by all types for which a correct specialization of `quantity_like_traits`
* type trait is provided.
*/
template<typename T>
concept QuantityLike = detail::is_quantity_like<T>;
/**
* @brief A concept matching all quantity point-like types (other than specialization of @c quantity_point)
*
* Satisfied by all types for which a correct specialization of `quantity_point_like_traits`
* type trait is provided.
*/
template<typename T>
concept QuantityPointLike = detail::is_quantity_point_like<T>;
// QuantityValue
template<typename T, typename U>
concept common_type_with_ = // exposition only
std::same_as<std::common_type_t<T, U>, std::common_type_t<U, T>> &&
std::constructible_from<std::common_type_t<T, U>, T> &&
std::constructible_from<std::common_type_t<T, U>, U>;
template<typename T, typename U = T>
concept scalable_number_ = // exposition only
std::regular_invocable<std::multiplies<>, T, U> &&
std::regular_invocable<std::divides<>, T, U>;
template<typename T>
concept castable_number_ = // exposition only
common_type_with_<T, std::intmax_t> &&
scalable_number_<std::common_type_t<T, std::intmax_t>>;
template<typename T>
concept scalable_ = // exposition only
castable_number_<T> ||
(requires { typename T::value_type; } && castable_number_<typename T::value_type> && scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
template<typename T, typename U>
concept scalable_with_ = // exposition only
common_type_with_<T, U> &&
scalable_<std::common_type_t<T, U>>;
// WrappedQuantity
namespace detail {
template<typename T>
inline constexpr bool is_wrapped_quantity = false;
template<typename T>
requires requires { typename T::value_type; }
inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::value_type> || QuantityLike<typename T::value_type> || is_wrapped_quantity<typename T::value_type>;
template<typename T>
requires requires { typename T::quantity_type; }
inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::quantity_type>;
} // namespace detail
/**
* @brief A concept matching types that wrap quantity objects.
*
* Satisfied by all wrapper types that satisfy `Quantity<typename T::value_type>`
* recursively (i.e. `std::optional<si::length<si::metre>>`).
*/
template<typename T>
concept wrapped_quantity_ = // exposition only
detail::is_wrapped_quantity<T>;
/**
* @brief A concept matching non-Quantity types.
*
* Satisfied by types that satisfy `(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>`.
*/
template<typename T>
concept QuantityValue =
(!Quantity<T>) &&
(!QuantityLike<T>) &&
(!wrapped_quantity_<T>) &&
std::regular<T> &&
scalable_<T>;
namespace detail {
template<typename T>
requires requires(T q) {
typename quantity_like_traits<T>::dimension;
typename quantity_like_traits<T>::unit;
typename quantity_like_traits<T>::rep;
requires Dimension<typename quantity_like_traits<T>::dimension>;
requires Unit<typename quantity_like_traits<T>::unit>;
requires QuantityValue<typename quantity_like_traits<T>::rep>;
{ quantity_like_traits<T>::count(q) } -> std::convertible_to<typename quantity_like_traits<T>::rep>;
}
inline constexpr bool is_quantity_like<T> = true;
template<typename T>
requires requires(T q) {
typename quantity_point_like_traits<T>::dimension;
typename quantity_point_like_traits<T>::unit;
typename quantity_point_like_traits<T>::rep;
requires Dimension<typename quantity_point_like_traits<T>::dimension>;
requires Unit<typename quantity_point_like_traits<T>::unit>;
requires QuantityValue<typename quantity_point_like_traits<T>::rep>;
{ quantity_point_like_traits<T>::relative(q) } -> QuantityLike;
}
inline constexpr bool is_quantity_point_like<T> = true;
} // namespace detail
} // namespace units
@@ -0,0 +1,123 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/dimension_op.h>
#include <units/bits/equivalent.h>
#include <units/quantity_cast.h>
namespace units {
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity;
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity_point;
template<Kind K, UnitOf<typename K::dimension> U, QuantityValue Rep>
class quantity_kind;
template<PointKind PK, UnitOf<typename PK::dimension> U, QuantityValue Rep>
class quantity_point_kind;
namespace detail {
template<typename Q1, typename Q2, typename Rep>
struct common_quantity_impl;
template<typename D, typename U, typename Rep1, typename Rep2, typename Rep>
struct common_quantity_impl<quantity<D, U, Rep1>, quantity<D, U, Rep2>, Rep> {
using type = quantity<D, U, Rep>;
};
template<typename D, typename U1, typename Rep1, typename U2, typename Rep2, typename Rep>
struct common_quantity_impl<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, Rep> {
using type = quantity<D, downcast_unit<D, common_ratio(U1::ratio, U2::ratio)>, Rep>;
};
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep>
requires same_unit_reference<dimension_unit<D1>, dimension_unit<D2>>::value
struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> {
using type = quantity<D1, downcast_unit<D1, common_ratio(U1::ratio, U2::ratio)>, Rep>;
};
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep>
struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> {
using dimension = conditional<is_specialization_of<D1, unknown_dimension>, D2, D1>;
static constexpr ratio r1 = D1::base_units_ratio * U1::ratio;
static constexpr ratio r2 = D2::base_units_ratio * U2::ratio;
static constexpr ratio cr = common_ratio(r1, r2);
using unit = downcast_unit<dimension, cr / dimension::base_units_ratio>;
using type = quantity<dimension, unit, Rep>;
};
} // namespace detail
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2, QuantityValue Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>>
using common_quantity = TYPENAME detail::common_quantity_impl<Q1, Q2, Rep>::type;
template<QuantityPoint QP1, QuantityPointEquivalentTo<QP1> QP2>
using common_quantity_point = std::common_type_t<QP1, QP2>;
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
using common_quantity_kind = std::common_type_t<QK1, QK2>;
template<QuantityPointKind QPK1, QuantityPointKindEquivalentTo<QPK1> QPK2>
using common_quantity_point_kind = std::common_type_t<QPK1, QPK2>;
} // namespace units
namespace std {
template<units::Quantity Q1, units::QuantityEquivalentTo<Q1> Q2>
requires requires { typename common_type_t<typename Q1::rep, typename Q2::rep>; }
struct common_type<Q1, Q2> {
using type = units::common_quantity<Q1, Q2>;
};
template<units::QuantityPoint QP1, units::QuantityPointEquivalentTo<QP1> QP2>
requires requires { typename common_type_t<typename QP1::rep, typename QP2::rep>; }
struct common_type<QP1, QP2> {
using type = units::quantity_point<
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::dimension,
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::unit,
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::rep>;
};
template<units::QuantityKind QK1, units::QuantityKindEquivalentTo<QK1> QK2>
requires requires { typename common_type_t<typename QK1::rep, typename QK2::rep>; }
struct common_type<QK1, QK2> {
using type = units::quantity_kind<typename QK1::kind_type,
typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::unit,
typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::rep>;
};
template<units::QuantityPointKind QPK1, units::QuantityPointKindEquivalentTo<QPK1> QPK2>
requires requires { typename common_type_t<typename QPK1::rep, typename QPK2::rep>; }
struct common_type<QPK1, QPK2> {
using type = units::quantity_point_kind<typename QPK1::point_kind_type,
typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::unit,
typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::rep>;
};
}
@@ -0,0 +1,241 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <gsl/gsl-lite.hpp>
#include <units/bits/math_concepts.h>
#include <units/bits/pow.h>
#include <units/bits/ratio_maths.h>
namespace units::detail {
struct decimal_fp {
double significant;
std::intmax_t exponent;
};
[[nodiscard]] constexpr decimal_fp to_decimal(double v) noexcept
{
if (v == 0) {
return {.significant = 0.0, .exponent = 0};
}
double significant = abs(v);
std::intmax_t exponent = 0;
while (significant < 1) {
significant *= 10.0;
--exponent;
}
while (significant >= 10) {
significant /= 10.0;
++exponent;
}
if (v < 0) {
significant = -significant;
}
return {.significant = significant, .exponent = exponent};
}
/* approximate natural log as https://math.stackexchange.com/a/977836
far slower than std::log but works at compile time with similar accuracy
*/
[[nodiscard]] constexpr double constexpr_log(double v) noexcept
{
gsl_Expects(v > 0);
// lookup table to speed up convergence for all significant values
// significant values of 7 and greater benefit mostly as they now converge in 5 terms compared to O(10)-O(100)
// required without the table
//
// using python:
// >>> import math
// >>> for i in range(1, 100):
// ... print(f"/* log({i:>2d}) = */ {math.log(i):.16f},")
constexpr std::array<double, 99> log_table{
/* log( 1) = */ 0.0000000000000000,
/* log( 2) = */ 0.6931471805599453,
/* log( 3) = */ 1.0986122886681098,
/* log( 4) = */ 1.3862943611198906,
/* log( 5) = */ 1.6094379124341003,
/* log( 6) = */ 1.7917594692280550,
/* log( 7) = */ 1.9459101490553132,
/* log( 8) = */ 2.0794415416798357,
/* log( 9) = */ 2.1972245773362196,
/* log(10) = */ 2.3025850929940459,
/* log(11) = */ 2.3978952727983707,
/* log(12) = */ 2.4849066497880004,
/* log(13) = */ 2.5649493574615367,
/* log(14) = */ 2.6390573296152584,
/* log(15) = */ 2.7080502011022101,
/* log(16) = */ 2.7725887222397811,
/* log(17) = */ 2.8332133440562162,
/* log(18) = */ 2.8903717578961645,
/* log(19) = */ 2.9444389791664403,
/* log(20) = */ 2.9957322735539909,
/* log(21) = */ 3.0445224377234230,
/* log(22) = */ 3.0910424533583161,
/* log(23) = */ 3.1354942159291497,
/* log(24) = */ 3.1780538303479458,
/* log(25) = */ 3.2188758248682006,
/* log(26) = */ 3.2580965380214821,
/* log(27) = */ 3.2958368660043291,
/* log(28) = */ 3.3322045101752038,
/* log(29) = */ 3.3672958299864741,
/* log(30) = */ 3.4011973816621555,
/* log(31) = */ 3.4339872044851463,
/* log(32) = */ 3.4657359027997265,
/* log(33) = */ 3.4965075614664802,
/* log(34) = */ 3.5263605246161616,
/* log(35) = */ 3.5553480614894135,
/* log(36) = */ 3.5835189384561099,
/* log(37) = */ 3.6109179126442243,
/* log(38) = */ 3.6375861597263857,
/* log(39) = */ 3.6635616461296463,
/* log(40) = */ 3.6888794541139363,
/* log(41) = */ 3.7135720667043080,
/* log(42) = */ 3.7376696182833684,
/* log(43) = */ 3.7612001156935624,
/* log(44) = */ 3.7841896339182610,
/* log(45) = */ 3.8066624897703196,
/* log(46) = */ 3.8286413964890951,
/* log(47) = */ 3.8501476017100584,
/* log(48) = */ 3.8712010109078911,
/* log(49) = */ 3.8918202981106265,
/* log(50) = */ 3.9120230054281460,
/* log(51) = */ 3.9318256327243257,
/* log(52) = */ 3.9512437185814275,
/* log(53) = */ 3.9702919135521220,
/* log(54) = */ 3.9889840465642745,
/* log(55) = */ 4.0073331852324712,
/* log(56) = */ 4.0253516907351496,
/* log(57) = */ 4.0430512678345503,
/* log(58) = */ 4.0604430105464191,
/* log(59) = */ 4.0775374439057197,
/* log(60) = */ 4.0943445622221004,
/* log(61) = */ 4.1108738641733114,
/* log(62) = */ 4.1271343850450917,
/* log(63) = */ 4.1431347263915326,
/* log(64) = */ 4.1588830833596715,
/* log(65) = */ 4.1743872698956368,
/* log(66) = */ 4.1896547420264252,
/* log(67) = */ 4.2046926193909657,
/* log(68) = */ 4.2195077051761070,
/* log(69) = */ 4.2341065045972597,
/* log(70) = */ 4.2484952420493594,
/* log(71) = */ 4.2626798770413155,
/* log(72) = */ 4.2766661190160553,
/* log(73) = */ 4.2904594411483910,
/* log(74) = */ 4.3040650932041702,
/* log(75) = */ 4.3174881135363101,
/* log(76) = */ 4.3307333402863311,
/* log(77) = */ 4.3438054218536841,
/* log(78) = */ 4.3567088266895917,
/* log(79) = */ 4.3694478524670215,
/* log(80) = */ 4.3820266346738812,
/* log(81) = */ 4.3944491546724391,
/* log(82) = */ 4.4067192472642533,
/* log(83) = */ 4.4188406077965983,
/* log(84) = */ 4.4308167988433134,
/* log(85) = */ 4.4426512564903167,
/* log(86) = */ 4.4543472962535073,
/* log(87) = */ 4.4659081186545837,
/* log(88) = */ 4.4773368144782069,
/* log(89) = */ 4.4886363697321396,
/* log(90) = */ 4.4998096703302650,
/* log(91) = */ 4.5108595065168497,
/* log(92) = */ 4.5217885770490405,
/* log(93) = */ 4.5325994931532563,
/* log(94) = */ 4.5432947822700038,
/* log(95) = */ 4.5538768916005408,
/* log(96) = */ 4.5643481914678361,
/* log(97) = */ 4.5747109785033828,
/* log(98) = */ 4.5849674786705723,
/* log(99) = */ 4.5951198501345898,
};
decimal_fp x = to_decimal(v);
// dividing the significant by nearest lower value in [1.0, 1.1, 1.2, ..., 9.9] will greatly improve convergence
x.significant *= 10;
const auto isignificant = static_cast<std::size_t>(x.significant);
x.significant /= static_cast<double>(isignificant);
const double result = static_cast<double>(x.exponent - 1) * log_table[9] + log_table[isignificant - 1];
// 1.0 <= significant < 1.1 converges rapidly
const double y = (x.significant - 1) / (x.significant + 1);
const double y_squared = y * y;
double sum = 0;
// 5 terms are needed for convergence to machine precision in the worst case scenario
for (int k = 4; k > 0; --k) {
sum = y_squared * (1 / (2 * static_cast<double>(k) + 1) + sum);
}
sum = 2 * y * (1 + sum); // k = 0 term
return result + sum;
}
/* approximate e^x as Taylor series e^x = 1 + x/1! + x^2/2! + x^3/3! +... where N is the order of the Taylor series
use https://math.stackexchange.com/a/1988927 to improve convergence for large values
larger Factor values improve convergence for all values but reduce the precision
*/
template<std::size_t N, std::intmax_t Factor = 256>
requires gt_zero<Factor>
[[nodiscard]] constexpr double constexpr_exp(double v) noexcept
{
if constexpr (N == 0) {
return 1.0;
} else {
constexpr auto coefficients = []() {
std::array<double, N> coeffs;
std::size_t factorial = 1;
for (std::size_t i = 0; i < N; ++i) {
factorial *= i + 1;
coeffs[i] = 1.0 / static_cast<double>(factorial);
}
return coeffs;
}();
const double x = v / static_cast<double>(Factor);
double result = 0;
for (auto i = static_cast<std::intmax_t>(N - 1); i >= 0; --i) {
result = x * (coefficients[static_cast<std::size_t>(i)] + result);
}
// for factors of power of 2 this should be replaced by log2(Factor) multiplications by the compiler
return pow_impl<Factor>(1 + result);
}
}
// default template arguments provide reasonable precision even for fairly large exponents
// see constexpr_exp for template arguments
template<std::size_t ExpOrder = 10, std::intmax_t Factor = 128>
[[nodiscard]] constexpr double constexpr_pow(double v, double exponent) noexcept
{
const double x = exponent * constexpr_log(v);
return constexpr_exp<ExpOrder, Factor>(x);
}
} // namespace units::detail
@@ -0,0 +1,95 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/fixed_string.h>
#include <units/bits/external/text_tools.h>
#include <units/derived_dimension.h>
#include <units/symbol_text.h>
namespace units::detail {
template<bool Divide, std::size_t NegativeExpCount, std::size_t Idx>
constexpr auto operator_text()
{
if constexpr(Idx == 0) {
if constexpr(Divide && NegativeExpCount == 1) {
return basic_fixed_string("1/");
}
else {
return basic_fixed_string("");
}
}
else {
if constexpr(Divide && NegativeExpCount == 1) {
return basic_fixed_string("/");
}
else {
return basic_symbol_text(" ⋅ ", " ");
}
}
}
template<typename E, basic_symbol_text Symbol, std::size_t NegativeExpCount, std::size_t Idx>
constexpr auto exp_text()
{
// get calculation operator + symbol
const auto txt = operator_text<E::num < 0, NegativeExpCount, Idx>() + Symbol;
if constexpr(E::den != 1) {
// add root part
return txt + basic_fixed_string("^(") + regular<abs(E::num)>() + basic_fixed_string("/") + regular<E::den>() + basic_fixed_string(")");
}
else if constexpr(E::num != 1) {
// add exponent part
if constexpr(NegativeExpCount > 1) { // no '/' sign here (only negative exponents)
return txt + superscript<E::num>();
}
else if constexpr(E::num != -1) { // -1 is replaced with '/' sign here
return txt + superscript<abs(E::num)>();
}
else {
return txt;
}
}
else {
return txt;
}
}
template<typename... Es>
inline constexpr int negative_exp_count = ((Es::num < 0 ? 1 : 0) + ... + 0);
template<typename... Us, typename... Es, std::size_t... Idxs>
constexpr auto deduced_symbol_text(exponent_list<Es...>, std::index_sequence<Idxs...>)
{
constexpr auto neg_exp = negative_exp_count<Es...>;
return (exp_text<Es, Us::symbol, neg_exp, Idxs>() + ...);
}
template<DerivedDimension Dim, Unit... Us>
constexpr auto deduced_symbol_text()
{
return deduced_symbol_text<Us...>(typename Dim::recipe(), std::index_sequence_for<Us...>());
}
} // namespace units::detail
@@ -0,0 +1,56 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/derived_dimension.h>
namespace units::detail {
// same_scaled_units
template<typename ExpList, Unit... Us>
inline constexpr bool same_scaled_units = false;
template<typename... Es, Unit... Us>
inline constexpr bool same_scaled_units<exponent_list<Es...>, Us...> = (UnitOf<Us, typename Es::dimension> && ...);
// deduced_unit
template<Exponent E>
constexpr ratio inverse_if_negative(const ratio& r)
{
if constexpr(E::num * E::den > 0)
return r;
else
return inverse(r);
}
template<Unit... Us, typename... Es>
constexpr ratio derived_ratio(exponent_list<Es...>)
{
return (... * inverse_if_negative<Es>(pow<detail::abs(Es::num)>(Us::ratio) / dimension_unit<typename Es::dimension>::ratio));
}
template<DerivedDimension D, Unit... Us>
using deduced_unit = scaled_unit<derived_ratio<Us...>(typename D::recipe()), typename D::coherent_unit::reference>;
} // namespace units::detail
@@ -0,0 +1,60 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/base_dimension.h>
#include <units/bits/external/downcasting.h>
#include <units/exponent.h>
namespace units::detail {
/**
* @brief A dimension of a derived quantity
*
* Expression of the dependence of a quantity on the base quantities (and their base dimensions) of a system of
* quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factors.
* A power of a factor is the factor raised to an exponent.
*
* A derived dimension can be formed from multiple exponents (i.e. speed is represented as "exponent<L, 1>, exponent<T, -1>").
* It is also possible to form a derived dimension with only one exponent (i.e. frequency is represented as just
* "exponent<T, -1>").
*
* @note This class template is used by the library engine and should not be directly instantiated by the user.
*
* @tparam Es zero or more exponents of a derived dimension
*/
template<Exponent... Es>
requires (BaseDimension<typename Es::dimension> && ...)
struct derived_dimension_base : downcast_base<derived_dimension_base<Es...>> {
using exponents = exponent_list<Es...>;
};
template<typename T>
struct to_derived_dimension_base;
template<Exponent... Es>
struct to_derived_dimension_base<exponent_list<Es...>> {
using type = derived_dimension_base<Es...>;
};
} // namespace units::detail
@@ -0,0 +1,67 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/type_list.h>
#include <units/exponent.h>
#include <ratio> // TODO remove this dependency with #11
namespace units::detail {
/**
* @brief Consolidates contiguous ranges of exponents of the same dimension
*
* If there is more than one exponent with the same dimension they are aggregated into one exponent by adding
* their exponents. If this accumulation will result with 0, such a dimension is removed from the list.
*
* @tparam D derived dimension to consolidate
*/
template<typename ExpList>
struct dim_consolidate;
template<>
struct dim_consolidate<exponent_list<>> {
using type = exponent_list<>;
};
template<typename E>
struct dim_consolidate<exponent_list<E>> {
using type = exponent_list<E>;
};
template<typename E1, typename... ERest>
struct dim_consolidate<exponent_list<E1, ERest...>> {
using type = type_list_push_front<typename dim_consolidate<exponent_list<ERest...>>::type, E1>;
};
template<BaseDimension Dim, std::intmax_t Num1, std::intmax_t Den1, std::intmax_t Num2, std::intmax_t Den2, typename... ERest>
struct dim_consolidate<exponent_list<exponent<Dim, Num1, Den1>, exponent<Dim, Num2, Den2>, ERest...>> {
// TODO: we have ration_add now, but dim_consolidate etc, now need to cope with our new ratio
using r1 = std::ratio<Num1, Den1>;
using r2 = std::ratio<Num2, Den2>;
using r = std::ratio_add<r1, r2>;
using type = conditional<r::num == 0, typename dim_consolidate<exponent_list<ERest...>>::type,
typename dim_consolidate<exponent_list<exponent<Dim, r::num, r::den>, ERest...>>::type>;
};
} // namespace units::detail
+59
View File
@@ -0,0 +1,59 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/derived_dimension_base.h>
#include <units/bits/external/type_list.h>
#include <units/exponent.h>
namespace units::detail {
/**
* @brief Unpacks the list of potentially derived dimensions to a list containing only base dimensions
*
* @tparam Es Exponents of potentially derived dimensions
*/
template<Exponent... Es>
struct dim_unpack;
template<>
struct dim_unpack<> {
using type = exponent_list<>;
};
template<BaseDimension Dim, std::intmax_t Num, std::intmax_t Den, Exponent... ERest>
struct dim_unpack<exponent<Dim, Num, Den>, ERest...> {
using type = type_list_push_front<typename dim_unpack<ERest...>::type, exponent<Dim, Num, Den>>;
};
template<DerivedDimension Dim, std::intmax_t Num, std::intmax_t Den, Exponent... ERest>
struct dim_unpack<exponent<Dim, Num, Den>, ERest...> {
using type = TYPENAME dim_unpack<exponent<downcast_base_t<Dim>, Num, Den>, ERest...>::type;
};
template<Exponent... Es, std::intmax_t Num, std::intmax_t Den, Exponent... ERest>
struct dim_unpack<exponent<derived_dimension_base<Es...>, Num, Den>, ERest...> {
using type = type_list_push_front<typename dim_unpack<ERest...>::type, exponent_multiply<Es, Num, Den>...>;
};
} // namespace units::detail
+201
View File
@@ -0,0 +1,201 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/derived_dimension.h>
#include <units/unit.h>
namespace units {
/**
* @brief Unknown dimension
*
* Sometimes a temporary partial result of a complex calculation may not result in a predefined
* dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit`
* and ratio(1).
*
* @tparam Es the list of exponents of ingredient dimensions
*/
template<Exponent... Es>
struct unknown_dimension : derived_dimension<unknown_dimension<Es...>, unknown_coherent_unit, Es...> {};
namespace detail {
template<DerivedDimension D>
struct check_unknown {
using type = D;
};
// downcast did not find a user predefined type
template<typename... Es>
struct check_unknown<derived_dimension_base<Es...>> {
using type = unknown_dimension<Es...>;
};
template<Dimension D>
struct downcast_dimension_impl;
template<BaseDimension D>
struct downcast_dimension_impl<D> {
using type = D;
};
template<DerivedDimension D>
struct downcast_dimension_impl<D> {
using type = TYPENAME check_unknown<downcast<D>>::type;
};
} // namespace detail
template<Dimension D>
using downcast_dimension = TYPENAME detail::downcast_dimension_impl<D>::type;
// dim_invert
namespace detail {
template<Dimension D>
struct dim_invert_impl;
template<BaseDimension D>
struct dim_invert_impl<D> {
using type = downcast_dimension<derived_dimension_base<exponent<D, -1>>>;
};
template<BaseDimension D>
struct dim_invert_impl<derived_dimension_base<exponent<D, -1>>> {
using type = D;
};
template<typename... Es>
struct dim_invert_impl<derived_dimension_base<Es...>> {
using type = downcast_dimension<derived_dimension_base<exponent_invert<Es>...>>;
};
template<DerivedDimension D>
struct dim_invert_impl<D> : dim_invert_impl<downcast_base_t<D>> {
};
} // namespace detail
template<Dimension D>
using dim_invert = TYPENAME detail::dim_invert_impl<D>::type;
// dimension_multiply
namespace detail {
template<typename T>
struct to_dimension;
template<Exponent... Es>
struct to_dimension<exponent_list<Es...>> {
using type = derived_dimension_base<Es...>;
};
template<BaseDimension D>
struct to_dimension<exponent_list<exponent<D, 1>>> {
using type = D;
};
/**
* @brief Merges 2 sorted derived dimensions into one units::derived_dimension_base
*
* A result of a dimensional calculation may result with many exponents of the same base dimension orginated
* from different parts of the equation. As the exponents lists of both operands it is enough to merge them
* into one list and consolidate duplicates. Also it is possible that final exponents list will contain only
* one element being a base dimension with exponent 1. In such a case the final dimension should be the base
* dimension itself.
*/
template<Dimension D1, Dimension D2>
using merge_dimension = TYPENAME to_dimension<typename dim_consolidate<type_list_merge_sorted<typename D1::exponents, typename D2::exponents, exponent_less>>::type>::type;
template<Dimension D1, Dimension D2>
struct dimension_multiply_impl;
template<BaseDimension D1, BaseDimension D2>
struct dimension_multiply_impl<D1, D2> {
using type = downcast_dimension<merge_dimension<derived_dimension_base<exponent<D1, 1>>, derived_dimension_base<exponent<D2, 1>>>>;
};
template<BaseDimension D1, DerivedDimension D2>
struct dimension_multiply_impl<D1, D2> {
using type = downcast_dimension<merge_dimension<derived_dimension_base<exponent<D1, 1>>, typename D2::downcast_base_type>>;
};
template<DerivedDimension D1, BaseDimension D2>
struct dimension_multiply_impl<D1, D2> {
using type = TYPENAME dimension_multiply_impl<D2, D1>::type;
};
template<DerivedDimension D1, DerivedDimension D2>
struct dimension_multiply_impl<D1, D2> {
using type = downcast_dimension<merge_dimension<typename D1::downcast_base_type, typename D2::downcast_base_type>>;
};
} // namespace detail
template<Dimension D1, Dimension D2>
using dimension_multiply = TYPENAME detail::dimension_multiply_impl<D1, D2>::type;
template<Dimension D1, Dimension D2>
using dimension_divide = TYPENAME detail::dimension_multiply_impl<D1, dim_invert<D2>>::type;
// dimension_pow
namespace detail {
template<Dimension D, std::intmax_t Num, std::intmax_t Den = 1>
struct dimension_pow_impl;
template<BaseDimension D, std::intmax_t Num, std::intmax_t Den>
struct dimension_pow_impl<D, Num, Den> {
using type = downcast_dimension<derived_dimension_base<exponent<D, Num, Den>>>;
};
template<BaseDimension D>
struct dimension_pow_impl<D, 1, 1> {
using type = D;
};
template<BaseDimension D, std::intmax_t Num, std::intmax_t Den>
struct dimension_pow_impl<derived_dimension_base<exponent<D, Den, Num>>, Num, Den> {
using type = D;
};
template<DerivedDimension D, std::intmax_t Num, std::intmax_t Den>
struct dimension_pow_impl<D, Num, Den> {
using type = TYPENAME dimension_pow_impl<downcast_base_t<D>, Num, Den>::type;
};
template<typename... Es, std::intmax_t Num, std::intmax_t Den>
struct dimension_pow_impl<derived_dimension_base<Es...>, Num, Den> {
using type = downcast_dimension<derived_dimension_base<exponent_multiply<Es, Num, Den>...>>;
};
} // namespace detail
template<Dimension D, std::intmax_t Num, std::intmax_t Den = 1>
using dimension_pow = TYPENAME detail::dimension_pow_impl<D, Num, Den>::type;
template<Dimension D>
using dimension_sqrt = TYPENAME detail::dimension_pow_impl<D, 1, 2>::type;
} // namespace units
+112
View File
@@ -0,0 +1,112 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/basic_concepts.h>
#include <units/unit.h>
namespace units {
namespace detail {
template<typename T, typename U>
struct equivalent_impl : std::false_type {
};
template<typename T>
struct equivalent_impl<T, T> : std::true_type {
};
// units
template<Unit U1, Unit U2>
struct equivalent_impl<U1, U2> : std::disjunction<std::is_base_of<U1, U2>, std::is_base_of<U2, U1>> {};
// dimensions
template<BaseDimension D1, BaseDimension D2>
struct equivalent_impl<D1, D2> : std::conjunction<std::bool_constant<D1::symbol == D2::symbol>,
same_unit_reference<typename D1::base_unit, typename D2::base_unit>> {
};
template<Exponent E1, Exponent E2>
struct equivalent_exp : std::false_type {
};
template<BaseDimension Dim1, std::intmax_t Num, std::intmax_t Den, BaseDimension Dim2>
struct equivalent_exp<exponent<Dim1, Num, Den>, exponent<Dim2, Num, Den>> : equivalent_impl<Dim1, Dim2> {
};
template<DerivedDimension D1, DerivedDimension D2>
struct equivalent_derived_dim : std::false_type {
};
template<typename... Es1, typename... Es2>
requires(sizeof...(Es1) == sizeof...(Es2))
struct equivalent_derived_dim<derived_dimension_base<Es1...>, derived_dimension_base<Es2...>> :
std::conjunction<equivalent_exp<Es1, Es2>...> {
};
template<DerivedDimension D1, DerivedDimension D2>
struct equivalent_impl<D1, D2> :
std::disjunction<std::is_base_of<D1, D2>, std::is_base_of<D2, D1>,
equivalent_derived_dim<downcast_base_t<D1>, downcast_base_t<D2>>> {
};
// additionally accounts for unknown dimensions
template<Unit U1, Dimension D1, Unit U2, Dimension D2>
struct equivalent_unit : std::disjunction<equivalent_impl<U1, U2>,
std::bool_constant<U1::ratio / dimension_unit<D1>::ratio == U2::ratio / dimension_unit<D2>::ratio>> {};
// (point) kinds
template<typename T, typename U>
requires (Kind<T> && Kind<U>) || (PointKind<T> && PointKind<U>)
struct equivalent_impl<T, U> :
std::conjunction<std::is_same<typename T::base_kind, typename U::base_kind>,
equivalent_impl<typename T::dimension, typename U::dimension>> {};
// quantities, quantity points, quantity (point) kinds
template<typename Q1, typename Q2>
requires (Quantity<Q1> && Quantity<Q2>) || (QuantityPoint<Q1> && QuantityPoint<Q2>)
struct equivalent_impl<Q1, Q2> : std::conjunction<equivalent_impl<typename Q1::dimension, typename Q2::dimension>,
equivalent_unit<typename Q1::unit, typename Q1::dimension,
typename Q2::unit, typename Q2::dimension>> {};
template<typename QK1, typename QK2>
requires (QuantityKind<QK1> && QuantityKind<QK2>) || (QuantityPointKind<QK1> && QuantityPointKind<QK2>)
struct equivalent_impl<QK1, QK2> : std::conjunction<equivalent_impl<typename QK1::kind_type, typename QK2::kind_type>,
equivalent_impl<typename QK1::quantity_type, typename QK2::quantity_type>> {};
} // namespace detail
template<typename T, typename U>
inline constexpr bool equivalent = detail::equivalent_impl<T, U>::value;
} // namespace units
+103
View File
@@ -0,0 +1,103 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/hacks.h>
#include <type_traits>
#ifdef UNITS_DOWNCAST_MODE
#if UNITS_DOWNCAST_MODE < 0 || UNITS_DOWNCAST_MODE > 2
#error "Invalid UNITS_DOWNCAST_MODE value"
#endif
#else
#define UNITS_DOWNCAST_MODE 1
#endif
namespace units {
template<typename BaseType>
struct downcast_base {
using downcast_base_type = BaseType;
friend auto downcast_guide(downcast_base);
friend auto downcast_poison_pill(downcast_base);
};
template<typename T>
concept Downcastable =
requires { typename T::downcast_base_type; } &&
std::derived_from<T, downcast_base<typename T::downcast_base_type>>;
template<typename T>
concept has_downcast_guide = requires(T t) { downcast_guide(t); };
template<typename T>
concept has_downcast_poison_pill = requires(T t) { downcast_poison_pill(t); };
template<typename Target, Downcastable T>
struct downcast_child : T {
friend auto downcast_guide(typename T::downcast_base)
{ return std::type_identity<Target>(); }
};
template<Downcastable T>
struct downcast_poison : T {
friend auto downcast_poison_pill(typename T::downcast_base)
{ return true; }
};
enum class downcast_mode {
off = 0, // no downcasting at all
on = 1, // downcasting always forced -> compile-time errors in case of duplicated definitions
automatic = 2 // downcasting automatically enabled if no collisions are present
};
template<typename Target, Downcastable T, downcast_mode mode = static_cast<downcast_mode>(UNITS_DOWNCAST_MODE)>
struct downcast_dispatch : std::conditional_t<mode == downcast_mode::off, T,
#ifdef UNITS_COMP_MSVC
downcast_child<Target, T>> {};
#else
std::conditional_t<mode == downcast_mode::automatic && has_downcast_guide<T>,
downcast_poison<T>, downcast_child<Target, T>>> {};
#endif
namespace detail {
template<typename T>
constexpr auto downcast_impl()
{
if constexpr(has_downcast_guide<downcast_base<T>> && !has_downcast_poison_pill<downcast_base<T>>)
return decltype(downcast_guide(std::declval<downcast_base<T>>()))();
else
return std::type_identity<T>();
}
} // namespace detail
template<Downcastable T>
using downcast = TYPENAME decltype(detail::downcast_impl<T>())::type;
template<Downcastable T>
using downcast_base_t = TYPENAME T::downcast_base_type;
} // namespace units
+122
View File
@@ -0,0 +1,122 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/hacks.h>
#include <cstdlib>
#include <compare>
// TODO use <algorithm> when moved to C++20 modules (parsing takes too long for each translation unit)
namespace units {
/**
* @brief A compile-time fixed string
*
* @tparam CharT Character type to be used by the string
* @tparam N The size of the string
*/
template<typename CharT, std::size_t N>
struct basic_fixed_string {
CharT data_[N + 1] = {};
using iterator = CharT*;
using const_iterator = const CharT*;
constexpr explicit(false) basic_fixed_string(CharT ch) noexcept { data_[0] = ch; }
constexpr explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) noexcept
{
if constexpr (N != 0)
for (std::size_t i = 0; i < N; ++i) data_[i] = txt[i];
}
[[nodiscard]] constexpr bool empty() const noexcept { return N == 0; }
[[nodiscard]] constexpr std::size_t size() const noexcept { return N; }
[[nodiscard]] constexpr const CharT* data() const noexcept { return data_; }
[[nodiscard]] constexpr const CharT* c_str() const noexcept { return data(); }
[[nodiscard]] constexpr const CharT& operator[](std::size_t index) const noexcept { return data()[index]; }
[[nodiscard]] constexpr CharT operator[](std::size_t index) noexcept { return data()[index]; }
[[nodiscard]] constexpr iterator begin() noexcept { return data(); }
[[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); }
[[nodiscard]] constexpr iterator end() noexcept { return data() + size(); }
[[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); }
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_fixed_string<CharT, N + N2> operator+(
const basic_fixed_string& lhs, const basic_fixed_string<CharT, N2>& rhs) noexcept
{
CharT txt[N + N2 + 1] = {};
for (size_t i = 0; i != N; ++i) txt[i] = lhs[i];
for (size_t i = 0; i != N2; ++i) txt[N + i] = rhs[i];
return basic_fixed_string<CharT, N + N2>(txt);
}
[[nodiscard]] constexpr bool operator==(const basic_fixed_string& other) const
{
if (size() != other.size())
return false;
for (size_t i = 0; i != size(); ++i) {
if ((*this)[i] != other[i])
return false;
}
return true;
// return std::ranges::equal(*this, other);
}
template<std::size_t N2>
[[nodiscard]] friend constexpr bool operator==(const basic_fixed_string&, const basic_fixed_string<CharT, N2>&) { return false; }
template<std::size_t N2>
[[nodiscard]] friend constexpr auto operator<=>(const basic_fixed_string& lhs, const basic_fixed_string<CharT, N2>& rhs)
{
auto first1 = lhs.begin();
const auto last1 = lhs.end();
auto first2 = rhs.begin();
const auto last2 = rhs.end();
auto comp = std::compare_three_way();
for (; first1 != last1 && first2 != last2; ++first1, ++first2)
if (auto cmp = comp(*first1, *first2); cmp != 0)
return cmp;
return first1 != last1 ? std::strong_ordering::greater :
first2 != last2 ? std::strong_ordering::less :
std::strong_ordering::equal;
// return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
};
template<typename CharT, std::size_t N>
basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string<CharT, N - 1>;
template<typename CharT>
basic_fixed_string(CharT) -> basic_fixed_string<CharT, 1>;
template<std::size_t N>
using fixed_string = basic_fixed_string<char, N>;
} // namespace units
+36
View File
@@ -0,0 +1,36 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/fixed_string.h>
#include <ostream>
namespace units {
template<typename CharT, class Traits, std::size_t N>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const basic_fixed_string<CharT, N>& txt)
{
return os << txt.c_str();
}
} // namespace units
+188
View File
@@ -0,0 +1,188 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#if __clang__
#define UNITS_COMP_CLANG __clang_major__
#elif __GNUC__
#define UNITS_COMP_GCC __GNUC__
#define UNITS_COMP_GCC_MINOR __GNUC_MINOR__
#elif _MSC_VER
#define UNITS_COMP_MSVC _MSC_VER
#endif
#if UNITS_COMP_CLANG
#include <ciso646>
#if _LIBCPP_VERSION
#define UNITS_LIBCXX _LIBCPP_VERSION
#endif
#endif
#if UNITS_COMP_CLANG == 12 && UNITS_LIBCXX
#include <concepts/compare.hpp>
#include <concepts/concepts.hpp>
#include <range/v3/functional/comparisons.hpp>
#include <range/v3/iterator.hpp>
#include <range/v3/range/concepts.hpp>
#include <range/v3/algorithm/lower_bound.hpp>
#include <range/v3/algorithm/transform.hpp>
#endif
#include <concepts>
#include <compare>
#if UNITS_COMP_MSVC || UNITS_COMP_CLANG
#define TYPENAME typename
#else
#define TYPENAME
#endif
namespace std {
#if UNITS_COMP_GCC
template<class T>
concept default_constructible = constructible_from<T>;
#elif UNITS_COMP_CLANG && UNITS_LIBCXX
// concepts
using concepts::common_with;
using concepts::constructible_from;
using concepts::convertible_to;
using concepts::copy_constructible;
using concepts::derived_from;
using concepts::equality_comparable;
using concepts::equality_comparable_with;
using concepts::integral;
using concepts::move_constructible;
using concepts::regular;
using concepts::three_way_comparable;
using concepts::three_way_comparable_with;
using concepts::totally_ordered;
using ranges::compare_three_way;
namespace ranges {
using ::ranges::begin;
using ::ranges::end;
using ::ranges::distance;
using ::ranges::forward_range;
using ::ranges::input_range;
using ::ranges::range_value_t;
using ::ranges::lower_bound;
using ::ranges::transform;
}
// missing in Range-v3
template<class T>
concept floating_point = std::is_floating_point_v<T>;
template<class T>
concept default_initializable =
std::constructible_from<T> &&
requires { T{}; } &&
requires { ::new (static_cast<void*>(nullptr)) T; };
template<class F, class... Args>
concept invocable =
requires(F&& f, Args&&... args) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
};
template<class F, class... Args>
concept regular_invocable = invocable<F, Args...>;
template<class T, class U>
constexpr bool cmp_equal(T t, U u) noexcept
{
using UT = std::make_unsigned_t<T>;
using UU = std::make_unsigned_t<U>;
if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
return t == u;
else if constexpr (std::is_signed_v<T>)
return t < 0 ? false : UT(t) == u;
else
return u < 0 ? false : t == UU(u);
}
template<class T, class U>
constexpr bool cmp_not_equal(T t, U u) noexcept
{
return !cmp_equal(t, u);
}
template<class T, class U>
constexpr bool cmp_less(T t, U u) noexcept
{
using UT = std::make_unsigned_t<T>;
using UU = std::make_unsigned_t<U>;
if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
return t < u;
else if constexpr (std::is_signed_v<T>)
return t < 0 ? true : UT(t) < u;
else
return u < 0 ? false : t < UU(u);
}
template<class T, class U>
constexpr bool cmp_greater(T t, U u) noexcept
{
return cmp_less(u, t);
}
template<class T, class U>
constexpr bool cmp_less_equal(T t, U u) noexcept
{
return !cmp_greater(t, u);
}
template<class T, class U>
constexpr bool cmp_greater_equal(T t, U u) noexcept
{
return !cmp_less(t, u);
}
template<class R, class T>
constexpr bool in_range(T t) noexcept
{
return std::cmp_greater_equal(t, std::numeric_limits<R>::min()) &&
std::cmp_less_equal(t, std::numeric_limits<R>::max());
}
#endif
} // namespace std
+77
View File
@@ -0,0 +1,77 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/fixed_string.h>
#include <units/symbol_text.h>
namespace units::detail {
template<std::intmax_t Value>
requires (0 <= Value) && (Value < 10)
inline constexpr basic_fixed_string superscript_number = "";
template<> inline constexpr basic_fixed_string superscript_number<0> = "\u2070";
template<> inline constexpr basic_fixed_string superscript_number<1> = "\u00b9";
template<> inline constexpr basic_fixed_string superscript_number<2> = "\u00b2";
template<> inline constexpr basic_fixed_string superscript_number<3> = "\u00b3";
template<> inline constexpr basic_fixed_string superscript_number<4> = "\u2074";
template<> inline constexpr basic_fixed_string superscript_number<5> = "\u2075";
template<> inline constexpr basic_fixed_string superscript_number<6> = "\u2076";
template<> inline constexpr basic_fixed_string superscript_number<7> = "\u2077";
template<> inline constexpr basic_fixed_string superscript_number<8> = "\u2078";
template<> inline constexpr basic_fixed_string superscript_number<9> = "\u2079";
inline constexpr basic_symbol_text superscript_minus("\u207b", "-");
inline constexpr basic_symbol_text superscript_prefix("", "^");
template<std::intmax_t Value>
constexpr auto superscript_helper()
{
if constexpr(Value < 0)
return superscript_minus + superscript_helper<-Value>();
else if constexpr(Value < 10)
return basic_symbol_text(superscript_number<Value>, basic_fixed_string(static_cast<char>('0' + Value)));
else
return superscript_helper<Value / 10>() + superscript_helper<Value % 10>();
}
template<std::intmax_t Value>
constexpr auto superscript()
{
return superscript_prefix + superscript_helper<Value>();
}
template<std::intmax_t Value>
constexpr auto regular()
{
if constexpr (Value < 0)
return basic_fixed_string("-") + superscript_helper<-Value>();
else if constexpr (Value < 10)
return basic_symbol_text(static_cast<char>('0' + Value));
else
return regular<Value / 10>() + regular<Value % 10>();
}
} // namespace units::detail
+226
View File
@@ -0,0 +1,226 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/type_traits.h>
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable:4296) // warning C4296: '<': expression is always false
#endif //_MSC_VER
namespace units {
namespace detail {
template<typename T>
inline constexpr bool is_type_list = false;
template<template<typename...> typename T, typename... Types>
inline constexpr bool is_type_list<T<Types...>> = true;
} // namespace detail
template<typename T>
concept TypeList = detail::is_type_list<T>;
// push_front
namespace detail {
template<typename List, typename... Types>
struct type_list_push_front_impl;
template<template<typename...> typename List, typename... OldTypes, typename... NewTypes>
struct type_list_push_front_impl<List<OldTypes...>, NewTypes...> {
using type = List<NewTypes..., OldTypes...>;
};
} // namespace detail
template<TypeList List, typename... Types>
using type_list_push_front = TYPENAME detail::type_list_push_front_impl<List, Types...>::type;
// push_back
namespace detail {
template<typename List, typename... Types>
struct type_list_push_back_impl;
template<template<typename...> typename List, typename... OldTypes, typename... NewTypes>
struct type_list_push_back_impl<List<OldTypes...>, NewTypes...> {
using type = List<OldTypes..., NewTypes...>;
};
} // namespace detail
template<TypeList List, typename... Types>
using type_list_push_back = TYPENAME detail::type_list_push_back_impl<List, Types...>::type;
// join
namespace detail {
template<typename List, typename... Rest>
struct type_list_join_impl {
using type = List;
};
template<template<typename...> typename List, typename... First, typename... Second, typename... Rest>
struct type_list_join_impl<List<First...>, List<Second...>, Rest...> {
using type = TYPENAME type_list_join_impl<List<First..., Second...>, Rest...>::type;
};
} // namespace detail
template<TypeList... Lists>
using type_list_join = TYPENAME detail::type_list_join_impl<Lists...>::type;
// split
namespace detail {
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename... Types>
struct split_impl;
template<template<typename...> typename List, std::size_t Idx, std::size_t N>
struct split_impl<List, Idx, N> {
using first_list = List<>;
using second_list = List<>;
};
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename T, typename... Rest>
requires (Idx < N)
struct split_impl<List, Idx, N, T, Rest...> : split_impl<List, Idx + 1, N, Rest...> {
using base = split_impl<List, Idx + 1, N, Rest...>;
using first_list = TYPENAME type_list_push_front_impl<typename base::first_list, T>::type;
using second_list = TYPENAME base::second_list;
};
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename T, typename... Rest>
struct split_impl<List, Idx, N, T, Rest...> : split_impl<List, Idx + 1, N, Rest...> {
using base = split_impl<List, Idx + 1, N, Rest...>;
using first_list = TYPENAME base::first_list;
using second_list = TYPENAME type_list_push_front_impl<typename base::second_list, T>::type;
};
} // namespace detail
template<TypeList List, std::size_t N>
struct type_list_split;
template<template<typename...> typename List, std::size_t N, typename... Types>
struct type_list_split<List<Types...>, N> {
static_assert(N <= sizeof...(Types), "Invalid index provided");
using split = detail::split_impl<List, 0, N, Types...>;
using first_list = TYPENAME split::first_list;
using second_list = TYPENAME split::second_list;
};
// split_half
template<TypeList List>
struct type_list_split_half;
template<template<typename...> typename List, typename... Types>
struct type_list_split_half<List<Types...>> : type_list_split<List<Types...>, (sizeof...(Types) + 1) / 2> {
};
// merge_sorted
namespace detail {
template<typename SortedList1, typename SortedList2, template<typename, typename> typename Pred>
struct type_list_merge_sorted_impl;
template<template<typename...> typename List, template<typename, typename> typename Pred>
struct type_list_merge_sorted_impl<List<>, List<>, Pred> {
using type = List<>;
};
template<template<typename...> typename List, typename... Lhs, template<typename, typename> typename Pred>
struct type_list_merge_sorted_impl<List<Lhs...>, List<>, Pred> {
using type = List<Lhs...>;
};
template<template<typename...> typename List, typename... Rhs, template<typename, typename> typename Pred>
struct type_list_merge_sorted_impl<List<>, List<Rhs...>, Pred> {
using type = List<Rhs...>;
};
template<template<typename...> typename List, typename Lhs1, typename... LhsRest, typename Rhs1, typename... RhsRest,
template<typename, typename> typename Pred>
requires Pred<Lhs1, Rhs1>::value
struct type_list_merge_sorted_impl<List<Lhs1, LhsRest...>, List<Rhs1, RhsRest...>, Pred> {
using type = TYPENAME type_list_push_front_impl<typename type_list_merge_sorted_impl<List<LhsRest...>, List<Rhs1, RhsRest...>, Pred>::type, Lhs1>::type;
};
template<template<typename...> typename List, typename Lhs1, typename... LhsRest, typename Rhs1, typename... RhsRest,
template<typename, typename> typename Pred>
struct type_list_merge_sorted_impl<List<Lhs1, LhsRest...>, List<Rhs1, RhsRest...>, Pred> {
using type = TYPENAME type_list_push_front_impl<typename type_list_merge_sorted_impl<List<Lhs1, LhsRest...>, List<RhsRest...>, Pred>::type, Rhs1>::type;
};
} // namespace detail
template<TypeList SortedList1, TypeList SortedList2, template<typename, typename> typename Pred>
using type_list_merge_sorted = TYPENAME detail::type_list_merge_sorted_impl<SortedList1, SortedList2, Pred>::type;
// sort
namespace detail {
template<typename List, template<typename, typename> typename Pred>
struct type_list_sort_impl;
template<template<typename...> typename List, template<typename, typename> typename Pred>
struct type_list_sort_impl<List<>, Pred> {
using type = List<>;
};
template<template<typename...> typename List, typename T, template<typename, typename> typename Pred>
struct type_list_sort_impl<List<T>, Pred> {
using type = List<T>;
};
template<template<typename...> typename List, typename... Types, template<typename, typename> typename Pred>
struct type_list_sort_impl<List<Types...>, Pred> {
using types = List<Types...>;
using split = type_list_split_half<List<Types...>>;
using sorted_left = TYPENAME type_list_sort_impl<typename split::first_list, Pred>::type;
using sorted_right = TYPENAME type_list_sort_impl<typename split::second_list, Pred>::type;
using type = TYPENAME type_list_merge_sorted_impl<sorted_left, sorted_right, Pred>::type;
};
} // namespace detail
template<TypeList List, template<typename, typename> typename Pred>
using type_list_sort = TYPENAME detail::type_list_sort_impl<List, Pred>::type;
} // namespace units
#ifdef _MSC_VER
#pragma warning (pop)
#endif //_MSC_VER
+80
View File
@@ -0,0 +1,80 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/hacks.h>
#include <type_traits>
#include <utility>
namespace units {
// conditional
namespace detail {
template<bool>
struct conditional_impl {
template<typename T, typename F>
using type = F;
};
template<>
struct conditional_impl<true> {
template<typename T, typename F>
using type = T;
};
} // namespace detail
template<bool B, typename T, typename F>
using conditional = TYPENAME detail::conditional_impl<B>::template type<T, F>;
// is_same
template<class T, class U>
inline constexpr bool is_same_v = false;
template<class T>
inline constexpr bool is_same_v<T, T> = true;
template<class T, class U>
using is_same = std::bool_constant<is_same_v<T, U>>;
// is_specialization_of
template<typename T, template<typename...> typename Type>
inline constexpr bool is_specialization_of = false;
template<typename... Params, template<typename...> typename Type>
inline constexpr bool is_specialization_of<Type<Params...>, Type> = true;
// is_derived_from_specialization_of
namespace detail {
template<template<typename...> typename Type, typename... Params>
void to_base_specialization_of(const volatile Type<Params...>*);
} // namespace detail
template<typename T, template<typename...> typename Type>
// inline constexpr bool // TODO: Replace with concept when it works with MSVC
concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of<Type>(t); };
} // namespace units
@@ -0,0 +1,35 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <cstdint>
namespace units::detail {
template<std::intmax_t N>
concept gt_zero = (N > 0);
template<std::intmax_t N>
concept non_zero = (N != 0);
} // namespace units::detail
+77
View File
@@ -0,0 +1,77 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <cstdint>
#include <cassert>
namespace units::detail {
constexpr std::intmax_t ipow10(std::intmax_t exp)
{
assert(exp >= 0);
if (exp == 0) return 1;
std::intmax_t result = 1;
while (exp > 0) {
result *= 10;
--exp;
}
return result;
}
template<typename Rep>
constexpr Rep fpow10(std::intmax_t exp)
{
if (exp == 0) return Rep(1.0);
Rep result = Rep(1.0);
if (exp < 0) {
while (exp < 0) {
result = result / Rep(10.0);
++exp;
}
} else {
while (exp > 0) {
result = result * Rep(10.0);
--exp;
}
}
return result;
}
template<std::intmax_t N, typename T>
constexpr T pow_impl(const T& v) noexcept
{
if constexpr (N == 0) {
return T(1);
} else if constexpr (N == 1) {
return v;
} else if constexpr (N < 0) {
return 1 / pow_impl<-N>(v);
} else if constexpr (N % 2 == 0) { // even
return pow_impl<N / 2>(v * v);
} else { // odd
return v * pow_impl<(N - 1) / 2>(v * v);
}
}
} // namespace units::detail
+139
View File
@@ -0,0 +1,139 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/dimension_op.h>
#include <units/bits/equivalent.h>
namespace units {
// DimensionOfT
#if UNITS_DOWNCAST_MODE == 0
namespace detail {
template<typename Dim, template<typename...> typename DimTemplate>
inline constexpr bool same_exponents_of = false;
template<Exponent... Es, template<typename...> typename DimTemplate>
inline constexpr bool same_exponents_of<unknown_dimension<Es...>, DimTemplate> = requires { typename DimTemplate<unknown_dimension<Es...>, unknown_coherent_unit, typename Es::dimension...>; } && std::same_as<exponent_list<Es...>, typename DimTemplate<unknown_dimension<Es...>, unknown_coherent_unit, typename Es::dimension...>::recipe>;
} // namespace detail
template<typename Dim, template<typename...> typename DimTemplate>
concept EquivalentUnknownDimensionOfT = Dimension<Dim> && is_derived_from_specialization_of<Dim, unknown_dimension> && detail::same_exponents_of<Dim, DimTemplate>;
#endif
/**
* @brief A concept matching all dimensions matching provided dimension class template
*
* Satisfied by all dimensions being the instantiations derived from the provided dimension
* class template.
*/
template<typename Dim, template<typename...> typename DimTemplate>
concept DimensionOfT = Dimension<Dim> && (is_derived_from_specialization_of<Dim, DimTemplate>
#if UNITS_DOWNCAST_MODE == 0
|| EquivalentUnknownDimensionOfT<Dim, DimTemplate>
#endif
);
/**
* @brief A concept matching all quantities with provided dimension class template
*
* Satisfied by all quantities with a dimension being the instantiation derived from
* the provided dimension class template.
*/
template<typename Q, template<typename...> typename DimTemplate>
concept QuantityOfT = Quantity<Q> && DimensionOfT<typename Q::dimension, DimTemplate>;
/**
* @brief A concept matching all quantities with provided dimension
*
* Satisfied by all quantities with a dimension being the instantiation derived from
* the provided dimension type.
*/
template<typename Q, typename Dim>
concept QuantityOf = Quantity<Q> && Dimension<Dim> && equivalent<typename Q::dimension, Dim>;
/**
* @brief A concept matching two equivalent quantities
*
* Satisfied by quantities having equivalent dimensions.
*/
template<typename Q1, typename Q2>
concept QuantityEquivalentTo = Quantity<Q1> && QuantityOf<Q2, typename Q1::dimension>;
/**
* @brief A concept matching all quantity points with provided dimension
*
* Satisfied by all quantity points with a dimension being the instantiation derived from
* the provided dimension type.
*/
template<typename QP, typename Dim>
concept QuantityPointOf = QuantityPoint<QP> && Dimension<Dim> && equivalent<typename QP::dimension, Dim>;
/**
* @brief A concept matching two equivalent quantity points
*
* Satisfied by quantity points having equivalent dimensions.
*/
template<typename QP1, typename QP2>
concept QuantityPointEquivalentTo = QuantityPoint<QP1> && QuantityPointOf<QP2, typename QP1::dimension>;
/**
* @brief A concept matching only quantity kinds of a specific kind.
*
* @tparam QK Quantity kind to verify.
* @tparam K Kind type to use for verification.
*/
template<typename QK, typename K>
concept QuantityKindOf = QuantityKind<QK> && Kind<K> && equivalent<typename QK::kind_type, K>;
/**
* @brief A concept matching two equivalent quantity kinds
*
* Satisfied by quantity kinds having equivalent kinds.
*/
template<typename QK1, typename QK2>
concept QuantityKindEquivalentTo = QuantityKind<QK1> && QuantityKindOf<QK2, typename QK1::kind_type>;
/**
* @brief A concept matching only quantity point kinds of a specific point kind.
*
* @tparam QPK Quantity point kind to verify.
* @tparam PK Point kind type to use for verification.
*/
template<typename QPK, typename PK>
concept QuantityPointKindOf = QuantityPointKind<QPK> && PointKind<PK> && equivalent<typename QPK::point_kind_type, PK>;
/**
* @brief A concept matching two equivalent quantity point kinds
*
* Satisfied by quantity point kinds having equivalent kinds.
*/
template<typename QPK1, typename QPK2>
concept QuantityPointKindEquivalentTo =
QuantityPointKind<QPK1> && QuantityPointKindOf<QPK2, typename QPK1::point_kind_type>;
} // namespace units
+194
View File
@@ -0,0 +1,194 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz, Conor Williams, Oliver Schonrock
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/hacks.h>
#include <array>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <numeric>
#include <tuple>
#include <type_traits>
#include <gsl/gsl-lite.hpp>
namespace units::detail {
template<typename T>
[[nodiscard]] constexpr T abs(T v) noexcept
{
return v < 0 ? -v : v;
}
// the following functions enable gcd and related computations on ratios
// with exponents. They avoid overflow. Further information here:
// https://github.com/mpusz/units/issues/62#issuecomment-588152833
// Computes (a * b) mod m relies on unsigned integer arithmetic, should not
// overflow
[[nodiscard]] constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m)
{
std::uint64_t res = 0;
if (b >= m) {
if (m > UINT64_MAX / 2u) {
b -= m;
} else {
b %= m;
}
}
while (a != 0) {
if (a & 1) {
if (b >= m - res) {
res -= m;
}
res += b;
}
a >>= 1;
std::uint64_t temp_b = b;
if (b >= m - b) {
temp_b -= m;
}
b += temp_b;
}
return res;
}
// Calculates (a ^ e) mod m , should not overflow.
[[nodiscard]] constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m)
{
a %= m;
std::uint64_t result = 1;
while (e > 0) {
if (e & 1) {
result = mulmod(result, a, m);
}
a = mulmod(a, a, m);
e >>= 1;
}
return result;
}
// gcd(a * 10 ^ e, b), should not overflow
[[nodiscard]] constexpr std::intmax_t gcdpow(std::intmax_t a, std::intmax_t e, std::intmax_t b) noexcept
{
assert(a > 0);
assert(e >= 0);
assert(b > 0);
// gcd(i, j) = gcd(j, i mod j) for j != 0 Euclid;
//
// gcd(a 10^e, b) = gcd(b, a 10^e mod b)
//
// (a 10^e) mod b -> [ (a mod b) (10^e mod b) ] mod b
return std::gcd(
b, static_cast<std::intmax_t>(mulmod(static_cast<std::uint64_t>(a % b),
modpow(10, static_cast<std::uint64_t>(e), static_cast<std::uint64_t>(b)),
static_cast<std::uint64_t>(b))));
}
constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs)
{
std::intmax_t tmp = lhs;
lhs = rhs;
rhs = tmp;
}
// Computes the rational gcd of n1/d1 x 10^e1 and n2/d2 x 10^e2
[[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, std::intmax_t n2, std::intmax_t d2,
std::intmax_t e2) noexcept
{
// Short cut for equal ratios
if (n1 == n2 && d1 == d2 && e1 == e2) {
return std::array{n1, d1, e1};
}
if (e2 > e1) {
detail::cwap(n1, n2);
detail::cwap(d1, d2);
detail::cwap(e1, e2);
}
std::intmax_t exp = e2; // minimum
// gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d
assert(std::numeric_limits<std::intmax_t>::max() / n1 > d2);
assert(std::numeric_limits<std::intmax_t>::max() / n2 > d1);
std::intmax_t num = detail::gcdpow(n1 * d2, e1 - e2, n2 * d1);
assert(std::numeric_limits<std::intmax_t>::max() / d1 > d2);
std::intmax_t den = d1 * d2;
std::intmax_t gcd = std::gcd(num, den);
return std::array{num / gcd, den / gcd, exp};
}
constexpr void normalize(std::intmax_t& num, std::intmax_t& den, std::intmax_t& exp)
{
if(num == 0) {
den = 1;
exp = 0;
return;
}
std::intmax_t gcd = std::gcd(num, den);
num = num * (den < 0 ? -1 : 1) / gcd;
den = detail::abs(den) / gcd;
while (num % 10 == 0) {
num /= 10;
++exp;
}
while (den % 10 == 0) {
den /= 10;
--exp;
}
}
[[nodiscard]] static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs)
{
constexpr std::intmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4);
const std::intmax_t a0 = detail::abs(lhs) % c;
const std::intmax_t a1 = detail::abs(lhs) / c;
const std::intmax_t b0 = detail::abs(rhs) % c;
const std::intmax_t b1 = detail::abs(rhs) / c;
gsl_Expects(a1 == 0 || b1 == 0); // overflow in multiplication
gsl_Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication
gsl_Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication
gsl_Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication
return lhs * rhs;
}
} // namespace units::detail
+99
View File
@@ -0,0 +1,99 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <gsl/gsl-lite.hpp>
#include <units/bits/constexpr_math.h>
#include <units/bits/math_concepts.h>
#include <units/bits/pow.h>
#include <units/bits/ratio_maths.h>
#include <cmath>
namespace units::detail {
template<std::intmax_t N, typename F>
requires gt_zero<N>
[[nodiscard]] constexpr std::intmax_t iroot_impl(std::intmax_t v, F const& pow_function) noexcept
{
if constexpr (N == 1) {
return v;
} else {
gsl_Expects(v >= 0);
if (v == 0) {
return 0;
}
constexpr double exponent = 1.0 / static_cast<double>(N);
const auto root = static_cast<std::intmax_t>(pow_function(static_cast<double>(v), exponent));
// integer roots may be truncated down by 1 or in even rarer cases up by 1 due to finite precision of pow and
// exponent, check both cases
if (v == pow_impl<N>(root + 1)) {
return root + 1;
}
if (v < pow_impl<N>(root)) {
return root - 1;
}
return root;
}
}
// maximum v is std::numeric_limits<std::intmax_t>::max() which is the worst case for exp convergence
// ExpOrder = 12 and Factor = 64 give a precision of about O(1e-15) for a wide range of 1 / N exponents
// Factor = 32 needs quite a few more terms to converge
// https://godbolt.org/z/odWq1o
template<std::intmax_t N, std::size_t ExpOrder = 12, std::intmax_t Factor = 64>
requires gt_zero<N>
[[nodiscard]] constexpr std::intmax_t iroot_compile(std::intmax_t v) noexcept
{
return iroot_impl<N>(v, [](double x, double exponent) { return constexpr_pow<ExpOrder, Factor>(x, exponent); });
}
template<std::intmax_t N>
requires gt_zero<N>
[[nodiscard]] std::intmax_t iroot_runtime(std::intmax_t v) noexcept
{
return iroot_impl<N>(v, [](double x, [[maybe_unused]] double exponent) {
if constexpr (N == 2) {
return std::sqrt(x);
} else if constexpr (N == 3) {
return std::cbrt(x);
} else {
return std::pow(x, exponent);
}
});
}
template<std::intmax_t N>
requires gt_zero<N>
[[nodiscard]] constexpr std::intmax_t iroot(std::intmax_t v) noexcept
{
// compile time version is much slower, use faster version at runtime
if (std::is_constant_evaluated()) {
return iroot_compile<N>(v);
}
return iroot_runtime<N>(v);
}
} // namespace units::detail
+180
View File
@@ -0,0 +1,180 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/deduced_symbol_text.h>
#include <units/bits/external/text_tools.h>
#include <units/prefix.h>
#include <units/derived_dimension.h>
namespace units::detail {
inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10");
template<ratio R>
constexpr auto ratio_text()
{
if constexpr(R.num == 1 && R.den == 1 && R.exp != 0) {
return base_multiplier + superscript<R.exp>();
}
else if constexpr(R.num != 1 || R.den != 1 || R.exp != 0) {
auto txt = basic_fixed_string("[") + regular<R.num>();
if constexpr(R.den == 1) {
if constexpr(R.exp == 0) {
return txt + basic_fixed_string("]");
}
else {
return txt + " " + base_multiplier + superscript<R.exp>() +
basic_fixed_string("]");
}
}
else {
if constexpr(R.exp == 0) {
return txt + basic_fixed_string("/") + regular<R.den>() +
basic_fixed_string("]");
}
else {
return txt + basic_fixed_string("/") + regular<R.den>() +
" " + base_multiplier + superscript<R.exp>() +
basic_fixed_string("]");
}
}
}
else {
return basic_fixed_string("");
}
}
template<ratio R, typename PrefixFamily, std::size_t SymbolLen>
constexpr auto prefix_or_ratio_text()
{
if constexpr(R.num == 1 && R.den == 1 && R.exp == 0) {
// no ratio/prefix
return basic_fixed_string("");
}
else {
if constexpr (!is_same_v<PrefixFamily, no_prefix>) {
// try to form a prefix
using prefix = downcast<detail::prefix_base<PrefixFamily, R>>;
if constexpr(!is_same_v<prefix, prefix_base<PrefixFamily, R>>) {
// print as a prefixed unit
return prefix::symbol;
}
else {
// print as a ratio of the coherent unit
constexpr auto txt = ratio_text<R>();
if constexpr(SymbolLen > 0 && txt.standard().size() > 0)
return txt + basic_fixed_string(" ");
else
return txt;
}
}
else {
// print as a ratio of the coherent unit
constexpr auto txt = ratio_text<R>();
if constexpr(SymbolLen > 0 && txt.standard().size() > 0)
return txt + basic_fixed_string(" ");
else
return txt;
}
}
}
template<typename... Es, std::size_t... Idxs>
constexpr auto derived_dimension_unit_text(exponent_list<Es...>, std::index_sequence<Idxs...>)
{
return (exp_text<Es, dimension_unit<typename Es::dimension>::symbol, negative_exp_count<Es...>, Idxs>() + ... + basic_symbol_text(basic_fixed_string("")));
}
template<typename... Es>
constexpr auto derived_dimension_unit_text(exponent_list<Es...> list)
{
return derived_dimension_unit_text(list, std::index_sequence_for<Es...>());
}
template<Exponent... Es>
constexpr auto exponent_list_with_named_units(exponent_list<Es...>);
template<Exponent Exp>
constexpr auto exponent_list_with_named_units(Exp)
{
using dim = TYPENAME Exp::dimension;
if constexpr(dimension_unit<dim>::is_named) {
return exponent_list<Exp>();
}
else {
using recipe = TYPENAME dim::recipe;
return exponent_list_with_named_units(recipe());
}
}
template<Exponent... Es>
constexpr auto exponent_list_with_named_units(exponent_list<Es...>)
{
return type_list_join<decltype(exponent_list_with_named_units(Es()))...>();
}
constexpr auto exponent_list_with_named_units(exponent_list<> empty)
{
return empty;
}
template<Dimension Dim>
constexpr auto derived_dimension_unit_text()
{
using recipe = TYPENAME Dim::recipe;
return derived_dimension_unit_text(exponent_list_with_named_units(recipe()));
}
template<typename T>
// TODO replace with `inline constexpr bool has_symbol` when MSVC cathes up
concept has_symbol = requires{ T::symbol; };
template<Dimension Dim, Unit U>
constexpr auto unit_text()
{
if constexpr(has_symbol<U>) {
// already has a symbol so print it
return U::symbol;
}
else {
// print as a prefix or ratio of a coherent unit
using coherent_unit = dimension_unit<Dim>;
if constexpr(has_symbol<coherent_unit>) {
// use predefined coherent unit symbol
constexpr auto symbol_text = coherent_unit::symbol;
constexpr auto prefix_txt = prefix_or_ratio_text<U::ratio / coherent_unit::ratio, typename U::reference::prefix_family, symbol_text.standard().size()>();
return prefix_txt + symbol_text;
}
else {
// use derived dimension ingredients to create a unit symbol
constexpr auto symbol_text = derived_dimension_unit_text<Dim>();
constexpr auto prefix_txt = prefix_or_ratio_text<U::ratio / coherent_unit::ratio, typename U::reference::prefix_family, symbol_text.standard().size()>();
return prefix_txt + symbol_text;
}
}
}
} // namespace units::detail
+50
View File
@@ -0,0 +1,50 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/customization_points.h>
#include <units/isq/si/time.h>
#include <chrono>
namespace units {
template<typename Rep, typename Period>
struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
using dimension = isq::si::dim_time;
using unit = downcast_unit<dimension, ratio(Period::num, Period::den)>;
using rep = Rep;
[[nodiscard]] static constexpr rep count(const std::chrono::duration<Rep, Period>& q) { return q.count(); }
};
template<typename C, typename Rep, typename Period>
struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::duration<Rep, Period>>> {
using dimension = isq::si::dim_time;
using unit = downcast_unit<dimension, ratio(Period::num, Period::den)>;
using rep = Rep;
[[nodiscard]] static constexpr auto relative(
const std::chrono::time_point<C, std::chrono::duration<Rep, Period>>& qp) {
return qp.time_since_epoch();
}
};
} // namespace units
+26
View File
@@ -0,0 +1,26 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/basic_concepts.h>
#include <units/bits/quantity_of.h>
@@ -0,0 +1,101 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/hacks.h>
#include <limits>
#include <type_traits>
namespace units {
/**
* @brief Specifies if a value of a type should be treated as a floating-point value
*
* This type trait should be specialized for a custom representation type to specify
* that values fo this type should be treated by the library as a floating-point ones
* which will enable implicit conversions between quantities.
*
* @tparam Rep a representation type for which a type trait is defined
*/
template<typename Rep>
inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
template<typename T>
requires requires { typename T::value_type; }
inline constexpr bool treat_as_floating_point<T> = treat_as_floating_point<typename T::value_type>;
/**
* @brief A type trait that defines zero, one, min, and max for a representation type
*
* The zero, one, min, and max member functions in units::quantity forward their work to
* these methods. This type can be specialized if the representation Rep requires a specific
* implementation to return these quantity objects.
*
* @tparam Rep a representation type for which a type trait is defined
*/
template<typename Rep>
struct quantity_values {
static constexpr Rep zero() noexcept
requires std::constructible_from<Rep, int>
{ return Rep(0); }
static constexpr Rep one() noexcept
requires std::constructible_from<Rep, int>
{ return Rep(1); }
static constexpr Rep min() noexcept
requires requires { { std::numeric_limits<Rep>::lowest() } -> std::same_as<Rep>; }
{ return std::numeric_limits<Rep>::lowest(); }
static constexpr Rep max() noexcept
requires requires { { std::numeric_limits<Rep>::max() } -> std::same_as<Rep>; }
{ return std::numeric_limits<Rep>::max(); }
};
/**
* @brief Provides support for external quantity-like types
*
* The type trait should provide the following nested type aliases: @c dimension, @c unit, @c rep,
* and a static member function @c count(T) that will return the raw value of the quantity.
*
* Usage example can be found in @c units/chrono.h header file.
*
* @tparam T the type to provide support for
*/
template<typename T>
struct quantity_like_traits;
/**
* @brief Provides support for external quantity point-like types
*
* The type trait should provide the following nested type aliases: @c dimension, @c unit, @c rep,
* and a static member function @c relative(T) that will return the quantity-like value of the quantity point.
*
* Usage example can be found in @c units/chrono.h header file.
*
* @tparam T the type to provide support for
*/
template<typename T>
struct quantity_point_like_traits;
} // namespace units
@@ -0,0 +1,87 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/base_dimension.h>
#include <units/bits/base_units_ratio.h>
#include <units/bits/derived_dimension_base.h>
#include <units/bits/dim_consolidate.h>
#include <units/bits/dim_unpack.h>
#include <units/bits/external/downcasting.h>
#include <units/bits/external/type_list.h>
#include <units/exponent.h>
namespace units {
namespace detail {
/**
* @brief Converts user provided derived dimension specification into a valid units::derived_dimension_base definition
*
* User provided definition of a derived dimension may contain the same base dimension repeated more than once on the
* list possibly hidden in other derived units provided by the user. The process here should:
* 1. Extract derived dimensions into exponents of base dimensions.
* 2. Sort the exponents so the same dimensions are placed next to each other.
* 3. Consolidate contiguous range of exponents of the same base dimensions to a one (or possibly zero) exponent for
* this base dimension.
*/
template<Exponent... Es>
using make_dimension = TYPENAME to_derived_dimension_base<typename dim_consolidate<type_list_sort<typename dim_unpack<Es...>::type, exponent_less>>::type>::type;
} // namespace detail
/**
* @brief The list of exponents of dimensions (both base and derived) provided by the user
*
* This is the user's interface to create derived dimensions. Exponents list can contain powers of factors of both
* base and derived dimensions. This is called a "recipe" of the dimension and among others is used to print
* unnamed coherent units of this dimension.
*
* Coherent unit is a unit that, for a given system of quantities and for a chosen set of base units, is a product
* of powers of base units with no other proportionality factor than one.
*
* The implementation is responsible for unpacking all of the dimensions into a list containing only base dimensions
* and their factors and putting them to derived_dimension_base class template.
*
* Sometimes units of equivalent quantities in different systems of units do not share the same reference so they
* cannot be easily converted to each other. An example can be a pressure for which a coherent unit in SI is pascal
* and in CGS barye. Those two units are not directly related with each other with some ratio. As they both are
* coherent units of their dimensions, the ratio between them is directly determined by the ratios of base units
* defined in base dimensions end their exponents in the derived dimension recipe. To provide interoperability of
* such quantities of different systems base_units_ratio is being used. The result of the division of two
* base_units_ratio of two quantities of equivalent dimensions in two different systems gives a ratio between their
* coherent units. Alternatively, the user would always have to directly define a barye in terms of pascal or vice
* versa.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam U a coherent unit of a derived dimension
* @tparam Es the list of exponents of ingredient dimensions
*/
template<typename Child, Unit U, Exponent... Es>
struct derived_dimension : downcast_dispatch<Child, typename detail::make_dimension<Es...>> {
using recipe = exponent_list<Es...>;
using coherent_unit = U;
static constexpr ratio base_units_ratio = detail::base_units_ratio(typename derived_dimension::exponents());
};
} // namespace units
+85
View File
@@ -0,0 +1,85 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/base_dimension.h>
#include <units/ratio.h>
namespace units {
/**
* @brief A power of factor corresponding to the dimension of a quantity
*
* @tparam Dim component dimension of a derived quantity
* @tparam Num numinator of the factor
* @tparam Den denominator of the factor
*/
template<Dimension Dim, std::intmax_t Num, std::intmax_t Den = 1>
struct exponent {
using dimension = Dim;
static constexpr std::intmax_t num = Num;
static constexpr std::intmax_t den = Den;
};
// is_exponent
namespace detail {
template<typename Dim, std::intmax_t Num, std::intmax_t Den>
inline constexpr bool is_exponent<exponent<Dim, Num, Den>> = true;
} // namespace detail
// exponent_less
template<Exponent E1, Exponent E2>
requires BaseDimension<typename E1::dimension> && BaseDimension<typename E2::dimension>
struct exponent_less : base_dimension_less<typename E1::dimension, typename E2::dimension> {};
// exponent_invert
namespace detail {
template<typename Dim, std::intmax_t Num, std::intmax_t Den>
constexpr exponent<Dim, -Num, Den> exponent_invert_impl(exponent<Dim, Num, Den>);
} // namespace detail
template<Exponent E>
using exponent_invert = decltype(detail::exponent_invert_impl(E()));
// exponent_multiply
namespace detail {
template<Exponent E, std::intmax_t Num, std::intmax_t Den>
struct exponent_multiply_impl {
static constexpr ratio r = ratio(E::num, E::den) * ratio(Num, Den);
using type = exponent<typename E::dimension, r.num, r.den>;
};
} // namespace detail
template<Exponent E, std::intmax_t Num, std::intmax_t Den>
using exponent_multiply = TYPENAME detail::exponent_multiply_impl<E, Num, Den>::type;
template<Exponent... Es>
struct exponent_list {};
} // namespace units
+510
View File
@@ -0,0 +1,510 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/customization_points.h>
#include <units/quantity.h>
#include <string_view>
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable:4702) // C4702: unreachable code
#endif //_MSC_VER
#include <fmt/format.h>
#include <fmt/locale.h>
#ifdef _MSC_VER
#pragma warning (pop)
#endif //_MSC_VER
// Grammar
//
// units-format-spec ::= [fill-and-align] [width] [units-specs]
// units-specs ::= conversion-spec
// units-specs conversion-spec
// units-specs literal-char
// literal-char ::= any character other than '{' or '}'
// conversion-spec ::= '%' units-type
// units-type ::= [units-rep-modifier] 'Q'
// [units-unit-modifier] 'q'
// one of "nt%"
// units-rep-modifier ::= [sign] [#] [precision] [L] [units-rep-type]
// units-rep-type ::= one of "aAbBdeEfFgGoxX"
// units-unit-modifier ::= 'A'
// Guide for editing
//
// If you want to add a new `units-type` terminal character (e.g. 'Q', 'q'):
// - If needed, write a new `specs` class (e.g. `global_format_specs`)
// - Add the new symbol in the `units_types` variable in the `parse_units_format` function
// - Add a new case in the `if` following the format_error in `parse_units_format` function;
// this should invoke `handler.on_[...]`
// - Edit `fmt::formatter`:
// - Add a new field for the flag/specs
// - Add to the `fmt::formatter::spec_handler` a `on_[...]` function that set the flag/specs if needed
// - Edit `units_formatter`:
// - Add a new field for the flag/specs
// - write a `on_[...]` function that writes to the `out` iterator the correct output
//
// If you want to add a new `units-rep-type`:
// - Add the new symbol in the `valid_rep_types` variable (which is in the
// fmt::formatter::spec_handler::on_type member function)
// NB: currently this function forward the modifier to the value that must be formatted;
// if the symbol has no meaning for fmt::formatter<Rep>, this behavior should be disabled manually
// (as is done for '\0')
// - Implement the effect of the new flag in `format_units_quantity_value`
//
// If you want to add a new `units-unit-modifier`:
// - Add the new symbol in the `valid_modifiers` variable (which is in the
// fmt::formatter::spec_handler::on_modifier member function)
// - Implement the effect of the new flag in the `units_formatter::on_quantity_unit` member function
namespace units {
namespace detail {
// Holds specs about the whole object
template <typename CharT>
struct global_format_specs
{
fmt::detail::fill_t<CharT> fill;
fmt::align_t align = fmt::align_t::none;
int width = 0;
};
// Holds specs about the representation (%[specs]Q)
struct rep_format_specs
{
fmt::sign_t sign = fmt::sign_t::none;
int precision = -1;
char type = '\0';
bool alt = false;
bool use_locale = false;
};
// Holds specs about the unit (%[specs]q)
struct unit_format_specs
{
char modifier = '\0';
};
// Parse a `units-rep-modifier`
template <typename CharT, typename Handler>
constexpr const CharT* parse_units_rep(const CharT* begin, const CharT* end, Handler&& handler, bool treat_as_floating_point)
{
// parse sign
switch(static_cast<char>(*begin)) {
case '+':
handler.on_plus();
++begin;
break;
case '-':
handler.on_minus();
++begin;
break;
case ' ':
handler.on_space();
++begin;
break;
}
if(begin == end)
return begin;
// parse #
if (*begin == '#') {
handler.on_alt();
if (++begin == end) return begin;
}
// parse precision if a floating point
if(*begin == '.') {
if (treat_as_floating_point)
begin = fmt::detail::parse_precision(begin, end, handler);
else
handler.on_error("precision not allowed for integral quantity representation");
}
// parse L to enable the locale-specific form
if (*begin == 'L') {
handler.on_locale();
++begin;
}
if(begin != end && *begin != '}' && *begin != '%') {
handler.on_type(*begin++);
}
return begin;
}
// parse units-specs
template<typename CharT, typename Handler>
constexpr const CharT* parse_units_format(const CharT* begin, const CharT* end, Handler&& handler)
{
auto ptr = begin;
while(ptr != end) {
auto c = *ptr;
if(c == '}')
break;
if(c != '%') {
++ptr;
continue;
}
if(begin != ptr)
handler.on_text(begin, ptr);
begin = ++ptr; // consume '%'
if(ptr == end)
throw fmt::format_error("invalid format");
c = *ptr++;
switch(c) {
// units-type
case '%':
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
handler.on_text(tab, tab + 1);
break;
}
default:
constexpr auto units_types = std::string_view{"Qq"};
auto const new_end = std::find_first_of(begin, end, units_types.begin(), units_types.end());
if (new_end == end) {
throw fmt::format_error("invalid format");
}
if (*new_end == 'Q') {
handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers
} else {
handler.on_quantity_unit(*begin); // Edit `on_quantity_unit` to add an unit modifier
}
ptr = new_end + 1;
}
begin = ptr;
}
if(begin != ptr)
handler.on_text(begin, ptr);
return ptr;
}
// build the 'representation' as requested in the format string, applying only units-rep-modifiers
template<typename CharT, typename Rep, typename OutputIt, typename LocaleRef>
inline OutputIt format_units_quantity_value(OutputIt out, const Rep& val, const rep_format_specs& rep_specs, LocaleRef loc)
{
fmt::basic_memory_buffer<CharT> buffer;
auto to_buffer = std::back_inserter(buffer);
fmt::format_to(to_buffer, "{{:");
switch(rep_specs.sign) {
case fmt::sign::none:
break;
case fmt::sign::plus:
format_to(to_buffer, "+");
break;
case fmt::sign::minus:
format_to(to_buffer, "-");
break;
case fmt::sign::space:
format_to(to_buffer, " ");
break;
}
if (rep_specs.alt) {
format_to(to_buffer, "#");
}
auto type = rep_specs.type;
if (auto precision = rep_specs.precision; precision >= 0) {
format_to(to_buffer, ".{}{}", precision, type == '\0' ? 'f' : type);
} else if constexpr (treat_as_floating_point<Rep>) {
format_to(to_buffer, "{}", type == '\0' ? 'g' : type);
} else {
if (type != '\0') {
format_to(to_buffer, "{}", type);
}
}
if (rep_specs.use_locale) {
format_to(to_buffer, "L");
}
fmt::format_to(to_buffer, "}}");
if (rep_specs.use_locale and static_cast<bool>(loc)) {
return format_to(out, loc.template get<std::locale>(), fmt::to_string(buffer), val);
}
return format_to(out, fmt::to_string(buffer), val);
}
template<typename OutputIt, typename Dimension, typename Unit, typename Rep, typename LocaleRef, typename CharT>
struct units_formatter {
OutputIt out;
Rep val;
global_format_specs<CharT> const & global_specs;
rep_format_specs const & rep_specs;
unit_format_specs const & unit_specs;
LocaleRef loc;
explicit units_formatter(
OutputIt o, quantity<Dimension, Unit, Rep> q,
global_format_specs<CharT> const & gspecs,
rep_format_specs const & rspecs, unit_format_specs const & uspecs,
LocaleRef lc
):
out(o), val(q.count()), global_specs(gspecs), rep_specs(rspecs), unit_specs(uspecs), loc(lc)
{
}
template<typename CharT2>
void on_text(const CharT2* begin, const CharT2* end)
{
std::copy(begin, end, out);
}
void on_quantity_value([[maybe_unused]] const CharT*, [[maybe_unused]] const CharT*)
{
out = format_units_quantity_value<CharT>(out, val, rep_specs, loc);
}
void on_quantity_unit([[maybe_unused]] const CharT)
{
auto txt = unit_text<Dimension, Unit>();
if(unit_specs.modifier == 'A') {
format_to(out, "{}", txt.ascii().c_str());
}
else {
format_to(out, "{}", txt.standard().c_str());
}
}
};
} // namespace detail
} // namespace units
template<typename Dimension, typename Unit, typename Rep, typename CharT>
struct fmt::formatter<units::quantity<Dimension, Unit, Rep>, CharT> {
private:
using quantity = units::quantity<Dimension, Unit, Rep>;
using iterator = TYPENAME fmt::basic_format_parse_context<CharT>::iterator;
using arg_ref_type = fmt::detail::arg_ref<CharT>;
units::detail::global_format_specs<CharT> global_specs;
units::detail::rep_format_specs rep_specs;
units::detail::unit_format_specs unit_specs;
bool quantity_value = false;
bool quantity_unit = false;
bool quantity_unit_ascii_only = false;
arg_ref_type width_ref;
arg_ref_type precision_ref;
fmt::basic_string_view<CharT> format_str;
struct spec_handler {
formatter& f;
fmt::basic_format_parse_context<CharT>& context;
fmt::basic_string_view<CharT> format_str;
template<typename Id>
constexpr arg_ref_type make_arg_ref(Id arg_id)
{
context.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
constexpr arg_ref_type make_arg_ref(fmt::basic_string_view<CharT> arg_id)
{
context.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
constexpr arg_ref_type make_arg_ref(fmt::detail::auto_id)
{
return arg_ref_type(context.next_arg_id());
}
void on_error(const char* msg) { throw fmt::format_error(msg); }
constexpr void on_fill(basic_string_view<CharT> fill) { f.global_specs.fill = fill; } // global
constexpr void on_align(align_t align) { f.global_specs.align = align; } // global
constexpr void on_width(int width) { f.global_specs.width = width; } // global
constexpr void on_plus() { f.rep_specs.sign = fmt::sign::plus; } // rep
constexpr void on_minus() { f.rep_specs.sign = fmt::sign::minus; } // rep
constexpr void on_space() { f.rep_specs.sign = fmt::sign::space; } // rep
constexpr void on_alt() { f.rep_specs.alt = true; } // rep
constexpr void on_precision(int precision) { f.rep_specs.precision = precision; } // rep
constexpr void on_locale() { f.rep_specs.use_locale = true; } // rep
constexpr void on_type(char type) // rep
{
constexpr auto valid_rep_types = std::string_view{"aAbBdeEfFgGoxX"};
if (valid_rep_types.find(type) != std::string_view::npos) {
f.rep_specs.type = type;
} else {
on_error("invalid quantity type specifier");
}
}
constexpr void on_modifier(char mod) {
constexpr auto valid_modifiers = std::string_view{"A"};
if (valid_modifiers.find(mod) != std::string_view::npos) {
f.unit_specs.modifier = mod;
} else {
on_error("invalid unit modifier specified");
}
} // unit
constexpr void end_precision() {}
template<typename Id>
constexpr void on_dynamic_width(Id arg_id)
{
f.width_ref = make_arg_ref(arg_id);
}
template<typename Id>
constexpr void on_dynamic_precision(Id arg_id)
{
f.precision_ref = make_arg_ref(arg_id);
}
constexpr void on_text(const CharT*, const CharT*) {}
constexpr void on_quantity_value(const CharT* begin, const CharT* end)
{
if (begin != end) {
units::detail::parse_units_rep(begin, end, *this, units::treat_as_floating_point<Rep>);
}
f.quantity_value = true;
}
constexpr void on_quantity_unit(const CharT mod)
{
if (mod != 'q') {
f.unit_specs.modifier = mod;
}
f.quantity_unit = true;
}
};
struct parse_range {
iterator begin;
iterator end;
};
constexpr parse_range do_parse(fmt::basic_format_parse_context<CharT>& ctx)
{
auto begin = ctx.begin(), end = ctx.end();
if(begin == end || *begin == '}')
return {begin, begin};
// handler to assign parsed data to formatter data members
spec_handler handler{*this, ctx, format_str};
// parse alignment
begin = fmt::detail::parse_align(begin, end, handler);
if(begin == end)
return {begin, begin};
// parse width
begin = fmt::detail::parse_width(begin, end, handler);
if(begin == end)
return {begin, begin};
// parse units-specific specification
end = units::detail::parse_units_format(begin, end, handler);
if(global_specs.align == fmt::align_t::none && (!quantity_unit || quantity_value))
// quantity values should behave like numbers (by default aligned to right)
global_specs.align = fmt::align_t::right;
return {begin, end};
}
public:
constexpr auto parse(fmt::basic_format_parse_context<CharT>& ctx)
{
auto range = do_parse(ctx);
format_str = fmt::basic_string_view<CharT>(&*range.begin, fmt::detail::to_unsigned(range.end - range.begin));
return range.end;
}
template<typename FormatContext>
auto format(const units::quantity<Dimension, Unit, Rep>& q, FormatContext& ctx)
{
auto begin = format_str.begin(), end = format_str.end();
// process dynamic width and precision
fmt::detail::handle_dynamic_spec<fmt::detail::width_checker>(global_specs.width, width_ref, ctx);
fmt::detail::handle_dynamic_spec<fmt::detail::precision_checker>(rep_specs.precision, precision_ref, ctx);
// In `global_format_buffer` we will create a global format string
// e.g. "{:*^10%.1Q_%q}, 1.23_q_m" => "{:*^10}"
fmt::basic_memory_buffer<CharT> global_format_buffer;
auto to_gfb = std::back_inserter(global_format_buffer);
format_to(to_gfb, "{{:");
if (global_specs.fill.size() != 1 || global_specs.fill[0] != ' ') {
format_to(to_gfb, "{}", global_specs.fill.data());
}
if (auto align = global_specs.align; align != fmt::align_t::none) {
switch (align) {
case fmt::align_t::left:
format_to(to_gfb, "<");
break;
case fmt::align_t::right:
format_to(to_gfb, ">");
break;
case fmt::align_t::center:
format_to(to_gfb, "^");
break;
default:
break;
}
}
if (auto width = global_specs.width; width >= 1) {
format_to(to_gfb, "{}", width);
}
format_to(to_gfb, "}}");
// In `quantity_buffer` we will have the representation and the unit formatted according to their
// specification, ignoring global specifiers
// e.g. "{:*^10%.1Q_%q}, 1.23_q_m" => "1.2_m"
// TODO Avoid extra copying if width is not specified
fmt::basic_memory_buffer<CharT> quantity_buffer;
auto to_quantity_buffer = std::back_inserter(quantity_buffer);
// deal with quantity content
if(begin == end || *begin == '}') {
// default format should print value followed by the unit separated with 1 space
to_quantity_buffer = units::detail::format_units_quantity_value<CharT>(to_quantity_buffer, q.count(), rep_specs, ctx.locale());
constexpr auto symbol = units::detail::unit_text<Dimension, Unit>();
if(symbol.standard().size()) {
*to_quantity_buffer++ = CharT(' ');
format_to(to_quantity_buffer, "{}", symbol.standard().c_str());
}
}
else {
// user provided format
units::detail::units_formatter f(to_quantity_buffer, q, global_specs, rep_specs, unit_specs, ctx.locale());
parse_units_format(begin, end, f);
}
// Format the `quantity buffer` using specs from `global_format_buffer`
// In the example, equivalent to fmt::format("{:*^10}", "1.2_m")
return format_to(ctx.out(), fmt::to_string(global_format_buffer), fmt::to_string(quantity_buffer));
}
};
+50
View File
@@ -0,0 +1,50 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/isq/si/prefixes.h>
#include <units/quantity.h>
namespace units {
struct radian : named_unit<radian, "rad", isq::si::prefix> {};
template<Unit U = radian>
struct dim_angle : base_dimension<"A", U> {};
template<typename T>
concept Angle = QuantityOfT<T, dim_angle>;
template<UnitOf<dim_angle<>> U, QuantityValue Rep = double>
using angle = quantity<dim_angle<>, U, Rep>;
inline namespace literals {
// rad
constexpr auto operator"" _q_rad(unsigned long long l) { gsl_ExpectsAudit(std::in_range<std::int64_t>(l)); return angle<radian, std::int64_t>(static_cast<std::int64_t>(l)); }
constexpr auto operator"" _q_rad(long double l) { return angle<radian, long double>(l); }
} // namespace literals
} // namespace units
@@ -0,0 +1,46 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/quantity_cast.h>
namespace units {
struct one : named_unit<one, "", no_prefix> {};
struct percent : named_scaled_unit<percent, "%", no_prefix, ratio(1, 100), one> {};
/**
* @brief Dimension one
*
* Dimension for which all the exponents of the factors corresponding to the base
* dimensions are zero. Also commonly named as "dimensionless".
*/
struct dim_one : derived_dimension<dim_one, one> {};
template<typename T>
concept Dimensionless = QuantityOf<T, dim_one>;
template<UnitOf<dim_one> U, QuantityValue Rep = double>
using dimensionless = quantity<dim_one, U, Rep>;
} // namespace units
+64
View File
@@ -0,0 +1,64 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/basic_concepts.h>
#include <units/bits/external/downcasting.h>
namespace units {
namespace detail {
template<typename K, Dimension D>
struct _kind_base : downcast_base<_kind_base<K, D>> {
using base_kind = K;
using dimension = D;
};
template<Kind K>
struct _point_kind_base : downcast_base<_point_kind_base<K>> {
using base_kind = K;
using dimension = typename K::dimension;
};
} // namespace detail
template<Kind K, Dimension D>
requires Kind<downcast<detail::_kind_base<typename K::base_kind, D>>>
using downcast_kind = downcast<detail::_kind_base<typename K::base_kind, D>>;
template<Kind K>
requires PointKind<downcast<detail::_point_kind_base<K>>>
using downcast_point_kind = downcast<detail::_point_kind_base<K>>;
template<typename K, Dimension D>
struct kind : downcast_dispatch<K, detail::_kind_base<K, D>> {};
template<typename DK, Kind BK, Dimension D>
requires std::same_as<BK, typename BK::base_kind>
struct derived_kind : downcast_dispatch<DK, detail::_kind_base<BK, D>> {};
template<typename DPK, Kind BK>
struct point_kind : downcast_dispatch<DPK, detail::_point_kind_base<BK>> {};
} // namespace units
+135
View File
@@ -0,0 +1,135 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/concepts.h>
#include <units/quantity.h>
#include <cmath>
#include <limits>
namespace units {
/**
* @brief Computes the value of a quantity raised to the power `N`
*
* Both the quantity value and its dimension are the base of the operation.
*
* @tparam Num Exponent numerator
* @tparam Den Exponent denominator
* @param q Quantity being the base of the operation
* @return Quantity The result of computation
*/
template<std::intmax_t Num, std::intmax_t Den = 1, Quantity Q>
requires detail::non_zero<Den>
[[nodiscard]] inline auto pow(const Q& q) noexcept requires requires { std::pow(q.count(), 1.0); }
{
using rep = TYPENAME Q::rep;
if constexpr (Num == 0) {
return rep(1);
} else {
using dim = dimension_pow<typename Q::dimension, Num, Den>;
using unit = downcast_unit<dim, pow<Num, Den>(Q::unit::ratio)>;
return quantity<dim, unit, rep>(
static_cast<rep>(std::pow(q.count(), static_cast<double>(Num) / static_cast<double>(Den))));
}
}
/**
* @brief Computes the square root of a quantity
*
* Both the quantity value and its dimension are the base of the operation.
*
* @param q Quantity being the base of the operation
* @return Quantity The result of computation
*/
template<Quantity Q>
[[nodiscard]] inline Quantity auto sqrt(const Q& q) noexcept
requires requires { std::sqrt(q.count()); }
{
using dim = dimension_pow<typename Q::dimension, 1, 2>;
using unit = downcast_unit<dim, sqrt(Q::unit::ratio)>;
using rep = TYPENAME Q::rep;
return quantity<dim, unit, rep>(static_cast<rep>(std::sqrt(q.count())));
}
/**
* @brief Computes the cubic root of a quantity
*
* Both the quantity value and its dimension are the base of the operation.
*
* @param q Quantity being the base of the operation
* @return Quantity The result of computation
*/
template<Quantity Q>
[[nodiscard]] inline Quantity auto cbrt(const Q& q) noexcept
requires requires { std::cbrt(q.count()); }
{
using dim = dimension_pow<typename Q::dimension, 1, 3>;
using unit = downcast_unit<dim, cbrt(Q::unit::ratio)>;
using rep = TYPENAME Q::rep;
return quantity<dim, unit, rep>(static_cast<rep>(std::cbrt(q.count())));
}
/**
* @brief Computes Euler's raised to the given power
*
* @note Such an operation has sense only for a dimensionless quantity.
*
* @param q Quantity being the base of the operation
* @return Quantity The value of the same quantity type
*/
template<typename U, typename Rep>
[[nodiscard]] inline dimensionless<U, Rep> exp(const dimensionless<U, Rep>& q)
{
return quantity_cast<U>(dimensionless<one, Rep>(std::exp(quantity_cast<one>(q).count())));
}
/**
* @brief Computes the absolute value of a quantity
*
* @param q Quantity being the base of the operation
* @return Quantity The absolute value of a provided quantity
*/
template<typename D, typename U, typename Rep>
[[nodiscard]] inline quantity<D, U, Rep> abs(const quantity<D, U, Rep>& q) noexcept
requires requires { std::abs(q.count()); }
{
return quantity<D, U, Rep>(std::abs(q.count()));
}
/**
* @brief Returns the epsilon of the quantity
*
* The returned value is defined by a <tt>std::numeric_limits<typename Q::rep>::epsilon()</tt>.
*
* @tparam Q Quantity type being the base of the operation
* @return Quantity The epsilon value for quantity's representation type
*/
template<Quantity Q>
requires requires { std::numeric_limits<typename Q::rep>::epsilon(); }
[[nodiscard]] constexpr Quantity auto epsilon() noexcept
{
return Q(std::numeric_limits<typename Q::rep>::epsilon());
}
} // namespace units
+124
View File
@@ -0,0 +1,124 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/basic_concepts.h>
#include <units/customization_points.h>
#include <type_traits>
namespace units {
struct invalid_one_rep {};
/**
* @brief A representation type to be used for unit constants
*
* This representation type is intended to be used in the unit constants definition:
*
* @code{.cpp}
* namespace unit_constants {
*
* inline constexpr auto m = length<metre, one_rep>{};
* inline constexpr auto km = length<kilometre, one_rep>{};
*
* }
* @endcode
*
* Unit constants simplify quantity creation:
*
* @code{.cpp}
* using namespace units::isq::si::unit_constants;
*
* auto d = 123 * m;
* auto v = 70 * km / h;
* @endcode
*
* Also, it is allowed to define custom unit constants from existing ones:
*
* @code{.cpp}
* constexpr auto Nm = N * m;
* constexpr auto mph = mi / h;
* @endcode
*
* `km * 3` or `s / 4` syntax is not allowed for quantity creation.
*/
struct one_rep {
[[nodiscard]] friend constexpr one_rep operator*(one_rep, one_rep) { return {}; }
[[nodiscard]] friend constexpr one_rep operator/(one_rep, one_rep) { return {}; }
template<QuantityValue Rep>
[[nodiscard]] friend constexpr Rep operator*(const Rep& lhs, one_rep)
{
return lhs;
}
template<QuantityValue Rep>
[[nodiscard]] friend constexpr Rep operator/(const Rep& lhs, one_rep)
{
return lhs;
}
template<QuantityValue Rep>
[[nodiscard]] friend constexpr Rep operator*(one_rep, const Rep&) = delete;
template<QuantityValue Rep>
[[nodiscard]] friend constexpr Rep operator/(one_rep, const Rep&) = delete;
template<typename Rep>
requires requires { quantity_values<Rep>::one(); } &&
(!Quantity<Rep> && !QuantityLike<Rep> && !wrapped_quantity_<Rep>) // TODO: Replace with `QuantityValue`
[[nodiscard]] constexpr operator Rep() const noexcept // when Clang catches up.
{
return quantity_values<Rep>::one();
}
[[nodiscard]] bool operator==(const one_rep&) const = default;
[[nodiscard]] auto operator<=>(const one_rep&) const = default;
[[nodiscard]] constexpr bool operator==(const invalid_one_rep&) const { return false; }
};
template<>
struct quantity_values<one_rep> {
static constexpr invalid_one_rep zero() noexcept { return invalid_one_rep{}; }
static constexpr one_rep one() noexcept { return one_rep{}; }
static constexpr one_rep min() noexcept { return one(); }
static constexpr one_rep max() noexcept { return one(); }
};
} // namespace units
namespace std {
template<>
struct common_type<units::one_rep, units::one_rep> {
using type = units::one_rep;
};
template<units::QuantityValue Rep>
struct common_type<units::one_rep, Rep> {
using type = Rep;
};
template<units::QuantityValue Rep>
struct common_type<Rep, units::one_rep> {
using type = Rep;
};
} // namespace std
+78
View File
@@ -0,0 +1,78 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/downcasting.h>
#include <units/bits/basic_concepts.h>
#include <units/ratio.h>
#include <units/symbol_text.h>
namespace units {
/**
* @brief The base for all prefix families
*
* Every prefix family should inherit from this type to satisfy PrefixFamily concept.
*/
struct prefix_family {};
/**
* @brief No prefix possible for the unit
*
* This is a special prefix type tag specifying that the unit can not be scaled with any kind
* of the prefix.
*/
struct no_prefix : prefix_family {};
namespace detail {
template<PrefixFamily PF, ratio R>
struct prefix_base : downcast_base<prefix_base<PF, R>> {
using prefix_family = PF;
static constexpr ::units::ratio ratio = R;
};
} // namespace detail
/**
* @brief A prefix used to scale units
*
* Data from a prefix class is used in two cases:
* - when defining a prefixed_unit its ratio is used to scale the reference unit and its
* symbol is used to prepend to the symbol of referenced unit
* - when printing the symbol of a scaled unit that was not predefined by the user but its
* factor matches ratio of a prefix from the specified prefix family, its symbol will be
* prepended to the symbol of the unit
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam PF a type of prefix family
* @tparam Symbol a text representation of the prefix
* @tparam R factor to be used to scale a unit
*/
template<typename Child, PrefixFamily PF, basic_symbol_text Symbol, ratio R>
requires (!std::same_as<PF, no_prefix>)
struct prefix : downcast_dispatch<Child, detail::prefix_base<PF, R>, downcast_mode::on> {
static constexpr auto symbol = Symbol;
};
} // namespace units
+461
View File
@@ -0,0 +1,461 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/common_quantity.h>
#include <units/bits/dimension_op.h>
#include <units/bits/pow.h>
#include <units/bits/unit_text.h>
#include <units/generic/dimensionless.h>
#include <units/quantity_cast.h>
#include <compare>
namespace units {
template<typename T>
concept floating_point_ = // exposition only
(Quantity<T> && treat_as_floating_point<typename T::rep>) ||
(!Quantity<T> && treat_as_floating_point<T>);
template<typename From, typename To>
concept safe_convertible_to_ = // exposition only
(!Quantity<From>) &&
(!Quantity<To>) &&
std::convertible_to<From, To> &&
(floating_point_<To> || (!floating_point_<From>));
// QFrom ratio is an exact multiple of QTo
template<typename QFrom, typename QTo>
concept harmonic_ = // exposition only
Quantity<QFrom> &&
Quantity<QTo> &&
is_integral(detail::quantity_ratio<QFrom> / detail::quantity_ratio<QTo>);
template<typename QFrom, typename QTo>
concept safe_castable_to_ = // exposition only
Quantity<QFrom> &&
QuantityOf<QTo, typename QFrom::dimension> &&
scalable_with_<typename QFrom::rep, typename QTo::rep> &&
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
template<typename Func, typename T, typename U>
concept quantity_value_for_ =
std::regular_invocable<Func, T, U> &&
QuantityValue<std::invoke_result_t<Func, T, U>>;
template<typename T, typename Func, typename U, typename V>
concept invoke_result_convertible_to_ =
QuantityValue<T> &&
quantity_value_for_<Func, U, V> &&
safe_convertible_to_<T, std::invoke_result_t<Func, U, V>>;
template<typename Func, typename Q, typename V>
concept have_quantity_for_ =
Quantity<Q> &&
(!Quantity<V>) &&
quantity_value_for_<Func, typename Q::rep, V>;
template<typename Func, Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires quantity_value_for_<Func, typename Q1::rep, typename Q2::rep>
using common_quantity_for = common_quantity<Q1, Q2, std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
template<QuantityLike Q>
using quantity_like_type = quantity<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit, typename quantity_like_traits<Q>::rep>;
/**
* @brief A quantity
*
* Property of a phenomenon, body, or substance, where the property has a magnitude that can be
* expressed by means of a number and a measurement unit.
*
* @tparam D a dimension of the quantity (can be either a BaseDimension or a DerivedDimension)
* @tparam U a measurement unit of the quantity
* @tparam Rep a type to be used to represent values of a quantity
*/
template<Dimension D, UnitOf<D> U, QuantityValue Rep = double>
class quantity {
Rep value_;
public:
// member types
using dimension = D;
using unit = U;
using rep = Rep;
// static member functions
[[nodiscard]] static constexpr quantity zero() noexcept
requires requires { quantity_values<rep>::zero(); }
{
return quantity(quantity_values<rep>::zero());
}
[[nodiscard]] static constexpr quantity one() noexcept
requires requires { quantity_values<rep>::one(); }
{
return quantity(quantity_values<rep>::one());
}
[[nodiscard]] static constexpr quantity min() noexcept
requires requires { quantity_values<rep>::min(); }
{
return quantity(quantity_values<rep>::min());
}
[[nodiscard]] static constexpr quantity max() noexcept
requires requires { quantity_values<rep>::max(); }
{
return quantity(quantity_values<rep>::max());
}
// construction, assignment, destruction
quantity() = default;
quantity(const quantity&) = default;
quantity(quantity&&) = default;
template<safe_convertible_to_<rep> Value>
constexpr explicit(!(is_same_v<dimension, dim_one> && is_same_v<unit, ::units::one>))
quantity(const Value& v) : value_(v) {}
template<safe_castable_to_<quantity> Q>
constexpr explicit(false) quantity(const Q& q) : value_(quantity_cast<quantity>(q).count()) {}
template<QuantityLike Q>
requires safe_castable_to_<quantity_like_type<Q>, quantity>
constexpr explicit quantity(const Q& q) : quantity(quantity_like_type<Q>(quantity_like_traits<Q>::count(q))) {}
quantity& operator=(const quantity&) = default;
quantity& operator=(quantity&&) = default;
// data access
[[nodiscard]] constexpr rep count() const noexcept { return value_; }
// member unary operators
[[nodiscard]] constexpr Quantity auto operator+() const
requires requires(rep v) { { +v } -> std::common_with<rep>; }
{
using ret = quantity<D, U, decltype(+count())>;
return ret(+count());
}
[[nodiscard]] constexpr Quantity auto operator-() const
requires std::regular_invocable<std::negate<>, rep>
{
using ret = quantity<D, U, decltype(-count())>;
return ret(-count());
}
constexpr quantity& operator++()
requires requires(rep v) { { ++v } -> std::same_as<rep&>; }
{
++value_;
return *this;
}
[[nodiscard]] constexpr quantity operator++(int)
requires requires(rep v) { { v++ } -> std::same_as<rep>; }
{
return quantity(value_++);
}
constexpr quantity& operator--()
requires requires(rep v) { { --v } -> std::same_as<rep&>; }
{
--value_;
return *this;
}
[[nodiscard]] constexpr quantity operator--(int)
requires requires(rep v) { { v-- } -> std::same_as<rep>; }
{
return quantity(value_--);
}
constexpr quantity& operator+=(const quantity& q)
requires requires(rep a, rep b) { { a += b } -> std::same_as<rep&>; }
{
value_ += q.count();
return *this;
}
constexpr quantity& operator-=(const quantity& q)
requires requires(rep a, rep b) { { a -= b } -> std::same_as<rep&>; }
{
value_ -= q.count();
return *this;
}
template<typename Rep2>
constexpr quantity& operator*=(const Rep2& rhs)
requires requires(rep a, const Rep2 b) { { a *= b } -> std::same_as<rep&>; }
{
value_ *= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity& operator*=(const dimensionless<units::one, Rep2>& rhs)
requires requires(rep a, const Rep2 b) { { a *= b } -> std::same_as<rep&>; }
{
value_ *= rhs.count();
return *this;
}
template<typename Rep2>
constexpr quantity& operator/=(const Rep2& rhs)
requires requires(rep a, const Rep2 b) { { a /= b } -> std::same_as<rep&>; }
{
gsl_ExpectsAudit(rhs != quantity_values<Rep2>::zero());
value_ /= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity& operator/=(const dimensionless<units::one, Rep2>& rhs)
requires requires(rep a, const Rep2 b) { { a /= b } -> std::same_as<rep&>; }
{
gsl_ExpectsAudit(rhs.count() != quantity_values<Rep2>::zero());
value_ /= rhs.count();
return *this;
}
template<typename Rep2>
constexpr quantity& operator%=(const Rep2& rhs)
requires (!floating_point_<rep>) && (!floating_point_<Rep2>) &&
requires(rep a, const Rep2 b) { { a %= b } -> std::same_as<rep&>; }
{
gsl_ExpectsAudit(rhs != quantity_values<Rep2>::zero());
value_ %= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity& operator%=(const dimensionless<units::one, Rep2>& rhs)
requires (!floating_point_<rep>) && (!floating_point_<Rep2>) &&
requires(rep a, const Rep2 b) { { a %= b } -> std::same_as<rep&>; }
{
gsl_ExpectsAudit(rhs.count() != quantity_values<Rep2>::zero());
value_ %= rhs.count();
return *this;
}
constexpr quantity& operator%=(const quantity& q)
requires (!floating_point_<rep>) &&
requires(rep a, rep b) { { a %= b } -> std::same_as<rep&>; }
{
gsl_ExpectsAudit(q.count() != quantity_values<rep>::zero());
value_ %= q.count();
return *this;
}
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const Value& rhs)
requires requires { requires !Quantity<Value>; requires is_same_v<unit, units::one>; // TODO: Simplify
requires invoke_result_convertible_to_<rep, std::plus<>, rep, Value>; } // when Clang catches up.
{
return units::quantity(lhs.count() + rhs);
}
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator+(const Value& lhs, const quantity& rhs)
requires requires { requires !Quantity<Value>; requires is_same_v<unit, units::one>; // TODO: Simplify
requires invoke_result_convertible_to_<rep, std::plus<>, Value, rep>; } // when Clang catches up.
{
return units::quantity(lhs + rhs.count());
}
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const Value& rhs)
requires requires { requires !Quantity<Value>; requires is_same_v<unit, units::one>; // TODO: Simplify
requires invoke_result_convertible_to_<rep, std::minus<>, rep, Value>; } // when Clang catches up.
{
return units::quantity(lhs.count() - rhs);
}
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator-(const Value& lhs, const quantity& rhs)
requires requires { requires !Quantity<Value>; requires is_same_v<unit, units::one>; // TODO: Simplify
requires invoke_result_convertible_to_<rep, std::minus<>, Value, rep>; } // when Clang catches up.
{
return units::quantity(lhs - rhs.count());
}
template<typename Value>
requires (!Quantity<Value>) &&
invoke_result_convertible_to_<rep, std::multiplies<>, rep, const Value&>
[[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v)
{
using ret = quantity<D, U, std::invoke_result_t<std::multiplies<>, rep, Value>>;
return ret(q.count() * v);
}
template<typename Value>
requires (!Quantity<Value>) &&
invoke_result_convertible_to_<rep, std::multiplies<>, const Value&, rep>
[[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q)
{
using ret = quantity<D, U, std::invoke_result_t<std::multiplies<>, Value, rep>>;
return ret(v * q.count());
}
template<typename Value>
requires (!Quantity<Value>) &&
invoke_result_convertible_to_<rep, std::divides<>, rep, const Value&>
[[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
using ret = quantity<D, U, std::invoke_result_t<std::divides<>, rep, Value>>;
return ret(q.count() / v);
}
template<typename Value>
requires (!Quantity<Value>) &&
invoke_result_convertible_to_<rep, std::divides<>, const Value&, rep>
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{
gsl_ExpectsAudit(q.count() != quantity_values<rep>::zero());
using dim = dim_invert<D>;
using ret_unit = downcast_unit<dim, inverse(U::ratio)>;
using ret = quantity<dim, ret_unit, std::invoke_result_t<std::divides<>, Value, rep>>;
return ret(v / q.count());
}
template<typename Value>
requires (!Quantity<Value>) && (!floating_point_<rep>) && (!floating_point_<Value>) &&
invoke_result_convertible_to_<rep, std::modulus<>, rep, const Value&>
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
using ret = quantity<D, U, std::invoke_result_t<std::modulus<>, rep, Value>>;
return ret(q.count() % v);
}
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs)
requires (!floating_point_<rep>) && is_same_v<unit, units::one> &&
invoke_result_convertible_to_<rep, std::modulus<>, rep, rep>
{
gsl_ExpectsAudit(rhs.count() != quantity_values<rep>::zero());
return units::quantity(lhs.count() % rhs.count());
}
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs)
requires std::three_way_comparable<rep>
#if UNITS_COMP_GCC == 10 && UNITS_COMP_GCC_MINOR >= 2
= default;
#else
{
return lhs.count() <=> rhs.count();
}
#endif
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) = default;
};
// CTAD
template<QuantityValue V>
explicit(false) quantity(V) -> quantity<dim_one, one, V>;
template<QuantityLike Q>
explicit quantity(Q) -> quantity<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit, typename quantity_like_traits<Q>::rep>;
// non-member binary operators
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
{
using ret = common_quantity_for<std::plus<>, Q1, Q2>;
return ret(ret(lhs).count() + ret(rhs).count());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
{
using ret = common_quantity_for<std::minus<>, Q1, Q2>;
return ret(ret(lhs).count() - ret(rhs).count());
}
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires quantity_value_for_<std::multiplies<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator*(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
{
using dim = dimension_multiply<D1, D2>;
using unit = downcast_unit<dim, (U1::ratio / dimension_unit<D1>::ratio) * (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using ret = quantity<dim, unit, std::invoke_result_t<std::multiplies<>, Rep1, Rep2>>;
return ret(lhs.count() * rhs.count());
}
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires quantity_value_for_<std::divides<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
{
gsl_ExpectsAudit(rhs.count() != quantity_values<Rep2>::zero());
using dim = dimension_divide<D1, D2>;
using unit = downcast_unit<dim, (U1::ratio / dimension_unit<D1>::ratio) / (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using ret = quantity<dim, unit, std::invoke_result_t<std::divides<>, Rep1, Rep2>>;
return ret(lhs.count() / rhs.count());
}
template<typename D1, typename U1, typename Rep1, typename U2, typename Rep2>
requires (!floating_point_<Rep1>) && (!floating_point_<Rep2>) &&
quantity_value_for_<std::modulus<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator%(const quantity<D1, U1, Rep1>& lhs, const quantity<dim_one, U2, Rep2>& rhs)
{
gsl_ExpectsAudit(rhs.count() != quantity_values<Rep2>::zero());
using unit = downcast_unit<D1, U1::ratio * U2::ratio>;
using ret = quantity<D1, unit, std::invoke_result_t<std::modulus<>, Rep1, Rep2>>;
return ret(lhs.count() % rhs.count());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires (!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) &&
quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs)
{
gsl_ExpectsAudit(rhs.count() != quantity_values<typename Q2::rep>::zero());
using ret = common_quantity_for<std::modulus<>, Q1, Q2>;
return ret(ret(lhs).count() % ret(rhs).count());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
{
using cq = common_quantity<Q1, Q2>;
return cq(lhs).count() <=> cq(rhs).count();
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
{
using cq = common_quantity<Q1, Q2>;
return cq(lhs).count() == cq(rhs).count();
}
// type traits
namespace detail {
template<typename D, typename U, typename Rep>
inline constexpr bool is_quantity<quantity<D, U, Rep>> = true;
} // namespace detail
} // namespace units
+388
View File
@@ -0,0 +1,388 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/concepts.h>
#include <units/customization_points.h>
#include <units/bits/dimension_op.h>
#include <units/bits/external/type_traits.h>
#include <units/bits/pow.h>
#include <cassert>
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable:4244) // warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int
#endif //_MSC_VER
namespace units {
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity;
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity_point;
template<Kind K, UnitOf<typename K::dimension> U, QuantityValue Rep>
class quantity_kind;
template<PointKind PK, UnitOf<typename PK::dimension> U, QuantityValue Rep>
class quantity_point_kind;
namespace detail {
template<typename T>
inline constexpr ratio quantity_ratio = std::enable_if_t<!Quantity<T>>{};
template<typename D, typename U, typename Rep>
inline constexpr ratio quantity_ratio<quantity<D, U, Rep>> = []
{
if constexpr(BaseDimension<D>) {
return U::ratio;
}
else {
return D::base_units_ratio * U::ratio / D::coherent_unit::ratio;
}
}();
template<typename QFrom, typename QTo>
inline constexpr ratio cast_ratio = []
{
using FromU = TYPENAME QFrom::unit;
using ToU = TYPENAME QTo::unit;
if constexpr(same_unit_reference<FromU, ToU>::value) {
return FromU::ratio / ToU::ratio;
}
else {
return quantity_ratio<QFrom> / quantity_ratio<QTo>;
}
}();
template<typename From, typename To>
struct cast_traits;
template<typename From, typename To>
requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
struct cast_traits<From, To> {
using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
using rep_type = ratio_type;
};
template<typename From, typename To>
requires (!common_type_with_<std::common_type_t<From, To>, std::intmax_t>) &&
scalable_number_<std::common_type_t<From, To>, std::intmax_t> &&
requires { typename std::common_type_t<From, To>::value_type; } &&
common_type_with_<typename std::common_type_t<From, To>::value_type, std::intmax_t>
struct cast_traits<From, To> {
using ratio_type = std::common_type_t<typename std::common_type_t<From, To>::value_type, std::intmax_t>;
using rep_type = std::common_type_t<From, To>;
};
} // namespace detail
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets the target quantity type to cast to. For example:
*
* auto q1 = units::quantity_cast<units::isq::si::time<units::isq::si::second>>(1_q_ms);
*
* @tparam To a target quantity type to cast to
*/
template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> Rep>
requires QuantityOf<To, D> && std::constructible_from<typename To::rep, std::common_type_t<typename To::rep, Rep>>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
using traits = detail::cast_traits<Rep, typename To::rep>;
using ratio_type = TYPENAME traits::ratio_type;
using rep_type = TYPENAME traits::rep_type;
constexpr auto c_ratio = detail::cast_ratio<quantity<D, U, Rep>, To>;
if constexpr (treat_as_floating_point<rep_type>) {
return To(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
(static_cast<ratio_type>(c_ratio.num) * detail::fpow10<ratio_type>(c_ratio.exp) / static_cast<ratio_type>(c_ratio.den))));
}
else {
if constexpr (c_ratio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
(static_cast<ratio_type>(c_ratio.num) * static_cast<ratio_type>(detail::ipow10(c_ratio.exp))) /
static_cast<ratio_type>(c_ratio.den)));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
static_cast<ratio_type>(c_ratio.num) /
(static_cast<ratio_type>(c_ratio.den) * static_cast<ratio_type>(detail::ipow10(-c_ratio.exp)))));
}
}
}
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets only the target dimension to cast to. For example:
*
* auto q1 = units::quantity_cast<units::isq::si::dim_acceleration>(200_q_Gal);
*
* @tparam ToD a dimension type to use for a target quantity
*/
template<Dimension ToD, typename D, typename U, typename Rep>
requires equivalent<ToD, D>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
return quantity_cast<quantity<ToD, downcast_unit<ToD, U::ratio>, Rep>>(q);
}
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets only the target unit to cast to. For example:
*
* auto q1 = units::quantity_cast<units::isq::si::second>(1_q_ms);
*
* @tparam ToU a unit type to use for a target quantity
*/
template<Unit ToU, typename D, typename U, typename Rep>
requires UnitOf<ToU, D>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
return quantity_cast<quantity<D, ToU, Rep>>(q);
}
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets both the target dimension and unit to cast to. For example:
*
* auto q1 = units::quantity_cast<units::isq::si::dim_speed, units::isq::si::kilometre_per_hour>(v1);
*
* @note This cast is especially useful when working with quantities of unknown dimensions
* (@c unknown_dimension).
*
* @tparam ToD a dimension type to use for a target quantity
* @tparam ToU a unit type to use for a target quantity
*/
template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
requires equivalent<ToD, D> && UnitOf<ToU, ToD>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
return quantity_cast<quantity<ToD, ToU, Rep>>(q);
}
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
*
* This cast gets only representation to cast to. For example:
*
* auto q1 = units::quantity_cast<int>(1_q_ms);
*
* @tparam ToRep a representation type to use for a target quantity
*/
template<QuantityValue ToRep, typename D, typename U, scalable_with_<ToRep> Rep>
requires std::constructible_from<ToRep, std::common_type_t<ToRep, Rep>>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
return quantity_cast<quantity<D, U, ToRep>>(q);
}
/**
* @brief Explicit cast of a quantity point
*
* Implicit conversions between quantity points of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets the target quantity point type to cast to or anything that works for quantity_cast. For example:
*
* auto q1 = units::quantity_point_cast<decltype(quantity_point{0_q_s})>(quantity_point{1_q_ms});
* auto q1 = units::quantity_point_cast<units::isq::si::time<units::isq::si::second>>(quantity_point{1_q_ms});
* auto q1 = units::quantity_point_cast<units::isq::si::dim_acceleration>(quantity_point{200_q_Gal});
* auto q1 = units::quantity_point_cast<units::isq::si::second>(quantity_point{1_q_ms});
* auto q1 = units::quantity_point_cast<int>(quantity_point{1_q_ms});
*
* @tparam CastSpec a target quantity point type to cast to or anything that works for quantity_cast
*/
template<typename CastSpec, typename D, typename U, typename Rep>
requires is_specialization_of<CastSpec, quantity_point> ||
requires(quantity<D, U, Rep> q) { quantity_cast<CastSpec>(q); }
[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<D, U, Rep>& qp)
{
if constexpr (is_specialization_of<CastSpec, quantity_point>)
return quantity_point(quantity_cast<typename CastSpec::quantity_type>(qp.relative()));
else
return quantity_point(quantity_cast<CastSpec>(qp.relative()));
}
/**
* @brief Explicit cast of a quantity point
*
* Implicit conversions between quantity points of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets both the target dimension and unit to cast to. For example:
*
* auto q1 = units::quantity_point_cast<units::isq::si::dim_speed, units::isq::si::kilometre_per_hour>(v1);
*
* @note This cast is especially useful when working with quantity points of unknown dimensions
* (@c unknown_dimension).
*
* @tparam ToD a dimension type to use for a target quantity
* @tparam ToU a unit type to use for a target quantity
*/
template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
requires equivalent<ToD, D> && UnitOf<ToU, ToD>
[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<D, U, Rep>& q)
{
return quantity_point_cast<quantity_point<ToD, ToU, Rep>>(q);
}
/**
* @brief Explicit cast of a quantity kind
*
* Implicit conversions between quantity kinds of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets the target (quantity) kind type to cast to or anything that works for quantity_cast. For example:
*
* auto q1 = units::quantity_kind_cast<decltype(ns::width{1 * m})>(quantity_kind{ns::width{1 * mm});
* auto q1 = units::quantity_kind_cast<ns::height_kind>(ns::width{1 * m});
* auto q1 = units::quantity_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::width{1 * mm});
* auto q1 = units::quantity_kind_cast<units::isq::si::dim_acceleration>(ns::rate_of_climb{200 * Gal});
* auto q1 = units::quantity_kind_cast<units::isq::si::metre>(ns::width{1 * mm});
* auto q1 = units::quantity_kind_cast<int>(ns::width{1.0 * mm});
*
* @tparam CastSpec a target (quantity) kind type to cast to or anything that works for quantity_cast
*/
template<typename CastSpec, typename K, typename U, typename Rep>
[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
requires requires { requires is_specialization_of<CastSpec, quantity_kind>;
requires requires { quantity_cast<typename CastSpec::quantity_type>(qk.common()); }; } ||
requires { requires Kind<CastSpec>; requires UnitOf<U, typename CastSpec::dimension>; } ||
requires { quantity_cast<CastSpec>(qk.common()); } // TODO: Simplify when Clang catches up.
{
if constexpr (is_specialization_of<CastSpec, quantity_kind>)
return CastSpec(quantity_cast<typename CastSpec::quantity_type>(qk.common()));
else if constexpr (Kind<CastSpec>)
return quantity_kind<CastSpec, U, Rep>(qk.common());
else {
auto q{quantity_cast<CastSpec>(qk.common())};
using Q = decltype(q);
return quantity_kind<K, typename Q::unit, typename Q::rep>(static_cast<Q&&>(q));
}
}
/**
* @brief Explicit cast of a quantity kind
*
* Implicit conversions between quantity kinds of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets both the target kind and unit to cast to. For example:
*
* auto q1 = units::quantity_kind_cast<ns::height_kind, units::isq::si::kilometre>(w);
*
* @note This cast is especially useful when working with quantity kinds of unknown kind.
*
* @tparam ToK the kind type to use for the target quantity
* @tparam ToU the unit type to use for the target quantity
*/
template<Kind ToK, Unit ToU, typename K, typename U, typename Rep>
requires equivalent<typename ToK::dimension, typename K::dimension> && UnitOf<ToU, typename ToK::dimension>
[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
{
return quantity_kind_cast<quantity_kind<ToK, ToU, Rep>>(qk);
}
/**
* @brief Explicit cast of a quantity point kind
*
* Implicit conversions between quantity point kinds of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast. For example:
*
* auto q1 = units::quantity_point_kind_cast<decltype(ns::x_coordinate{1 * m))>(ns::x_coordinate{1 * mm});
* auto q1 = units::quantity_point_kind_cast<decltype(ns::width{1 * m})>(ns::x_coordinate{1 * mm});
* auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind>(ns::x_coordinate{1 * m});
* auto q1 = units::quantity_point_kind_cast<ns::height_kind>(ns::x_coordinate{1 * m});
* auto q1 = units::quantity_point_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::x_coordinate{1 * mm});
* auto q1 = units::quantity_point_kind_cast<units::isq::si::dim_acceleration>(quantity_point_kind(ns::rate_of_climb{200 * Gal}));
* auto q1 = units::quantity_point_kind_cast<units::isq::si::metre>(ns::x_coordinate{1 * mm});
* auto q1 = units::quantity_point_kind_cast<int>(ns::x_coordinate{1.0 * mm});
*
* @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast
*/
template<typename CastSpec, typename PK, typename U, typename Rep>
[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
requires requires { requires is_specialization_of<CastSpec, quantity_point_kind>;
requires requires { quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()); }; } ||
requires { requires PointKind<CastSpec> && UnitOf<U, typename CastSpec::dimension>; } ||
requires { quantity_kind_cast<CastSpec>(qpk.relative()); } // TODO: Simplify when Clang catches up.
{
if constexpr (is_specialization_of<CastSpec, quantity_point_kind>)
return CastSpec(quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()));
else if constexpr (PointKind<CastSpec>)
return quantity_point_kind(quantity_kind_cast<typename CastSpec::base_kind>(qpk.relative()));
else
return quantity_point_kind(quantity_kind_cast<CastSpec>(qpk.relative()));
}
/**
* @brief Explicit cast of a quantity point kind
*
* Implicit conversions between quantity point kinds of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
*
* This cast gets both the target point kind and unit to cast to. For example:
*
* auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind, units::isq::si::kilometre>(x);
*
* @note This cast is especially useful when working with quantity point kinds of unknown point kind.
*
* @tparam ToPK the point kind type to use for the target quantity
* @tparam ToU the unit type to use for the target quantity
*/
template<PointKind ToPK, Unit ToU, typename PK, typename U, typename Rep>
requires equivalent<typename ToPK::dimension, typename PK::dimension> && UnitOf<ToU, typename ToPK::dimension>
[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
{
return quantity_point_kind_cast<quantity_point_kind<ToPK, ToU, Rep>>(qpk);
}
} // namespace units
#ifdef _MSC_VER
#pragma warning (pop)
#endif //_MSC_VER
+61
View File
@@ -0,0 +1,61 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/fixed_string_io.h>
#include <units/quantity.h>
#include <sstream>
namespace units {
namespace detail {
template<typename CharT, class Traits, typename D, typename U, typename Rep>
void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<D, U, Rep>& q)
{
os << q.count();
constexpr auto symbol = detail::unit_text<D, U>();
if constexpr (!symbol.standard().empty()) {
os << " " << symbol.standard();
}
}
} // namespace detail
template<typename CharT, typename Traits, typename D, typename U, typename Rep>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity<D, U, Rep>& q)
requires requires { os << q.count(); }
{
if(os.width()) {
// std::setw() applies to the whole quantity output so it has to be first put into std::string
std::basic_ostringstream<CharT, Traits> s;
detail::to_stream(s, q);
return os << s.str();
}
detail::to_stream(os, q);
return os;
}
} // namespace units
+385
View File
@@ -0,0 +1,385 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/kind.h>
#include <units/quantity.h>
#include <concepts>
#include <type_traits>
#include <utility>
namespace units {
namespace detail {
template<Kind K>
inline constexpr auto make_quantity_kind_fn = [](auto q) {
using Q = decltype(q);
return quantity_kind<K, typename Q::unit, typename Q::rep>(std::move(q));
};
template<QuantityKind QK>
inline constexpr auto& make_quantity_kind = make_quantity_kind_fn<typename QK::kind_type>;
template<Kind K>
inline constexpr auto downcasted_kind_fn = [](auto q) {
using Q = decltype(q);
return make_quantity_kind_fn<downcast_kind<K, typename Q::dimension>>(std::move(q));
};
template<QuantityKind QK>
inline constexpr auto& downcasted_kind = downcasted_kind_fn<typename QK::kind_type::base_kind>;
} // namespace detail
/**
* @brief A concept matching two related quantity kinds
*
* Satisfied by quantity kinds having equivalent base kinds.
*/
template<typename QK1, typename QK2>
concept QuantityKindRelatedTo = QuantityKind<QK1> && QuantityKind<QK2> &&
equivalent<typename QK1::kind_type::base_kind, typename QK2::kind_type::base_kind>;
/**
* @brief A quantity kind
*
* A quantity with more specific usage as determined by its kind.
* See https://jcgm.bipm.org/vim/en/1.2.html and NOTE 1 at https://jcgm.bipm.org/vim/en/1.1.html.
*
* @tparam K the kind of quantity
* @tparam U the measurement unit of the quantity kind
* @tparam Rep the type to be used to represent values of the quantity kind
*/
template<Kind K, UnitOf<typename K::dimension> U, QuantityValue Rep = double>
class quantity_kind {
public:
using kind_type = K;
using quantity_type = quantity<typename K::dimension, U, Rep>;
using dimension = typename quantity_type::dimension;
using unit = typename quantity_type::unit;
using rep = typename quantity_type::rep;
private:
quantity_type q_;
public:
quantity_kind() = default;
quantity_kind(const quantity_kind&) = default;
quantity_kind(quantity_kind&&) = default;
template<safe_convertible_to_<rep> Value>
requires is_same_v<dimension, dim_one> && std::is_constructible_v<quantity_type, Value>
constexpr explicit quantity_kind(const Value& v) : q_(v) {}
template<typename Q>
requires (Quantity<Q> || QuantityLike<Q>) && std::is_constructible_v<quantity_type, Q>
constexpr explicit quantity_kind(const Q& q) : q_{q} {}
template<QuantityKindEquivalentTo<quantity_kind> QK2>
requires std::is_convertible_v<typename QK2::quantity_type, quantity_type>
constexpr explicit(false) quantity_kind(const QK2& qk) : q_{qk.common()} {}
quantity_kind& operator=(const quantity_kind&) = default;
quantity_kind& operator=(quantity_kind&&) = default;
[[nodiscard]] constexpr quantity_type common() const noexcept { return q_; }
[[nodiscard]] static constexpr quantity_kind zero() noexcept
requires requires { quantity_type::zero(); }
{
return quantity_kind(quantity_type::zero());
}
[[nodiscard]] static constexpr quantity_kind one() noexcept
requires requires { quantity_type::one(); }
{
return quantity_kind(quantity_type::one());
}
[[nodiscard]] static constexpr quantity_kind min() noexcept
requires requires { quantity_type::min(); }
{
return quantity_kind(quantity_type::min());
}
[[nodiscard]] static constexpr quantity_kind max() noexcept
requires requires { quantity_type::max(); }
{
return quantity_kind(quantity_type::max());
}
[[nodiscard]] constexpr quantity_kind operator+() const
requires requires(quantity_type q) { +q; }
{
return *this;
}
[[nodiscard]] constexpr QuantityKind auto operator-() const
requires requires(quantity_type q) { -q; }
{
return detail::make_quantity_kind<quantity_kind>(-q_);
}
constexpr quantity_kind& operator++()
requires requires(quantity_type q) { ++q; }
{
++q_;
return *this;
}
[[nodiscard]] constexpr quantity_kind operator++(int)
requires requires(quantity_type q) { q++; }
{
return quantity_kind(q_++);
}
constexpr quantity_kind& operator--()
requires requires(quantity_type q) { --q; }
{
--q_;
return *this;
}
[[nodiscard]] constexpr quantity_kind operator--(int)
requires requires(quantity_type q) { q--; }
{
return quantity_kind(q_--);
}
constexpr quantity_kind& operator+=(const quantity_kind& qk)
requires requires(quantity_type q) { q += qk.common(); }
{
q_ += qk.common();
return *this;
}
constexpr quantity_kind& operator-=(const quantity_kind& qk)
requires requires(quantity_type q) { q -= qk.common(); }
{
q_ -= qk.common();
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator*=(const Rep2& rhs)
requires requires(quantity_type q) { q *= rhs; }
{
q_ *= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator*=(const quantity_kind<downcast_kind<K, dim_one>, units::one, Rep2>& rhs)
requires requires(quantity_type q) { q *= rhs.common(); }
{
q_ *= rhs.common();
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator/=(const Rep2& rhs)
requires requires(quantity_type q) { q /= rhs; }
{
gsl_ExpectsAudit(rhs != quantity_values<Rep2>::zero());
q_ /= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator/=(const quantity_kind<downcast_kind<K, dim_one>, units::one, Rep2>& rhs)
requires requires(quantity_type q) { q /= rhs.common(); }
{
q_ /= rhs.common();
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator%=(const Rep2& rhs)
requires (!Quantity<Rep2> || Dimensionless<Rep2>) && requires(quantity_type q, const Rep2 r) { q %= r; }
{
gsl_ExpectsAudit(rhs != quantity_values<Rep2>::zero());
q_ %= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator%=(const quantity_kind<downcast_kind<K, dim_one>, units::one, Rep2>& rhs)
requires requires(quantity_type q) { q %= rhs.common(); }
{
q_ %= rhs.common();
return *this;
}
constexpr quantity_kind& operator%=(const quantity_kind& qk)
requires requires(quantity_type q) { q %= qk.common(); }
{
gsl_ExpectsAudit(qk.common().count() != quantity_values<rep>::zero());
q_ %= qk.common();
return *this;
}
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
template<QuantityValue Value>
[[nodiscard]] friend constexpr QuantityKind auto operator*(const quantity_kind& qk, const Value& v)
requires requires(quantity_type q) { { q * v } -> Quantity; }
{
return detail::make_quantity_kind<quantity_kind>(qk.common() * v);
}
template<QuantityValue Value>
[[nodiscard]] friend constexpr QuantityKind auto operator*(const Value& v, const quantity_kind& qk)
requires requires(quantity_type q) { { v * q } -> Quantity; }
{
return detail::make_quantity_kind<quantity_kind>(v * qk.common());
}
template<QuantityValue Value>
[[nodiscard]] friend constexpr QuantityKind auto operator/(const quantity_kind& qk, const Value& v)
requires requires(quantity_type q) { { q / v } -> Quantity; }
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
return detail::make_quantity_kind<quantity_kind>(qk.common() / v);
}
template<QuantityValue Value>
[[nodiscard]] friend constexpr QuantityKind auto operator/(const Value& v, const quantity_kind& qk)
requires requires(quantity_type q) { { v / q } -> Quantity; }
{
gsl_ExpectsAudit(qk.common().count() != quantity_values<rep>::zero());
return detail::downcasted_kind<quantity_kind>(v / qk.common());
}
template<QuantityValue Value>
[[nodiscard]] friend constexpr QuantityKind auto operator%(const quantity_kind& qk, const Value& v)
requires requires(quantity_type q) { q % v; }
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
return detail::make_quantity_kind<quantity_kind>(qk.common() % v);
}
[[nodiscard]] friend constexpr auto operator<=>(const quantity_kind& lhs, const quantity_kind& rhs)
requires std::three_way_comparable<quantity_type>
{
return lhs.common() <=> rhs.common();
}
[[nodiscard]] friend constexpr bool operator==(const quantity_kind&, const quantity_kind&) = default;
};
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator+(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() + rhs.common(); }
{
return detail::make_quantity_kind<QK1>(lhs.common() + rhs.common());
}
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator-(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() - rhs.common(); }
{
return detail::make_quantity_kind<QK1>(lhs.common() - rhs.common());
}
template<QuantityKind QK, Quantity Q>
[[nodiscard]] constexpr QuantityKind auto operator*(const QK& lhs, const Q& rhs)
requires requires { lhs.common() * rhs; }
{
return detail::downcasted_kind<QK>(lhs.common() * rhs);
}
template<Quantity Q, QuantityKind QK>
[[nodiscard]] constexpr QuantityKind auto operator*(const Q& lhs, const QK& rhs)
requires requires { lhs * rhs.common(); }
{
return detail::downcasted_kind<QK>(lhs * rhs.common());
}
template<QuantityKind QK1, QuantityKindRelatedTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator*(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() * rhs.common(); }
{
return detail::downcasted_kind<QK1>(lhs.common() * rhs.common());
}
template<QuantityKind QK, Quantity Q>
[[nodiscard]] constexpr QuantityKind auto operator/(const QK& lhs, const Q& rhs)
requires requires { lhs.common() / rhs; }
{
gsl_ExpectsAudit(rhs.count() != quantity_values<typename Q::rep>::zero());
return detail::downcasted_kind<QK>(lhs.common() / rhs);
}
template<Quantity Q, QuantityKind QK>
[[nodiscard]] constexpr QuantityKind auto operator/(const Q& lhs, const QK& rhs)
requires requires { lhs / rhs.common(); }
{
gsl_ExpectsAudit(rhs.common().count() != quantity_values<typename QK::rep>::zero());
return detail::downcasted_kind<QK>(lhs / rhs.common());
}
template<QuantityKind QK1, QuantityKindRelatedTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator/(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() / rhs.common(); }
{
return detail::downcasted_kind<QK1>(lhs.common() / rhs.common());
}
template<QuantityKind QK, Dimensionless D>
[[nodiscard]] constexpr QuantityKind auto operator%(const QK& lhs, const D& rhs)
requires requires { lhs.common() % rhs; }
{
gsl_ExpectsAudit(rhs.count() != quantity_values<typename D::rep>::zero());
return detail::make_quantity_kind<QK>(lhs.common() % rhs);
}
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
[[nodiscard]] constexpr QuantityKind auto operator%(const QK1& lhs, const QK2& rhs)
requires requires { lhs.common() % rhs.common(); }
{
gsl_ExpectsAudit(rhs.common().count() != quantity_values<typename QK2::rep>::zero());
return detail::make_quantity_kind<QK1>(lhs.common() % rhs.common());
}
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
requires std::three_way_comparable_with<typename QK1::quantity_type, typename QK2::quantity_type>
[[nodiscard]] constexpr auto operator<=>(const QK1& lhs, const QK2& rhs)
{
return lhs.common() <=> rhs.common();
}
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
requires std::equality_comparable_with<typename QK1::quantity_type, typename QK2::quantity_type>
[[nodiscard]] constexpr bool operator==(const QK1& lhs, const QK2& rhs)
{
return lhs.common() == rhs.common();
}
// type traits
namespace detail {
template<typename K, typename U, typename Rep>
inline constexpr bool is_quantity_kind<quantity_kind<K, U, Rep>> = true;
} // namespace detail
} // namespace units
+202
View File
@@ -0,0 +1,202 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/quantity.h>
#include <compare>
namespace units {
/**
* @brief A quantity point
*
* An absolute quantity with respect to zero (which represents some origin).
*
* @tparam D a dimension of the quantity point (can be either a BaseDimension or a DerivedDimension)
* @tparam U a measurement unit of the quantity point
* @tparam Rep a type to be used to represent values of a quantity point
*/
template<Dimension D, UnitOf<D> U, QuantityValue Rep = double>
class quantity_point {
public:
using quantity_type = quantity<D, U, Rep>;
using dimension = typename quantity_type::dimension;
using unit = typename quantity_type::unit;
using rep = typename quantity_type::rep;
private:
quantity_type q_{};
public:
quantity_point() = default;
quantity_point(const quantity_point&) = default;
quantity_point(quantity_point&&) = default;
template<safe_convertible_to_<rep> Value>
requires is_same_v<dimension, dim_one> && std::is_constructible_v<quantity_type, Value>
constexpr explicit quantity_point(const Value& v) : q_(v) {}
constexpr explicit quantity_point(const quantity_type& q) : q_{q} {}
template<QuantityLike Q>
requires std::is_constructible_v<quantity_type, Q>
constexpr explicit quantity_point(const Q& q) : q_{q} {}
template<QuantityPointLike QP>
constexpr explicit quantity_point(const QP& qp)
requires std::is_constructible_v<quantity_type, decltype(quantity_point_like_traits<QP>::relative(qp))>
: q_{quantity_point_like_traits<QP>::relative(qp)} {}
template<QuantityPoint QP2>
requires std::is_convertible_v<typename QP2::quantity_type, quantity_type>
constexpr explicit(false) quantity_point(const QP2& qp) : q_{qp.relative()} {}
quantity_point& operator=(const quantity_point&) = default;
quantity_point& operator=(quantity_point&&) = default;
[[nodiscard]] constexpr quantity_type relative() const noexcept { return q_; }
[[nodiscard]] static constexpr quantity_point min() noexcept
requires requires { quantity_type::min(); }
{
return quantity_point(quantity_type::min());
}
[[nodiscard]] static constexpr quantity_point max() noexcept
requires requires { quantity_type::max(); }
{
return quantity_point(quantity_type::max());
}
constexpr quantity_point& operator++()
requires requires(quantity_type q) { ++q; }
{
++q_;
return *this;
}
[[nodiscard]] constexpr quantity_point operator++(int)
requires requires(quantity_type q) { q++; }
{
return quantity_point(q_++);
}
constexpr quantity_point& operator--()
requires requires(quantity_type q) { --q; }
{
--q_;
return *this;
}
[[nodiscard]] constexpr quantity_point operator--(int)
requires requires(quantity_type q) { q--; }
{
return quantity_point(q_--);
}
constexpr quantity_point& operator+=(const quantity_type& q)
requires requires { q_ += q; }
{
q_ += q;
return *this;
}
constexpr quantity_point& operator-=(const quantity_type& q)
requires requires(quantity_type q1, quantity_type q2) { q1 -= q2; }
{
q_ -= q;
return *this;
}
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
template<Quantity Q>
[[nodiscard]] friend constexpr QuantityPoint auto operator+(const quantity_point& lhs, const Q& rhs)
requires requires(quantity_type q) { q + rhs; }
{
const auto q = lhs.relative() + rhs;
using q_type = decltype(q);
return quantity_point<typename q_type::dimension, typename q_type::unit, typename q_type::rep>(q);
}
template<Quantity Q>
[[nodiscard]] friend constexpr QuantityPoint auto operator+(const Q& lhs, const quantity_point& rhs)
requires requires { rhs + lhs; }
{
return rhs + lhs;
}
template<Quantity Q>
[[nodiscard]] friend constexpr QuantityPoint auto operator-(const quantity_point& lhs, const Q& rhs)
requires requires(quantity_type q) { q - rhs; }
{
const auto q = lhs.relative() - rhs;
using q_type = decltype(q);
return quantity_point<typename q_type::dimension, typename q_type::unit, typename q_type::rep>(q);
}
template<QuantityPoint QP>
[[nodiscard]] friend constexpr Quantity auto operator-(const quantity_point& lhs, const QP& rhs)
requires requires(quantity_type q) { q - rhs.relative(); }
{
return lhs.relative() - rhs.relative();
}
template<QuantityPoint QP>
requires std::three_way_comparable_with<quantity_type, typename QP::quantity_type>
[[nodiscard]] friend constexpr auto operator<=>(const quantity_point& lhs, const QP& rhs)
{
return lhs.relative() <=> rhs.relative();
}
template<QuantityPoint QP>
requires std::equality_comparable_with<quantity_type, typename QP::quantity_type>
[[nodiscard]] friend constexpr bool operator==(const quantity_point& lhs, const QP& rhs)
{
return lhs.relative() == rhs.relative();
}
};
template<QuantityValue V>
explicit(false) quantity_point(V) -> quantity_point<dim_one, one, V>;
template<QuantityLike Q>
quantity_point(Q) -> quantity_point<typename quantity_like_traits<Q>::dimension,
typename quantity_like_traits<Q>::unit, typename quantity_like_traits<Q>::rep>;
template<QuantityPointLike QP>
explicit quantity_point(QP) -> quantity_point<typename quantity_point_like_traits<QP>::dimension,
typename quantity_point_like_traits<QP>::unit,
typename quantity_point_like_traits<QP>::rep>;
namespace detail {
template<typename D, typename U, typename Rep>
inline constexpr bool is_quantity_point<quantity_point<D, U, Rep>> = true;
} // namespace detail
} // namespace units
@@ -0,0 +1,196 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/quantity_kind.h>
#include <units/quantity_point.h>
#include <compare>
namespace units {
/**
* @brief A quantity point kind
*
* An absolute quantity kind with respect to zero (which represents some origin).
*
* @tparam PK the point kind of quantity point
* @tparam U the measurement unit of the quantity point kind
* @tparam Rep the type to be used to represent values of the quantity point kind
*/
template<PointKind PK, UnitOf<typename PK::dimension> U, QuantityValue Rep = double>
class quantity_point_kind {
public:
using point_kind_type = PK;
using kind_type = typename PK::base_kind;
using quantity_kind_type = quantity_kind<kind_type, U, Rep>;
using quantity_type = typename quantity_kind_type::quantity_type;
using dimension = typename quantity_type::dimension;
using unit = typename quantity_type::unit;
using rep = typename quantity_type::rep;
private:
quantity_kind_type qk_;
public:
quantity_point_kind() = default;
quantity_point_kind(const quantity_point_kind&) = default;
quantity_point_kind(quantity_point_kind&&) = default;
template<safe_convertible_to_<rep> Value>
requires std::is_constructible_v<quantity_kind_type, Value>
constexpr explicit quantity_point_kind(const Value& v) : qk_(v) {}
constexpr explicit quantity_point_kind(const quantity_type& q) : qk_{q} {}
template<QuantityLike Q>
requires std::is_constructible_v<quantity_type, Q>
constexpr explicit quantity_point_kind(const Q& q) : qk_{q} {}
template<QuantityPointLike QP>
requires std::is_constructible_v<quantity_point<dimension, U, Rep>, QP>
constexpr explicit quantity_point_kind(const QP& qp) : qk_{quantity_point_like_traits<QP>::relative(qp)} {}
constexpr explicit quantity_point_kind(const quantity_point<dimension, U, Rep>& qp) : qk_{qp.relative()} {}
constexpr explicit quantity_point_kind(const quantity_kind_type& qk) : qk_{qk} {}
template<QuantityPointKindEquivalentTo<quantity_point_kind> QPK2>
requires std::is_convertible_v<typename QPK2::quantity_kind_type, quantity_kind_type>
constexpr explicit(false) quantity_point_kind(const QPK2& qpk) : qk_{qpk.relative()} {}
quantity_point_kind& operator=(const quantity_point_kind&) = default;
quantity_point_kind& operator=(quantity_point_kind&&) = default;
[[nodiscard]] constexpr quantity_kind_type relative() const noexcept { return qk_; }
[[nodiscard]] static constexpr quantity_point_kind min() noexcept
requires requires { quantity_kind_type::min(); }
{
return quantity_point_kind(quantity_kind_type::min());
}
[[nodiscard]] static constexpr quantity_point_kind max() noexcept
requires requires { quantity_kind_type::max(); }
{
return quantity_point_kind(quantity_kind_type::max());
}
constexpr quantity_point_kind& operator++()
requires requires(quantity_kind_type qk) { ++qk; }
{
++qk_;
return *this;
}
[[nodiscard]] constexpr quantity_point_kind operator++(int)
requires requires(quantity_kind_type qk) { qk++; }
{
return quantity_point_kind(qk_++);
}
constexpr quantity_point_kind& operator--()
requires requires(quantity_kind_type qk) { --qk; }
{
--qk_;
return *this;
}
[[nodiscard]] constexpr quantity_point_kind operator--(int)
requires requires(quantity_kind_type qk) { qk--; }
{
return quantity_point_kind(qk_--);
}
constexpr quantity_point_kind& operator+=(const quantity_kind_type& qk)
requires requires(quantity_kind_type qk1, const quantity_kind_type qk2) { qk1 += qk2; }
{
qk_ += qk;
return *this;
}
constexpr quantity_point_kind& operator-=(const quantity_kind_type& qk)
requires requires(quantity_kind_type qk1, const quantity_kind_type qk2) { qk1 -= qk2; }
{
qk_ -= qk;
return *this;
}
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
template<QuantityKind QK>
[[nodiscard]] friend constexpr QuantityPointKind auto operator+(const quantity_point_kind& lhs, const QK& rhs)
requires requires(quantity_kind_type qk) { qk + rhs; }
{
return units::quantity_point_kind(lhs.relative() + rhs);
}
template<QuantityKind QK>
[[nodiscard]] friend constexpr QuantityPointKind auto operator+(const QK& lhs, const quantity_point_kind& rhs)
requires requires(quantity_kind_type qk) { lhs + qk; }
{
return units::quantity_point_kind(lhs + rhs.relative());
}
template<QuantityKind QK>
[[nodiscard]] friend constexpr QuantityPointKind auto operator-(const quantity_point_kind& lhs, const QK& rhs)
requires requires(quantity_kind_type qk) { qk - rhs; }
{
return units::quantity_point_kind(lhs.relative() - rhs);
}
[[nodiscard]] friend constexpr QuantityKind auto operator-(const quantity_point_kind& lhs, const quantity_point_kind& rhs)
requires requires(quantity_kind_type qk) { qk - qk; }
{
return lhs.relative() - rhs.relative();
}
template<QuantityPointKind QPK>
requires std::three_way_comparable_with<quantity_kind_type, typename QPK::quantity_kind_type>
[[nodiscard]] friend constexpr auto operator<=>(const quantity_point_kind& lhs, const QPK& rhs)
{
return lhs.relative() <=> rhs.relative();
}
template<QuantityPointKind QPK>
requires std::equality_comparable_with<quantity_kind_type, typename QPK::quantity_kind_type>
[[nodiscard]] friend constexpr bool operator==(const quantity_point_kind& lhs, const QPK& rhs)
{
return lhs.relative() == rhs.relative();
}
};
template<QuantityKind QK>
quantity_point_kind(QK) ->
quantity_point_kind<downcast_point_kind<typename QK::kind_type>, typename QK::unit, typename QK::rep>;
namespace detail {
template<typename PK, typename U, typename Rep>
inline constexpr bool is_quantity_point_kind<quantity_point_kind<PK, U, Rep>> = true;
} // namespace detail
} // namespace units
+482
View File
@@ -0,0 +1,482 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <random>
#include <functional>
#include <units/concepts.h>
namespace units {
namespace detail {
template <Quantity Q, typename InputIt>
static std::vector<typename Q::rep> i_qty_to_rep(InputIt first, InputIt last)
{
std::vector<typename Q::rep> intervals_rep;
intervals_rep.reserve(static_cast<size_t>(std::distance(first, last)));
for (auto itr = first; itr != last; ++itr) { intervals_rep.push_back(itr->count()); }
return intervals_rep;
}
template <Quantity Q>
static std::vector<typename Q::rep> bl_qty_to_rep(std::initializer_list<Q>& bl)
{
std::vector<typename Q::rep> bl_rep;
bl_rep.reserve(bl.size());
for (const Q& qty : bl) { bl_rep.push_back(qty.count()); }
return bl_rep;
}
template <Quantity Q, typename UnaryOperation>
inline static std::vector<typename Q::rep> fw_bl_pwc(std::initializer_list<Q>& bl, UnaryOperation fw)
{
using rep = TYPENAME Q::rep;
std::vector<rep> w_bl;
w_bl.reserve(bl.size());
for (const Q& qty : bl) { w_bl.push_back(fw(qty)); }
std::vector<rep> weights;
weights.reserve(bl.size());
for (size_t i = 0; i < bl.size() - 1; ++i) { weights.push_back(w_bl[i] + w_bl[i + 1]); }
weights.push_back(0);
return weights;
}
template <Quantity Q, typename UnaryOperation>
static std::vector<typename Q::rep> fw_bl_pwl(std::initializer_list<Q>& bl, UnaryOperation fw)
{
std::vector<typename Q::rep> weights;
weights.reserve(bl.size());
for (const Q& qty : bl) { weights.push_back(fw(qty)); }
return weights;
}
} // namespace detail
template<Quantity Q>
requires std::integral<typename Q::rep>
struct uniform_int_distribution : public std::uniform_int_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::uniform_int_distribution<rep>;
uniform_int_distribution() : base() {}
uniform_int_distribution(const Q& a, const Q& b) : base(a.count(), b.count()) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q a() const { return Q(base::a()); }
Q b() const { return Q(base::b()); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct uniform_real_distribution : public std::uniform_real_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::uniform_real_distribution<rep>;
uniform_real_distribution() : base() {}
uniform_real_distribution(const Q& a, const Q& b) : base(a.count(), b.count()) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q a() const { return Q(base::a()); }
Q b() const { return Q(base::b()); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::integral<typename Q::rep>
struct binomial_distribution : public std::binomial_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::binomial_distribution<rep>;
binomial_distribution() : base() {}
binomial_distribution(const Q& t, double p) : base(t.count(), p) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q t() const { return Q(base::t()); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::integral<typename Q::rep>
struct negative_binomial_distribution : public std::negative_binomial_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::negative_binomial_distribution<rep>;
negative_binomial_distribution() : base() {}
negative_binomial_distribution(const Q& k, double p) : base(k.count(), p) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q k() const { return Q(base::k()); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::integral<typename Q::rep>
struct geometric_distribution : public std::geometric_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::geometric_distribution<rep>;
geometric_distribution() : base() {}
geometric_distribution(double p) : base(p) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::integral<typename Q::rep>
struct poisson_distribution : public std::poisson_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::poisson_distribution<rep>;
poisson_distribution() : base() {}
explicit poisson_distribution(double p) : base(p) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct exponential_distribution : public std::exponential_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::exponential_distribution<rep>;
exponential_distribution() : base() {}
explicit exponential_distribution(const rep& lambda) : base(lambda) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct gamma_distribution : public std::gamma_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::gamma_distribution<rep>;
gamma_distribution() : base() {}
gamma_distribution(const rep& alpha, const rep& beta) : base(alpha, beta) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct weibull_distribution : public std::weibull_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::weibull_distribution<rep>;
weibull_distribution() : base() {}
weibull_distribution(const rep& a, const rep& b) : base(a, b) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct extreme_value_distribution : public std::extreme_value_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::extreme_value_distribution<rep>;
extreme_value_distribution() : base() {}
extreme_value_distribution(const Q& a, const rep& b) : base(a.count(), b) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q a() const { return Q(base::a()); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct normal_distribution : public std::normal_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::normal_distribution<rep>;
normal_distribution() : base() {}
normal_distribution(const Q& mean, const Q& stddev) : base(mean.count(), stddev.count()) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q mean() const { return Q(base::mean()); }
Q stddev() const { return Q(base::stddev()); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct lognormal_distribution : public std::lognormal_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::lognormal_distribution<rep>;
lognormal_distribution() : base() {}
lognormal_distribution(const Q& m, const Q& s) : base(m.count(), s.count()) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q m() const { return Q(base::m()); }
Q s() const { return Q(base::s()); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct chi_squared_distribution : public std::chi_squared_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::chi_squared_distribution<rep>;
chi_squared_distribution() : base() {}
explicit chi_squared_distribution(const rep& n) : base(n) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct cauchy_distribution : public std::cauchy_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::cauchy_distribution<rep>;
cauchy_distribution() : base() {}
cauchy_distribution(const Q& a, const Q& b) : base(a.count(), b.count()) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q a() const { return Q(base::a()); }
Q b() const { return Q(base::b()); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct fisher_f_distribution : public std::fisher_f_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::fisher_f_distribution<rep>;
fisher_f_distribution() : base() {}
fisher_f_distribution(const rep& m, const rep& n) : base(m, n) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
struct student_t_distribution : public std::student_t_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::student_t_distribution<rep>;
student_t_distribution() : base() {}
explicit student_t_distribution(const rep& n) : base(n) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::integral<typename Q::rep>
struct discrete_distribution : public std::discrete_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::discrete_distribution<rep>;
discrete_distribution() : base() {}
template <typename InputIt>
discrete_distribution(InputIt first, InputIt last) : base(first, last) {}
discrete_distribution(std::initializer_list<double> weights) : base(weights) {}
template <typename UnaryOperation>
discrete_distribution(std::size_t count, double xmin, double xmax, UnaryOperation unary_op) :
base(count, xmin, xmax, unary_op) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
class piecewise_constant_distribution : public std::piecewise_constant_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::piecewise_constant_distribution<rep>;
template <typename InputIt>
piecewise_constant_distribution(const std::vector<rep>& i, InputIt first_w) :
base(i.cbegin(), i.cend(), first_w) {}
piecewise_constant_distribution(const std::vector<rep>& bl, const std::vector<rep>& weights) :
base(bl.cbegin(), bl.cend(), weights.cbegin()) {}
public:
piecewise_constant_distribution() : base() {}
template <typename InputIt1, typename InputIt2>
piecewise_constant_distribution(InputIt1 first_i, InputIt1 last_i, InputIt2 first_w) :
piecewise_constant_distribution(detail::i_qty_to_rep<Q>(first_i, last_i), first_w) {}
template <typename UnaryOperation>
piecewise_constant_distribution(std::initializer_list<Q> bl, UnaryOperation fw) :
piecewise_constant_distribution(detail::bl_qty_to_rep(bl), detail::fw_bl_pwc(bl, fw)) {}
template <typename UnaryOperation>
piecewise_constant_distribution(std::size_t nw, const Q& xmin, const Q& xmax, UnaryOperation fw) :
base(nw, xmin.count(), xmax.count(), [fw](rep val) { return fw(Q(val)); }) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
std::vector<Q> intervals() const
{
std::vector<rep> intervals_rep = base::intervals();
std::vector<Q> intervals_qty;
intervals_qty.reserve(intervals_rep.size());
for (const rep& val : intervals_rep) { intervals_qty.push_back(Q(val)); }
return intervals_qty;
}
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
template<Quantity Q>
requires std::floating_point<typename Q::rep>
class piecewise_linear_distribution : public std::piecewise_linear_distribution<typename Q::rep>
{
using rep = TYPENAME Q::rep;
using base = TYPENAME std::piecewise_linear_distribution<rep>;
template <typename InputIt>
piecewise_linear_distribution(const std::vector<rep>& i, InputIt first_w) :
base(i.cbegin(), i.cend(), first_w) {}
piecewise_linear_distribution(const std::vector<rep>& bl, const std::vector<rep>& weights) :
base(bl.cbegin(), bl.cend(), weights.cbegin()) {}
public:
piecewise_linear_distribution() : base() {}
template <typename InputIt1, typename InputIt2>
piecewise_linear_distribution(InputIt1 first_i, InputIt1 last_i, InputIt2 first_w) :
piecewise_linear_distribution(detail::i_qty_to_rep<Q>(first_i, last_i), first_w) {}
template <typename UnaryOperation>
piecewise_linear_distribution(std::initializer_list<Q> bl, UnaryOperation fw) :
piecewise_linear_distribution(detail::bl_qty_to_rep(bl), detail::fw_bl_pwl(bl, fw)) {}
template <typename UnaryOperation>
piecewise_linear_distribution(std::size_t nw, const Q& xmin, const Q& xmax, UnaryOperation fw) :
base(nw, xmin.count(), xmax.count(), [fw](rep val) { return fw(Q(val)); }) {}
template<typename Generator>
Q operator()(Generator& g) { return Q(base::operator()(g)); }
std::vector<Q> intervals() const
{
std::vector<rep> intervals_rep = base::intervals();
std::vector<Q> intervals_qty;
intervals_qty.reserve(intervals_rep.size());
for (const rep& val : intervals_rep) { intervals_qty.push_back(Q(val)); }
return intervals_qty;
}
Q min() const { return Q(base::min()); }
Q max() const { return Q(base::max()); }
};
} // namespace units
+164
View File
@@ -0,0 +1,164 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/hacks.h>
#include <units/bits/math_concepts.h>
#include <units/bits/ratio_maths.h>
#include <units/bits/pow.h>
#include <units/bits/root.h>
#include <cstdint>
#include <numeric>
#include <type_traits>
#include <tuple>
#include <gsl/gsl-lite.hpp>
namespace units {
struct ratio;
constexpr ratio inverse(const ratio& r);
/**
* @brief Provides compile-time rational arithmetic support.
*
* This class is really similar to @c std::ratio but gets an additional `Exp`
* template parameter that defines the exponent of the ratio. Another important
* difference is the fact that the objects of that class are used as class NTTPs
* rather then a type template parameter kind.
*/
struct ratio {
std::intmax_t num;
std::intmax_t den;
std::intmax_t exp;
constexpr explicit ratio(std::intmax_t n, std::intmax_t d = 1, std::intmax_t e = 0): num(n), den(d), exp(e)
{
gsl_Expects(den != 0);
detail::normalize(num, den, exp);
}
[[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default;
[[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs)
{
const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den);
const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den);
return ratio(detail::safe_multiply(lhs.num / gcd1, rhs.num / gcd2),
detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1),
lhs.exp + rhs.exp);
}
[[nodiscard]] friend constexpr ratio operator/(const ratio& lhs, const ratio& rhs)
{
return lhs * inverse(rhs);
}
};
[[nodiscard]] constexpr ratio inverse(const ratio& r)
{
return ratio(r.den, r.num, -r.exp);
}
[[nodiscard]] constexpr bool is_integral(const ratio& r)
{
if(r.exp < 0) {
return false;
} else {
return detail::gcdpow(r.num, r.exp, r.den) == r.den;
}
}
namespace detail {
[[nodiscard]] constexpr auto make_exp_align(const ratio& r, std::intmax_t alignment)
{
gsl_Expects(alignment > 0);
const std::intmax_t rem = r.exp % alignment;
if (rem == 0) { // already aligned
return std::array{r.num, r.den, r.exp};
}
if (r.exp > 0) { // remainder is positive
return std::array{r.num * ipow10(rem), r.den, r.exp - rem};
}
// remainder is negative
return std::array{r.num, r.den * ipow10(-rem), r.exp - rem};
}
template<std::intmax_t N>
requires gt_zero<N>
[[nodiscard]] constexpr ratio root(const ratio& r)
{
if constexpr (N == 1) {
return r;
} else {
if (r.num == 0) {
return ratio(0);
}
const auto aligned = make_exp_align(r, N);
return ratio(iroot<N>(aligned[0]), iroot<N>(aligned[1]), aligned[2] / N);
}
}
} // namespace detail
template<std::intmax_t Num, std::intmax_t Den = 1>
requires detail::non_zero<Den>
[[nodiscard]] constexpr ratio pow(const ratio& r)
{
if constexpr (Num == 0) {
return ratio(1);
} else if constexpr (Num == Den) {
return r;
} else {
// simplify factors first and compute power for positive exponent
constexpr std::intmax_t gcd = std::gcd(Num, Den);
constexpr std::intmax_t num = detail::abs(Num / gcd);
constexpr std::intmax_t den = detail::abs(Den / gcd);
// integer root loses precision so do pow first
const ratio result = detail::root<den>(detail::pow_impl<num>(r));
if constexpr (Num * Den < 0) { // account for negative exponent
return inverse(result);
} else {
return result;
}
}
}
[[nodiscard]] constexpr ratio sqrt(const ratio& r) { return pow<1, 2>(r); }
[[nodiscard]] constexpr ratio cbrt(const ratio& r) { return pow<1, 3>(r); }
// common_ratio
[[nodiscard]] constexpr ratio common_ratio(const ratio& r1, const ratio& r2)
{
const auto res = detail::gcd_frac(r1.num, r1.den, r1.exp, r2.num, r2.den, r2.exp);
return ratio(res[0], res[1], res[2]);
}
} // namespace units
+159
View File
@@ -0,0 +1,159 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/fixed_string.h>
#include <units/bits/external/hacks.h>
#include <gsl/gsl-lite.hpp>
#include <compare>
namespace units {
namespace detail {
constexpr void validate_ascii_char([[maybe_unused]] char c) noexcept { gsl_Expects((c & 0x80) == 0); }
template<std::size_t N>
constexpr void validate_ascii_string([[maybe_unused]] const char (&s)[N + 1]) noexcept
{
#ifndef NDEBUG
if constexpr (N != 0)
for (size_t i = 0; i < N; ++i)
validate_ascii_char(s[i]);
#endif
}
} // namespace detail
/**
* @brief A symbol text representation
*
* This class template is responsible for definition and handling of a symbol text
* representation. In the libary it is used to define symbols of units and prefixes.
* Each symbol can have two versions: Unicode and ASCI-only.
*
* @tparam StandardCharT Character type to be used for a Unicode representation
* @tparam N The size of a Unicode symbol
* @tparam M The size of the ASCII-only symbol
*/
template<typename StandardCharT, std::size_t N, std::size_t M>
struct basic_symbol_text {
basic_fixed_string<StandardCharT, N> standard_;
basic_fixed_string<char, M> ascii_;
constexpr basic_symbol_text(char s) noexcept: standard_(s), ascii_(s) { detail::validate_ascii_char(s); }
constexpr basic_symbol_text(StandardCharT s, char a) noexcept: standard_(s), ascii_(a) { detail::validate_ascii_char(a); }
constexpr basic_symbol_text(const char (&s)[N + 1]) noexcept: standard_(s), ascii_(s) { detail::validate_ascii_string<N>(s); }
constexpr basic_symbol_text(const basic_fixed_string<char, N>& s) noexcept: standard_(s), ascii_(s) { detail::validate_ascii_string<N>(s.data_); }
constexpr basic_symbol_text(const StandardCharT (&s)[N + 1], const char (&a)[M + 1]) noexcept: standard_(s), ascii_(a) { detail::validate_ascii_string<M>(a); }
constexpr basic_symbol_text(const basic_fixed_string<StandardCharT, N>& s, const basic_fixed_string<char, M>& a) noexcept: standard_(s), ascii_(a) { detail::validate_ascii_string<M>(a.data_); }
[[nodiscard]] constexpr auto& standard() { return standard_; }
[[nodiscard]] constexpr const auto& standard() const { return standard_; }
[[nodiscard]] constexpr auto& ascii() { return ascii_; }
[[nodiscard]] constexpr const auto& ascii() const { return ascii_; }
template<std::size_t N2, std::size_t M2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2, M + M2> operator+(
const basic_symbol_text& lhs, const basic_symbol_text<StandardCharT, N2, M2>& rhs) noexcept
{
return basic_symbol_text<StandardCharT, N + N2, M + M2>(
lhs.standard() + rhs.standard(), lhs.ascii() + rhs.ascii());
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2, M + N2> operator+(
const basic_symbol_text& lhs, const basic_fixed_string<StandardCharT, N2>& rhs) noexcept
{
return lhs + basic_symbol_text<StandardCharT, N2, N2>(rhs);
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2, M + N2> operator+(
const basic_fixed_string<StandardCharT, N2>& lhs, const basic_symbol_text& rhs) noexcept
{
return basic_symbol_text<StandardCharT, N2, N2>(lhs) + rhs;
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2 - 1, M + N2 - 1> operator+(
const basic_symbol_text& lhs, const StandardCharT (&rhs)[N2]) noexcept
{
return lhs + basic_symbol_text<StandardCharT, N2 - 1, N2 - 1>(rhs);
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + N2 - 1, M + N2 - 1> operator+(
const StandardCharT (&lhs)[N2], const basic_symbol_text& rhs) noexcept
{
return basic_symbol_text<StandardCharT, N2 - 1, N2 - 1>(lhs) + rhs;
}
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + 1, M + 1> operator+(
const basic_symbol_text& lhs, StandardCharT rhs) noexcept
{
return lhs + basic_symbol_text<StandardCharT, 1, 1>(rhs);
}
[[nodiscard]] constexpr friend basic_symbol_text<StandardCharT, N + 1, M + 1> operator+(
StandardCharT lhs, const basic_symbol_text& rhs) noexcept
{
return basic_symbol_text<StandardCharT, 1, 1>(lhs) + rhs;
}
template<typename StandardCharT2, std::size_t N2, std::size_t M2>
[[nodiscard]] friend constexpr auto operator<=>(const basic_symbol_text& lhs,
const basic_symbol_text<StandardCharT2, N2, M2>& rhs) noexcept
{
if (const auto cmp = lhs.standard() <=> rhs.standard(); cmp != 0) return cmp;
return lhs.ascii() <=> rhs.ascii();
}
template<typename StandardCharT2, std::size_t N2, std::size_t M2>
[[nodiscard]] friend constexpr bool operator==(const basic_symbol_text& lhs,
const basic_symbol_text<StandardCharT2, N2, M2>& rhs) noexcept
{
return lhs.standard() == rhs.standard() && lhs.ascii() == rhs.ascii();
}
};
basic_symbol_text(char) -> basic_symbol_text<char, 1, 1>;
template<typename StandardCharT>
basic_symbol_text(StandardCharT, char) -> basic_symbol_text<StandardCharT, 1, 1>;
template<std::size_t N>
basic_symbol_text(const char (&)[N]) -> basic_symbol_text<char, N - 1, N - 1>;
template<std::size_t N>
basic_symbol_text(const basic_fixed_string<char, N>&) -> basic_symbol_text<char, N, N>;
template<typename StandardCharT, std::size_t N, std::size_t M>
basic_symbol_text(const StandardCharT (&)[N], const char (&)[M]) -> basic_symbol_text<StandardCharT, N - 1, M - 1>;
template<typename StandardCharT, std::size_t N, std::size_t M>
basic_symbol_text(const basic_fixed_string<StandardCharT, N>&,
const basic_fixed_string<char, M>&) -> basic_symbol_text<StandardCharT, N, M>;
} // namespace units
+262
View File
@@ -0,0 +1,262 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/deduced_symbol_text.h>
#include <units/bits/deduced_unit.h>
#include <units/bits/external/downcasting.h>
#include <units/bits/external/fixed_string.h>
#include <units/bits/external/text_tools.h>
#include <units/bits/external/type_traits.h>
#include <units/derived_dimension.h>
#include <units/prefix.h>
#include <units/ratio.h>
#include <units/symbol_text.h>
namespace units {
/**
* @brief A common point for a hierarchy of units
*
* A unit is an entity defined and adopted by convention, with which any other quantity of
* the same kind can be compared to express the ratio of the second quantity to the first
* one as a number.
*
* All units of the same dimension can be convereted between each other. To allow this all of
* them are expressed as different ratios of the same one proprietary chosen reference unit
* (i.e. all length units are expressed in terms of meter, all mass units are expressed in
* terms of gram, ...)
*
* @tparam R a ratio of a reference unit
* @tparam U a unit to use as a reference for this dimension
*/
template<ratio R, typename U>
requires UnitRatio<R>
struct scaled_unit : downcast_base<scaled_unit<R, U>> {
static constexpr ::units::ratio ratio = R;
using reference = U;
};
template<Dimension D, auto R>
// template<Dimension D, ratio R> // TODO: GCC crash!!!
requires UnitRatio<R>
using downcast_unit = downcast<scaled_unit<R, typename dimension_unit<D>::reference>>;
template<Unit U1, Unit U2>
struct same_unit_reference : is_same<typename U1::reference, typename U2::reference> {};
/**
* @brief An unnamed unit
*
* Defines a new unnamed (in most cases coherent) derived unit of a specific derived dimension
* and it should be passed in this dimension's definition.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
*/
template<typename Child>
struct unit : downcast_dispatch<Child, scaled_unit<ratio(1), Child>> {
static constexpr bool is_named = false;
using prefix_family = no_prefix;
};
/**
* @brief A named unit
*
* Defines a named (in most cases coherent) unit that is then passed to a dimension definition.
* A named unit may be used by other units defined with the prefix of the same type, unless
* no_prefix is provided for PF template parameter (in such a case it is impossible to define
* a prefix unit based on this one).
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Symbol a short text representation of the unit
* @tparam PF no_prefix or a type of prefix family
*/
template<typename Child, basic_symbol_text Symbol, PrefixFamily PF>
struct named_unit : downcast_dispatch<Child, scaled_unit<ratio(1), Child>> {
static constexpr bool is_named = true;
static constexpr auto symbol = Symbol;
using prefix_family = PF;
};
/**
* @brief A scaled unit
*
* Defines a new named unit that is a scaled version of another unit. Such unit can be used by
* other units defined with the prefix of the same type, unless no_prefix is provided for PF
* template parameter (in such a case it is impossible to define a prefix unit based on this
* one).
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Symbol a short text representation of the unit
* @tparam PF no_prefix or a type of prefix family
* @tparam R a scale to apply to U
* @tparam U a reference unit to scale
*/
template<typename Child, basic_symbol_text Symbol, PrefixFamily PF, ratio R, Unit U>
requires UnitRatio<R>
struct named_scaled_unit : downcast_dispatch<Child, scaled_unit<R * U::ratio, typename U::reference>> {
static constexpr bool is_named = true;
static constexpr auto symbol = Symbol;
using prefix_family = PF;
};
/**
* @brief A prefixed unit
*
* Defines a new unit that is a scaled version of another unit by the provided prefix. It is
* only possible to create such a unit if the given prefix type matches the one defined in a
* reference unit.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam P prefix to be appied to the reference unit
* @tparam U reference unit
*/
template<typename Child, Prefix P, Unit U>
requires U::is_named && std::same_as<typename P::prefix_family, typename U::prefix_family>
struct prefixed_unit : downcast_dispatch<Child, scaled_unit<P::ratio * U::ratio, typename U::reference>> {
static constexpr bool is_named = true;
static constexpr auto symbol = P::symbol + U::symbol;
using prefix_family = no_prefix;
};
/**
* @brief A unit with a deduced ratio and symbol
*
* Defines a new unit with a deduced ratio and symbol based on the recipe from the provided
* derived dimension. The number and order of provided units should match the recipe of the
* derived dimension. All of the units provided should also be a named ones so it is possible
* to create a deduced symbol text.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Dim a derived dimension recipe to use for deduction
* @tparam U the unit of the first composite dimension from provided derived dimension's recipe
* @tparam URest the units for the rest of dimensions from the recipe
*/
template<typename Child, DerivedDimension Dim, Unit U, Unit... URest>
requires detail::same_scaled_units<typename Dim::recipe, U, URest...> &&
(U::is_named && (URest::is_named && ... && true))
struct deduced_unit : downcast_dispatch<Child, detail::deduced_unit<Dim, U, URest...>> {
static constexpr bool is_named = false;
static constexpr auto symbol = detail::deduced_symbol_text<Dim, U, URest...>();
using prefix_family = no_prefix;
};
/**
* @brief A unit with a deduced ratio and symbol that can be used as a named unit for children
*
* Defines a new unit with a deduced ratio and symbol based on the recipe from the provided
* derived dimension. The number and order of provided units should match the recipe of the
* derived dimension. All of the units provided should also be a named ones so it is possible
* to create a deduced symbol text.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Dim a derived dimension recipe to use for deduction
* @tparam U the unit of the first composite dimension from provided derived dimension's recipe
* @tparam URest the units for the rest of dimensions from the recipe
*/
template<typename Child, DerivedDimension Dim, Unit U, Unit... URest>
requires detail::same_scaled_units<typename Dim::recipe, U, URest...> &&
(U::is_named && (URest::is_named && ... && true))
// TODO - 'noble' is placeholder to sort of mean can pass its name on to other deduced units
struct noble_deduced_unit : downcast_dispatch<Child, detail::deduced_unit<Dim, U, URest...>> {
static constexpr bool is_named = true;
static constexpr auto symbol = detail::deduced_symbol_text<Dim, U, URest...>();
using prefix_family = no_prefix;
};
/**
* @brief A named unit with a deduced ratio
*
* Defines a new unit with a deduced ratio and the given symbol based on the recipe from the provided
* derived dimension. The number and order of provided units should match the recipe of the
* derived dimension. All of the units provided should also be a named ones so it is possible
* to create a deduced symbol text.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Dim a derived dimension recipe to use for deduction
* @tparam Symbol a short text representation of the unit
* @tparam PF no_prefix or a type of prefix family
* @tparam U the unit of the first composite dimension from provided derived dimension's recipe
* @tparam URest the units for the rest of dimensions from the recipe
*/
template<typename Child, DerivedDimension Dim, basic_symbol_text Symbol, PrefixFamily PF, Unit U, Unit... URest>
requires detail::same_scaled_units<typename Dim::recipe, U, URest...>
struct named_deduced_unit : downcast_dispatch<Child, detail::deduced_unit<Dim, U, URest...>> {
static constexpr bool is_named = true;
static constexpr auto symbol = Symbol;
using prefix_family = PF;
};
/**
* @brief An aliased named unit
*
* Defines a named alias for another unit. It is useful to assign alternative names and symbols
* to the already predefined units (i.e. "tonne" for "megagram").
* An alias unit may be used by other units defined with the prefix of the same type, unless
* no_prefix is provided for PF template parameter (in such a case it is impossible to define
* a prefix unit based on this one).
*
* @tparam U Unit for which an alias is defined
* @tparam Symbol a short text representation of the unit
* @tparam PF no_prefix or a type of prefix family
*/
template<Unit U, basic_symbol_text Symbol, PrefixFamily PF>
struct alias_unit : U {
static constexpr bool is_named = true;
static constexpr auto symbol = Symbol;
using prefix_family = PF;
};
/**
* @brief A prefixed alias unit
*
* Defines a new unit that is an alias for a scaled version of another unit by the provided
* prefix. It is only possible to create such a unit if the given prefix type matches the one
* defined in a reference unit.
*
* @tparam U Unit for which an alias is defined
* @tparam P prefix to be appied to the reference unit
* @tparam AU reference alias unit
*/
// TODO gcc bug: 95015
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95015
// template<Unit U, Prefix P, AliasUnit AU>
// requires (!AliasUnit<U>) && std::same_as<typename P::prefix_family, typename AU::prefix_family>
template<Unit U, Prefix P, Unit AU>
requires AU::is_named && std::same_as<typename P::prefix_family, typename AU::prefix_family>
struct prefixed_alias_unit : U {
static constexpr bool is_named = true;
static constexpr auto symbol = P::symbol + AU::symbol;
using prefix_family = no_prefix;
};
/**
* @brief Unknown unit
*
* Used as a coherent unit of an unknown dimension.
*/
struct unknown_coherent_unit : unit<unknown_coherent_unit> {};
} // namespace units