diff --git a/docs/api_reference/src/intro.tex b/docs/api_reference/src/intro.tex index 4daca1c50..220e24f1d 100644 --- a/docs/api_reference/src/intro.tex +++ b/docs/api_reference/src/intro.tex @@ -38,6 +38,16 @@ Available from: \url{https://wg21.link/\IsoCpp{}} \item The \Cpp{} Standards Committee. +P2900R10: \doccite{Contracts for \Cpp{}}. +Edited by Joshua Berne. +Available from: \url{https://wg21.link/P2900R10} +\item +The \Cpp{} Standards Committee. +P2996R7: \doccite{Reflection for \Cpp{}26}. +Edited by Barry Revzin. +Available from: \url{https://wg21.link/P2996R7} +\item +The \Cpp{} Standards Committee. SD-8: \doccite{Standard Library Compatibility}. Edited by Bryce Lelbach. Available from: \url{https://wg21.link/SD8} diff --git a/docs/api_reference/src/macros_extensions.tex b/docs/api_reference/src/macros_extensions.tex index 53eabe670..ee810646e 100644 --- a/docs/api_reference/src/macros_extensions.tex +++ b/docs/api_reference/src/macros_extensions.tex @@ -1,5 +1,9 @@ \newcommand{\IsoCpp}{N4971} +\newcommand{\stdconcept}[1]{#1} + +\newcommand{\liboverload}[2]{\indexlibraryglobal{#1(#2)}#1} + %% Inline non-parenthesized C++ reference \newcommand{\refcpp}[1]{\href{https://wg21.link/#1}{\IsoCpp{}, [#1]}} \newcommand{\irefcpp}[1]{\nolinebreak[3] (\refcpp{#1})} diff --git a/docs/api_reference/src/quantities.tex b/docs/api_reference/src/quantities.tex index 56e0d69e5..67d4df40a 100644 --- a/docs/api_reference/src/quantities.tex +++ b/docs/api_reference/src/quantities.tex @@ -9,11 +9,14 @@ \begin{modularlibsumtab}{Quantities library summary}{qties.summary} \ref{qty.helpers} & Helpers & \tcode{mp_units.core} \\ -\ref{qty.traits} & Traits & \\ -\ref{qty.concepts} & Concepts & \\ -\ref{qty.types} & Types & \\ -\ref{qty.compat} & Compatibility & \\ -\ref{qty.one} & Dimension one & \\ \rowsep +\ref{qty.expr.temp} & Expression template & \\ +\ref{qty.dim} & Dimension & \\ +\ref{qty.spec} & Quantity specification & \\ +\ref{qty.mag} & Magnitude & \\ +\ref{qty.unit} & Unit & \\ +\ref{qty.ref} & Reference & \\ +\ref{qty.rep} & Representation & \\ +\ref{qty.types} & Quantity types & \\ \rowsep \ref{qty.systems} & Systems & \tcode{mp_units.systems} \\ \ref{qty.chrono} & \tcode{std::chrono} compatibility & \\ \end{modularlibsumtab} @@ -36,107 +39,1410 @@ export namespace mp_units { -export enum class quantity_character { scalar, vector, tensor }; +enum class @\libglobal{text_encoding}@ : std::int8_t { @\libmember{utf8}{text_encoding}@, @\libmember{portable}{text_encoding}@, @\libmember{default_encoding}{text_encoding}@ = utf8 }; + +enum class @\libglobal{quantity_character}@ { @\libmember{scalar}{quantity_character}@, @\libmember{complex}{quantity_character}@, @\libmember{vector}{quantity_character}@, @\libmember{tensor}{quantity_character}@ }; + +// \ref{qty.helpers}, helpers + +// \ref{qty.symbol.text}, class template \tcode{symbol_text} + +// \ref{qty.expr.temp}, expression template + +// \ref{qty.expr.temp.types}, types + +template +struct type_list; + +template +struct per; + +template + requires @\seebelownc@ +struct power; + +// \ref{qty.dim}, dimension + +// \ref{qty.dim.concepts}, concepts + +template +concept Dimension = @\seebelownc@; + +template +concept DimensionOf = @\seebelownc@; + +// \ref{qty.dim.types}, types + +template +struct base_dimension; + +template<@\seebelownc@> +struct derived_dimension; + +struct dimension_one; +inline constexpr dimension_one dimension_one = @\seebelownc@; + +consteval @\libconcept{Dimension}@ auto @\liboverload{inverse}{\cname{Dimension}}@(@\libconcept{Dimension}@ auto d) { return dimension_one / d; } + +template + requires(Den != 0) +consteval @\libconcept{Dimension}@ auto pow(D d); +consteval @\libconcept{Dimension}@ auto @\liboverload{sqrt}{\cname{Dimension}}@(@\libconcept{Dimension}@ auto d) { return pow<1, 2>(d); } +consteval @\libconcept{Dimension}@ auto @\liboverload{cbrt}{\cname{Dimension}}@(@\libconcept{Dimension}@ auto d) { return pow<1, 3>(d); } + +struct @\libglobal{dimension_symbol_formatting}@ { + text_encoding encoding = text_encoding::default_encoding; +}; + +template Out, @\libconcept{Dimension}@ D> +constexpr Out dimension_symbol_to(Out out, D d, const dimension_symbol_formatting& fmt = {}); + +template +constexpr auto dimension_symbol(D); + +// \ref{qty.spec}, quantity specification + +// \ref{qty.spec.concepts}, concepts + +template +concept QuantitySpec = @\seebelownc@; + +template +consteval @\seebelownc@ get_kind(Q q); + +template +concept QuantitySpecOf = @\seebelownc@; + +// \ref{qty.spec.types}, types + +struct is_kind; +inline constexpr is_kind is_kind = @\seebelownc@; + +template +struct quantity_spec; // \notdef + +template<@\exposconceptnc{BaseDimension}@ auto Dim, auto... Args> + requires @\seebelownc@ +struct quantity_spec; + +template<@\exposconceptnc{DerivedQuantitySpec}@ auto Eq, auto... Args> + requires @\seebelownc@ +struct quantity_spec; + +template<@\exposconceptnc{NamedQuantitySpec}@ auto QS, auto... Args> + requires @\seebelownc@ +struct quantity_spec; + +template<@\exposconceptnc{NamedQuantitySpec}@ auto QS, @\exposconceptnc{DerivedQuantitySpec}@ auto Eq, auto... Args> + requires @\seebelownc@ +struct quantity_spec; + +template<@\exposconceptnc{DerivedQuantitySpecExpr}@... Expr> +struct derived_quantity_spec; + +struct dimensionless; +inline constexpr dimensionless dimensionless = @\seebelownc@; + +template + requires @\seebelownc@ +struct kind_of_; +template + requires requires { typename kind_of_; } +inline constexpr kind_of_ @\libglobal{kind_of}@{}; + +consteval @\libconcept{QuantitySpec}@ auto @\liboverload{inverse}{\cname{QuantitySpec}}@(@\libconcept{QuantitySpec}@ auto q) { return dimensionless / q; } + +template + requires(Den != 0) +consteval @\libconcept{QuantitySpec}@ auto pow(Q q); +consteval @\libconcept{QuantitySpec}@ auto @\liboverload{sqrt}{\cname{QuantitySpec}}@(@\libconcept{QuantitySpec}@ auto q) { return pow<1, 2>(q); } +consteval @\libconcept{QuantitySpec}@ auto @\liboverload{cbrt}{\cname{QuantitySpec}}@(@\libconcept{QuantitySpec}@ auto q) { return pow<1, 3>(q); } + +consteval bool implicitly_convertible(@\libconcept{QuantitySpec}@ auto from, @\libconcept{QuantitySpec}@ auto to); +consteval bool explicitly_convertible(@\libconcept{QuantitySpec}@ auto from, @\libconcept{QuantitySpec}@ auto to); +consteval bool castable(@\libconcept{QuantitySpec}@ auto from, @\libconcept{QuantitySpec}@ auto to); +consteval bool interconvertible(@\libconcept{QuantitySpec}@ auto qs1, @\libconcept{QuantitySpec}@ auto qs2); + +template<@\libconcept{QuantitySpec}@ Q> +consteval @\exposconceptnc{QuantityKindSpec}@ auto get_kind(Q q); + +consteval @\libconcept{QuantitySpec}@ auto get_common_quantity_spec(@\libconcept{QuantitySpec}@ auto... qs) + requires @\seebelownc@; + +// \ref{qty.mag}, magnitude + +// \ref{qty.unit}, unit + +// \ref{qty.ref}, reference + +// \ref{qty.rep}, representation + +// \ref{qty.rep.traits}, traits + +// \ref{qty.fp.traits}, floating-point + +template +constexpr bool treat_as_floating_point = @\seebelownc@; + +// \ref{qty.set.traits}, set + +template +constexpr bool is_scalar = @\seebelownc@; + +template +constexpr bool is_complex = false; + +template +constexpr bool is_vector = false; + +template +constexpr bool is_tensor = false; + +// \ref{qty.rep.concepts}, concepts + +template +concept Representation = @\seebelownc@; + +template +concept RepresentationOf = @\seebelownc@; + +// \ref{qty.types}, quantity types // \ref{qty.traits}, traits +// \ref{qty.val.traits}, values + template -constexpr bool treat_as_floating_point = std::is_floating_point_v; +struct quantity_values; + +// \ref{qty.compat.traits}, compatibility + +template +struct quantity_like_traits; // \notdef + +template +struct quantity_point_like_traits; // \notdef + +template +concept quantity_like = @\seebelownc@; + +template +concept quantity_point_like = @\seebelownc@; + +// \ref{qty.concepts}, quantity concepts + +// \ref{qty}, class template \tcode{quantity} +template<@\libconcept{Reference}@ auto R, @\libconcept{RepresentationOf}@ Rep = double> +class quantity; + +// \ref{qty.pt.concepts}, quantity point concepts + +// \ref{qty.pt}, class template \tcode{quantity_point} +template<@\unspec@> +class quantity_point; + +} +\end{codeblock} + +\rSec1[mp.units.systems.syn]{Module \tcode{mp_units.systems} synopsis} +\indexmodule{mp_units.systems}% +\begin{codeblock} +export module mp_units.systems; + +export import mp_units.core; +import std; + +export namespace mp_units { + +// \ref{qty.chrono}, \tcode{std::chrono} compatibility + +template +struct quantity_like_traits>; + +template +struct chrono_point_origin_; +template +constexpr chrono_point_origin_ @\libglobal{chrono_point_origin}@{}; + +template +struct quantity_point_like_traits< + std::chrono::time_point>>; + +} +\end{codeblock} + +\rSec1[qty.helpers]{Helpers} + +\rSec2[qty.helpers.non.types]{Non-types} + +\begin{itemdecl} +template +concept @\defexposconceptnc{tag-type}@ = type_is_empty(^T) && type_is_final(^T); // \expos +\end{itemdecl} + +\begin{itemdecl} +consteval bool @\exposidnc{is-specialization-of}@( // \expos + std::meta::info type, std::meta::info template_name); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{is_type(type) \&\& is_class_template(template_name)} is \tcode{true}. + +\pnum +\returns +\tcode{has_template_arguments(type) \&\& template_of(type) == template_name}. +\end{itemdescr} + +\begin{itemdecl} +consteval bool @\exposidnc{is-convertible-to-base-subobject-of}@( // \expos + std::meta::info type, std::meta::info template_name); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{is_type(type) \&\& is_class_template(template_name)} is \tcode{true}. + +\pnum +\returns +\tcode{true} if +\tcode{[:type:]} has an unambiguous and accessible base +that is a specialization of \tcode{[:template_name:]}, and +\tcode{false} otherwise. +\end{itemdescr} + +\rSec2[qty.ratio]{Struct \exposid{ratio}} + +\begin{codeblock} +namespace mp_units { + +struct @\exposidnc{ratio}@ { // \expos + std::intmax_t num; + std::intmax_t den; + + consteval @\exposidnc{ratio}@(std::intmax_t n, std::intmax_t d = 1); + + friend consteval bool operator==(@\exposidnc{ratio}@, @\exposidnc{ratio}@) = default; + friend consteval auto operator<=>(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs) { return (lhs - rhs).num <=> 0; } + + friend consteval @\exposidnc{ratio}@ operator-(@\exposidnc{ratio}@ r) { return @\exposidnc{ratio}@{-r.num, r.den}; } + + friend consteval @\exposidnc{ratio}@ operator+(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs) + { + return @\exposidnc{ratio}@{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den}; + } + + friend consteval @\exposidnc{ratio}@ operator-(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs) { return lhs + (-rhs); } + + friend consteval @\exposidnc{ratio}@ operator*(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs); + + friend consteval @\exposidnc{ratio}@ operator/(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs) { return lhs* @\exposidnc{ratio}@{rhs.den, rhs.num}; } +}; + +consteval bool is_integral(@\exposidnc{ratio}@ r) { return r.num % r.den == 0; } + +consteval @\exposidnc{ratio}@ common_ratio(@\exposidnc{ratio}@ r1, @\exposidnc{ratio}@ r2) +{ + if (r1.num == r2.num && r1.den == r2.den) return r1; + + // $\operatorname{gcd}(a/b, c/d) = \operatorname{gcd}(ad, cb) / bd$ + contract_assert(std::numeric_limits::max() / r1.num > r2.den); + contract_assert(std::numeric_limits::max() / r2.num > r1.den); + contract_assert(std::numeric_limits::max() / r1.den > r2.den); + + const std::intmax_t num = std::gcd(r1.num * r2.den, r2.num * r1.den); + const std::intmax_t den = r1.den * r2.den; + const std::intmax_t gcd = std::gcd(num, den); + return @\exposidnc{ratio}@{num / gcd, den / gcd}; +} + +} +\end{codeblock} + +\begin{itemdecl} +consteval @\exposidnc{ratio}@(std::intmax_t n, std::intmax_t d = 1); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{N} and \tcode{D} be the values of \tcode{n} and \tcode{d}. +Let \tcode{R} be \tcode{std::ratio}. + +\pnum +\effects +Equivalent to: \tcode{R}. + +\pnum +\ensures +\tcode{num == R::num \&\& den == R::den} is \tcode{true}. +\end{itemdescr} + +\begin{itemdecl} +friend consteval @\exposidnc{ratio}@ operator*(@\exposidnc{ratio}@ lhs, @\exposidnc{ratio}@ rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{R(r)} be \tcode{std::ratio}, +where \tcode{N} and \tcode{D} are the values of \tcode{r.num} and \tcode{r.den}. +Let \tcode{Res} be \tcode{std::ratio_multiply}. + +\pnum +\effects +Equivalent to: \tcode{return \exposidnc{ratio}\{Res::num, Res::den\}}. +\end{itemdescr} + +\rSec2[qty.fixed.string]{Class template \exposidnc{basic-fixed-string}} + +\rSec2[qty.symbol.text]{Class template \tcode{symbol_text}} + +\rSec1[qty.expr.temp]{Expression template} + +\rSec2[qty.expr.temp.general]{General} + +\pnum +Subclause \ref{qty.expr.temp} specifies the components +used to maintain an ordered, simplified, and readable +argument lists in the names of specializations. +\begin{example} +The framework ensures the following assertion holds. +\begin{codeblock} +using namespace si::unit_symbols; +static_assert(^decltype(km / square(h)) == + ^derived_unit, per>>); +\end{codeblock} +\end{example} + +\rSec2[qty.expr.temp.types]{Types} + +\begin{codeblock} +namespace mp_units { + +template +struct @\libglobal{type_list}@ {}; + +} +\end{codeblock} + +\pnum +\tcode{type_list} encapsulates a list of types. + +\begin{codeblock} +namespace mp_units { + +template +struct @\libglobal{per}@ {}; + +} +\end{codeblock} + +\pnum +\tcode{per} stores the arguments with negative exponents. + +\begin{codeblock} +namespace mp_units { + +template + requires(sizeof...(Den) <= 1 && @\exposidnc{valid-ratio}@(Num, Den...) && @\exposidnc{positive-ratio}@(Num, Den...) && + !@\exposidnc{ratio-one}@(Num, Den...)) +struct @\libglobal{power}@ { + using factor = F; + static constexpr @\exposidnc{ratio}@ exponent{Num, Den...}; +}; + +} +\end{codeblock} + +\pnum +\tcode{power} represents a power\irefiev{102-02-08} +of the form $\tcode{F}^{\tcode{Num}/\tcode{Den}}$. +\begin{note} +\tcode{Den} is optional to shorten the type name when \tcode{Den} is \tcode{1}. +\end{note} + +\rSec2[qty.expr.temp.algo]{Algorithms} + +\pnum +Unless otherwise specified, in the following descriptions, let +\begin{itemize} +\item +\tcode{To} be the template of the resulting type list, and +\item +\tcode{OneType} be the neutral element\irefiev{102-01-19} of the operation. +\end{itemize} + +\begin{codeblock} +consteval std::meta::info @\exposidnc{expr-type}@(std::meta::info t) +{ + return @\exposidnc{is-specialization-of}@(t, ^power) ? template_arguments_of(t)[0] : t; +} + +template +struct @\exposidnc{type-less}@ : + std::bool_constant<@\exposidnc{is-specialization-of}@(^Rhs, ^power) || + display_string_of(@\exposidnc{expr-type}@(^Lhs)) < + display_string_of(@\exposidnc{expr-type}@(^Rhs))> { +}; +\end{codeblock} + +\begin{codeblock} +template +struct @\exposidnc{expr-fractions}@ { + using @\exposidnc{num}@ = @\seebelownc@; + using @\exposidnc{den}@ = @\seebelownc@; +} +\end{codeblock} + +\pnum +\exposidnc{expr-fractions} divides an expression template to numerator and denominator parts. +\exposidnc{num} is TBD. +\exposidnc{den} is TBD. + +\begin{itemdecl} +template typename To, typename OneType, + template typename Pred = @\exposidnc{type-less}@, typename Lhs, typename Rhs> +consteval auto @\exposidnc{expr-multiply}@(Lhs, Rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\exposidnc{expr-multiply} multiplies two sorted expression templates. + +\pnum +\effects +TBD. + +\pnum +\returns +TBD. +\end{itemdescr} + +\begin{itemdecl} +template typename To, typename OneType, + template typename Pred = @\exposidnc{type-less}@, typename Lhs, typename Rhs> +consteval auto @\exposidnc{expr-divide}@(Lhs lhs, Rhs rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\exposidnc{expr-divide} divides two sorted expression templates. + +\pnum +\effects +TBD. + +\pnum +\returns +TBD. +\end{itemdescr} + +\begin{itemdecl} +template typename To, typename OneType, typename T> +consteval auto @\exposidnc{expr-invert}@(T); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\exposidnc{expr-invert} inverts the expression template \tcode{T}. + +\pnum +\effects +TBD. + +\pnum +\returns +TBD. +\end{itemdescr} + +\begin{itemdecl} +template typename To, + typename OneType, template typename Pred = @\exposidnc{type-less}@, typename T> + requires(Den != 0) +consteval auto @\exposidnc{expr-pow}@(T); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\exposidnc{expr-pow} computes the power\irefiev{102-02-08} $\tcode{T}^{\tcode{Num}/\tcode{Den}}$. + +\pnum +\effects +TBD. + +\pnum +\returns +TBD. +\end{itemdescr} + +\begin{itemdecl} +template typename Proj, template typename To, typename OneType, + template typename Pred = @\exposidnc{type-less}@, @\exposidnc{expr-projectable}@ T> +consteval auto @\exposidnc{expr-map}@(T); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\exposidnc{expr-map} maps contents of one expression template to another resulting in a different type list. + +\pnum +Let \tcode{Proj} be projection used for mapping, and +let \tcode{T} be the expression template to map from. + +\pnum +\effects +TBD. + +\pnum +\returns +TBD. +\end{itemdescr} + +\rSec1[qty.dim]{Dimension} + +\rSec2[qty.dim.general]{General} + +\pnum +Subclause \ref{qty.dim} specifies the components +for defining the dimension of a quantity\irefiev{112-01-11}. + +\rSec2[qty.dim.concepts]{Concepts} + +\begin{itemdecl} +template +concept @\deflibconcept{Dimension}@ = @\exposconceptnc{tag-type}@ && std::@\stdconcept{derived_from}@; + +template +concept @\defexposconceptnc{BaseDimension}@ = @\libconcept{Dimension}@ && std::@\stdconcept{derived_from}@; + +consteval bool @\exposidnc{is-dimension-one}@(std::meta::info type_alias) { + return dealias(type_alias) == ^dimension_one; +} + +template +concept @\defexposconceptnc{IsPowerOfDim}@ = + (@\exposidnc{is-specialization-of}@(^T, ^power) && + (@\exposconceptnc{BaseDimension}@ || @\exposidnc{is-dimension-one}@(^typename T::factor))); + +template +constexpr bool @\exposidnc{is-per-of-dims}@ = false; + +template +constexpr bool @\exposidnc{is-per-of-dims}@> = + (... && (@\exposconceptnc{BaseDimension}@ || @\exposidnc{is-dimension-one}@(^Ts) || @\exposconceptnc{IsPowerOfDim}@)); + +template +concept @\defexposconceptnc{DerivedDimensionExpr}@ = + @\exposconceptnc{BaseDimension}@ || @\exposidnc{is-dimension-one}@(^T) || @\exposconceptnc{IsPowerOfDim}@ || @\exposidnc{is-per-of-dims}@; + +template +concept @\defexposconceptnc{SameDimension}@ = @\libconcept{Dimension}@ && @\libconcept{Dimension}@ && D1 == D2; + +template +concept @\deflibconcept{DimensionOf}@ = @\libconcept{Dimension}@ && @\libconcept{Dimension}@ && @\exposconceptnc{SameDimension}@; +\end{itemdecl} + +\rSec2[qty.dim.types]{Types} + +\begin{codeblock} +namespace mp_units { + +struct @\exposidnc{dimension-interface}@ { + friend consteval @\libconcept{Dimension}@ auto operator*(@\libconcept{Dimension}@ auto lhs, @\libconcept{Dimension}@ auto rhs) + { + return @\exposidnc{expr-multiply}@(lhs, rhs); + } + + friend consteval @\libconcept{Dimension}@ auto operator/(@\libconcept{Dimension}@ auto lhs, @\libconcept{Dimension}@ auto rhs) + { + return @\exposidnc{expr-divide}@(lhs, rhs); + } + + friend consteval bool operator==(@\libconcept{Dimension}@ auto lhs, @\libconcept{Dimension}@ auto rhs) + { + return ^decltype(lhs) == ^decltype(rhs); + } +}; + +template +struct @\libglobal{base_dimension}@ : @\exposidnc{dimension-interface}@ { + static constexpr auto symbol = Symbol; +}; + +} +\end{codeblock} + +\pnum +\tcode{base_dimension} is used by the program +to define the dimension of a base quantity\irefiev{112-01-08}. +\begin{example} +\begin{codeblock} +inline constexpr struct dim_length final : base_dimension<"L"> {} dim_length; +\end{codeblock} +\end{example} + +\begin{codeblock} +namespace mp_units { + +template<@\exposconceptnc{DerivedDimensionExpr}@... Expr> +struct @\exposidnc{derived-dimension-impl}@ : @\exposidnc{expr-fractions}@ {}; + +template<@\exposconceptnc{DerivedDimensionExpr}@... Expr> +struct @\libglobal{derived_dimension}@ final : @\exposidnc{dimension-interface}@, @\exposidnc{derived-dimension-impl}@ {}; + +} +\end{codeblock} + +\pnum +\tcode{derived_dimension} is used by the library +to represent the dimension of a derived quantity\irefiev{112-01-10}. +\begin{example} +\begin{codeblock} +constexpr auto acceleration = isq::dim_speed / isq::dim_time; +static_assert(dealias(^decltype(acceleration)) == + ^derived_dimension>>); +\end{codeblock} +\end{example} + +\pnum +\mandates +\tcode{Expr...} is simplified\iref{qty.expr.temp.algo}. +\begin{note} +The library handles the representation of a derived dimension. +The name is not \expos{} for the portability and readability of the specializations. +\end{note} + +\begin{codeblock} +namespace mp_units { + +inline constexpr struct @\libglobal{dimension_one}@ final : @\exposidnc{dimension-interface}@, @\exposidnc{derived-dimension-impl}@<> { +} @\libglobal{dimension_one}@; + +} +\end{codeblock} + +\pnum +\tcode{dimension_one} represents the dimension of a quantity of dimension one\irefiev{112-01-13}. + +\begin{itemdecl} +template + requires(Den != 0) +consteval @\libconcept{Dimension}@ auto @\liboverload{pow}{\cname{Dimension}}@(D d); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Computes $\tcode{d}^{\tcode{Num}/\tcode{Den}}$. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if constexpr (@\exposconceptnc{BaseDimension}@) { + if constexpr (Den == 1) + return derived_dimension>{}; + else + return derived_dimension>{}; +} else + return @\exposidnc{expr-pow}@(d); +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +template Out, @\libconcept{Dimension}@ D> +constexpr Out @\libglobal{dimension_symbol_to}@(Out out, D d, const dimension_symbol_formatting& fmt = {}); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +TBD. + +\pnum +\returns +TBD. +\end{itemdescr} + +\begin{itemdecl} +template +constexpr auto @\libglobal{dimension_symbol}@(D); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +TBD. +\end{codeblock} +\end{itemdescr} + +\rSec1[qty.spec]{Quantity specification} + +\rSec2[qty.spec.general]{General} + +\pnum +Subclause \ref{qty.spec} specifies the components +for defining a quantity\irefiev{112-01-01}. + +\rSec2[qty.spec.concepts]{Concepts} + +\begin{itemdecl} +template +concept @\deflibconcept{QuantitySpec}@ = @\exposconceptnc{tag-type}@ && std::@\stdconcept{derived_from}@; + +template +concept @\defexposconceptnc{QuantityKindSpec}@ = @\exposidnc{is-specialization-of}@(^T, ^kind_of_); + +template +concept @\defexposconceptnc{NamedQuantitySpec}@ = + @\libconcept{QuantitySpec}@ && std::@\stdconcept{derived_from}@ && !@\exposconceptnc{QuantityKindSpec}@; + +consteval bool @\exposidnc{is-dimensionless}@(std::meta::info type_alias) { + return dealias(type_alias) == ^dimensionless; +} + +template +concept @\defexposconceptnc{IsPowerOfQuantitySpec}@ = + (@\exposidnc{is-specialization-of}@(^T, ^power) && + (@\exposconceptnc{NamedQuantitySpec}@ || @\exposidnc{is-dimensionless}@(^typename T::factor))); + +template +constexpr bool @\exposidnc{is-per-of-quantity-specs}@ = false; + +template +constexpr bool @\exposidnc{is-per-of-quantity-specs}@> = + (... && (@\exposconceptnc{NamedQuantitySpec}@ || @\exposidnc{is-dimensionless}@(^Ts) || @\exposconceptnc{IsPowerOfQuantitySpec}@)); + +template +concept @\defexposconceptnc{DerivedQuantitySpecExpr}@ = @\exposconceptnc{NamedQuantitySpec}@ || @\exposidnc{is-dimensionless}@(^T) || + @\exposconceptnc{IsPowerOfQuantitySpec}@ || @\exposidnc{is-per-of-quantity-specs}@; + +template +concept @\defexposconceptnc{DerivedQuantitySpec}@ = + @\libconcept{QuantitySpec}@ && + (@\exposidnc{is-specialization-of}@(^T, ^derived_quantity_spec) || + (@\exposconceptnc{QuantityKindSpec}@ && @\exposidnc{is-specialization-of}@(type_remove_const(type_of(^T::@\exposidnc{quantity-spec}@)), + ^derived_quantity_spec))); + +template +concept @\defexposconceptnc{SameQuantitySpec}@ = + @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && (QS1 == QS2); + +template +concept @\defexposconceptnc{ChildQuantitySpecOf}@ = (@\exposidnc{is-child-of}@(Child, Parent)); + +template +concept @\defexposconceptnc{NestedQuantityKindSpecOf}@ = @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && + !@\exposconceptnc{SameQuantitySpec}@ && + @\exposconceptnc{ChildQuantitySpecOf}@; + +template +concept @\defexposconceptnc{QuantitySpecConvertibleTo}@ = + @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && implicitly_convertible(From, To); + +template +concept @\defexposconceptnc{QuantitySpecExplicitlyConvertibleTo}@ = + @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && explicitly_convertible(From, To); + +template +concept @\defexposconceptnc{QuantitySpecCastableTo}@ = + @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && castable(From, To); + +template +concept @\deflibconcept{QuantitySpecOf}@ = + @\libconcept{QuantitySpec}@ && @\libconcept{QuantitySpec}@ && @\exposconceptnc{QuantitySpecConvertibleTo}@ && + !@\exposconceptnc{NestedQuantityKindSpecOf}@ && + (@\exposconceptnc{QuantityKindSpec}@ || !@\exposconceptnc{NestedQuantityKindSpecOf}@); +\end{itemdecl} + +\rSec2[qty.spec.types]{Types} + +\begin{codeblock} +namespace mp_units { + +template<@\libconcept{QuantitySpec}@ QS, @\libconcept{Unit}@ U> + requires(!@\exposconceptnc{AssociatedUnit}@) || @\libconcept{UnitOf}@ +consteval @\libconcept{Reference}@ auto @\exposidnc{make-reference}@(QS, U u) +{ + if constexpr (@\exposconceptnc{QuantityKindSpec}@) + return u; + else + return reference{}; +} + +consteval quantity_character @\exposidnc{common-quantity-character}@( + std::initializer_list args) +{ + return max(args); +} + +template +consteval quantity_character @\exposidnc{derived-quantity-character}@(type_list, type_list) +{ + constexpr quantity_character num = + @\exposidnc{common-quantity-character}@({quantity_character::scalar, [:@\exposidnc{expr-type}@(^Qs1):]::character...}); + constexpr quantity_character den = + @\exposidnc{common-quantity-character}@({quantity_character::scalar, [:@\exposidnc{expr-type}@(^Qs2):]::character...}); + if constexpr (num == den) + return quantity_character::scalar; + else + return @\exposidnc{common-quantity-character}@({num, den}); +} + +consteval quantity_character @\exposidnc{quantity-character-init}@(quantity_character ch, + std::initializer_list args) +{ + auto it = + std::ranges::find_if(args, [](auto arg) { return type_of(arg) == ^quantity_character; }); + if (it != args.end()) return extract(*it); + return ch; +} + +template<@\defexposconceptnc{NamedQuantitySpec}@ Q> + requires requires { Q::dimension; } +using @\exposidnc{to-dimension}@ = [:type_remove_const(type_of(^Q::dimension)):]; + +struct @\exposidnc{quantity-spec-interface-base}@ { + friend consteval @\libconcept{QuantitySpec}@ auto operator*(@\libconcept{QuantitySpec}@ auto lhs, @\libconcept{QuantitySpec}@ auto rhs) + { + return @\exposidnc{clone-kind-of}@(@\exposidnc{expr-multiply}@( + @\exposidnc{remove-kind}@(lhs), @\exposidnc{remove-kind}@(rhs))); + } + + friend consteval @\libconcept{QuantitySpec}@ auto operator/(@\libconcept{QuantitySpec}@ auto lhs, @\libconcept{QuantitySpec}@ auto rhs) + { + return @\exposidnc{clone-kind-of}@(@\exposidnc{expr-divide}@( + @\exposidnc{remove-kind}@(lhs), @\exposidnc{remove-kind}@(rhs))); + } + + friend consteval bool operator==(@\libconcept{QuantitySpec}@ auto lhs, @\libconcept{QuantitySpec}@ auto rhs) + { + return ^decltype(lhs) == ^decltype(rhs); + } +}; + +struct @\exposidnc{quantity-spec-interface}@ : @\exposidnc{quantity-spec-interface-base}@ { + template U> + consteval @\libconcept{Reference}@ auto operator[](this Self self, U u) + { + return @\exposidnc{make-reference}@(self, u); + } + + template + requires @\exposconceptnc{QuantitySpecExplicitlyConvertibleTo}@ + constexpr @\libconcept{Quantity}@ auto operator()(this Self self, FwdQ&& q) + { + return quantity{std::forward(q).@\exposidnc{numerical-value}@, @\exposidnc{make-reference}@(self, Q::unit)}; + } +}; + +inline constexpr struct @\libglobal{is_kind}@ { +} @\libglobal{is_kind}@; + +template +concept @\defexposconceptnc{quantity-spec-1-arg}@ = + (type_of(^Arg) == ^quantity_character) && + std::ranges::contains(enumerators_of(^quantity_character), value_of(^Arg)); + +template +concept @\defexposconceptnc{quantity-spec-2-args}@ = (@\exposconceptnc{quantity-spec-1-arg}@ && value_of(^Rhs) == is_kind) || + (@\exposconceptnc{quantity-spec-1-arg}@ && value_of(^Lhs) == is_kind); + +template +concept @\defexposconceptnc{quantity-spec-args}@ = + (... && !@\libconcept{QuantitySpec}@) && + ((sizeof...(Args) == 0) || // + (sizeof...(Args) == 1 && @\exposconceptnc{quantity-spec-1-arg}@) || + (sizeof...(Args) == 2 && @\exposconceptnc{quantity-spec-2-args}@ && MaxArgs == 2)); + +template<@\exposconceptnc{BaseDimension}@ auto Dim, auto... Args> + requires @\exposconceptnc{quantity-spec-args}@<1, Args...> +struct @\libspec{quantity_spec}{\ecname{BaseDimension}}@ : @\exposidnc{quantity-spec-interface}@ { + using @\exposidnc{base-type}@ = quantity_spec; + static constexpr @\exposconceptnc{BaseDimension}@ auto dimension = Dim; + static constexpr quantity_character character = + @\exposidnc{quantity-character-init}@(quantity_character::scalar, {^Args...}); +}; + +template<@\exposconceptnc{DerivedQuantitySpec}@ auto Eq, auto... Args> + requires @\exposconceptnc{quantity-spec-args}@<1, Args...> +struct @\libspec{quantity_spec}{\ecname{DerivedQuantitySpec}}@ : @\exposidnc{quantity-spec-interface}@ { + using @\exposidnc{base-type}@ = quantity_spec; + static constexpr auto @\exposidnc{equation}@ = Eq; + static constexpr @\libconcept{Dimension}@ auto dimension = Eq.dimension; + static constexpr quantity_character character = + @\exposidnc{quantity-character-init}@(Eq.character, {^Args...}); +}; + +template<@\exposconceptnc{NamedQuantitySpec}@ auto QS, auto... Args> + requires @\exposconceptnc{quantity-spec-args}@<2, Args...> +struct @\libspec{quantity_spec}{\ecname{NamedQuantitySpec}}@ : @\exposidnc{quantity-spec-interface}@ { + using @\exposidnc{base-type}@ = quantity_spec; + static constexpr auto @\exposidnc{parent}@ = QS; // clang-format off + static constexpr auto @\exposidnc{equation}@ = @\exposidnc{parent}@.@\exposidnc{equation}@; // \expos, present only + // if the \fakegrammarterm{qualified-id} \tcode{\exposidnc{parent}.\exposidnc{equation}} is valid and denotes an object + static constexpr @\libconcept{Dimension}@ auto dimension = @\exposidnc{parent}@.dimension; // clang-format on + static constexpr quantity_character character = + @\exposidnc{quantity-character-init}@(QS.character, {^Args...}); +}; + +template<@\exposconceptnc{NamedQuantitySpec}@ auto QS, @\exposconceptnc{DerivedQuantitySpec}@ auto Eq, auto... Args> + requires @\exposconceptnc{quantity-spec-args}@<2, Args...> && @\exposconceptnc{QuantitySpecExplicitlyConvertibleTo}@ +struct @\libspec{quantity_spec}{<\ecname{NamedQuantitySpec}, \ecname{DerivedQuantitySpec}>}@ : @\exposidnc{quantity-spec-interface}@ { + using @\exposidnc{base-type}@ = quantity_spec; + static constexpr auto @\exposidnc{parent}@ = QS; + static constexpr auto @\exposidnc{equation}@ = Eq; + static constexpr @\libconcept{Dimension}@ auto dimension = @\exposidnc{parent}@.dimension; + static constexpr quantity_character character = + @\exposidnc{quantity-character-init}@(Eq.character, {^Args...}); +}; + +} +\end{codeblock} + +\pnum +A \defnadj{named}{quantity} is a type that models \exposconceptnc{NamedQuantitySpec}. +A specialization of \tcode{quantity_spec} is used as a base type when defining a named quantity. + +\pnum +In the following descriptions, let \tcode{Q} be a named quantity defined used an alluded signature. +The identifier of \tcode{Q} represents its quantity name\irefiev{112-01-02}. + +\pnum +Let \tcode{set} be an enumerator of \tcode{quantity_character}. +The possible arguments to \tcode{quantity_spec} are +\begin{itemize} +\item +$(\text{A base quantity dimension}, \opt{\tcode{set}})$, +\item +$(\text{A quantity calculus}, \opt{\tcode{set}})$, +\item +$(\text{A named quantity}, \opt{\tcode{set}}, \opt{\tcode{is_kind}})$, and +\item +$(\text{A named quantity}, \text{a quantity calculus}, \opt{\tcode{set}}, \opt{\tcode{is_kind}})$. +\end{itemize} + +\pnum +If the first argument is a base quantity dimension, +then \tcode{Q} is that base quantity\irefiev{112-01-08}. +If an argument is a quantity calculus\irefiev{112-01-30} \placeholder{C}, +then $\tcode{Q} = \placeholder{C}$ (a quantity equation\irefiev{112-01-31}). +If the first argument is a named quantity, +then \tcode{Q} is of its kind\irefiev{112-01-04}. + +\pnum +\tcode{set} is the set of the numerical value of \tcode{Q}. +If not specified, then it defaults to \tcode{scalar} for the first signature, +and to that of the quantity argument that comes before the \tcode{set} otherwise. +\tcode{is_kind} specifies \tcode{Q} to start a new hierarchy tree of a kind. + +\pnum +\begin{example} +\begin{codeblock} +// The first signature defines a base quantity. +inline constexpr struct length final : quantity_spec { +} length; // Length is a base quantity. + +// The second signature defines a derived quantity. +inline constexpr struct area final : quantity_spec(length)> { +} area; // An area equals length by length. + +// The third and fourth signatures add a leaf to a hierarchy of kinds. +inline constexpr struct width final : quantity_spec { +} width; // Width is a kind of length. + +// The fourth signature also refines the calculus required for implicit conversions. +inline constexpr struct angular_measure final : + quantity_spec { +} angular_measure; // Requires an arc length per radius, not just any quantity of dimension one. +\end{codeblock} +\end{example} + +\begin{codeblock} +namespace mp_units { + +template<@\exposconceptnc{DerivedQuantitySpecExpr}@... Expr> +struct @\exposidnc{derived-quantity-spec-impl}@ : + @\exposidnc{quantity-spec-interface}@, + @\exposidnc{expr-fractions}@ { + using @\exposidnc{base-type}@ = @\exposidnc{derived-quantity-spec-impl}@; + using @\exposidnc{base}@ = @\exposidnc{expr-fractions}@; + + static constexpr @\libconcept{Dimension}@ auto dimension = + @\exposidnc{expr-map}@<@\exposidnc{to-dimension}@, derived_dimension, struct dimension_one>(@\exposidnc{base}@{}); + static constexpr quantity_character character = + @\exposidnc{derived-quantity-character}@(typename @\exposidnc{base}@::@\exposidnc{num}@{}, typename @\exposidnc{base}@::@\exposidnc{den}@{}); +}; + +template<@\exposconceptnc{DerivedQuantitySpecExpr}@... Expr> +struct @\libglobal{derived_quantity_spec}@ final : @\exposidnc{derived-quantity-spec-impl}@ {}; + +} +\end{codeblock} + +\pnum +A specialization of \tcode{derived_quantity_spec} +is returned from a quantity calculus not equal to a named quantity. +\begin{example} +\begin{codeblock} +auto area = pow<2>(length); +static_assert(decltype(area) == ^derived_quantity_spec>); +\end{codeblock} +\end{example} + +\begin{codeblock} +namespace mp_units { -template -constexpr bool is_scalar = - std::is_floating_point_v || (std::is_integral_v && !is_same_v); +inline constexpr struct @\libglobal{dimensionless}@ final : quantity_spec> { +} @\libglobal{dimensionless}@; -template -constexpr bool is_vector = false; +} +\end{codeblock} -template -constexpr bool is_tensor = false; +\pnum +\tcode{dimensionless} represents the base quantity of dimension one\irefiev{112-01-13}. -template -struct quantity_values; +\begin{codeblock} +namespace mp_units { template -struct quantity_like_traits; +concept @\defexposconceptnc{QuantitySpecWithNoSpecifiers}@ = @\exposconceptnc{NamedQuantitySpec}@ || @\exposconceptnc{DerivedQuantitySpec}@; -template -struct quantity_point_like_traits; +template + requires @\exposconceptnc{QuantitySpecWithNoSpecifiers}@ && @\exposconceptnc{SameQuantitySpec}@<@\exposidnc{get-kind-tree-root}@(Q{}), Q{}> +struct @\libglobal{kind_of_}@ final : Q::@\exposidnc{base-type}@ { + using @\exposidnc{base-type}@ = kind_of_; + static constexpr auto @\exposidnc{quantity-spec}@ = Q{}; +}; -// \ref{qty.concepts}, concepts +} +\end{codeblock} -template -concept @\deflibconcept{some_reference}@ = template_of(^std::remove_cvref_t) == ^reference; +\pnum +\tcode{kind_of} specifies \tcode{Q} to be treated as a quantity kind. -template -concept representation = @\seebelownc@; +\begin{codeblock} +namespace mp_units { -template -concept representation_of = @\seebelownc@; +template<@\libconcept{QuantitySpec}@ auto... From, @\libconcept{QuantitySpec}@ Q> +consteval @\libconcept{QuantitySpec}@ auto @\exposidnc{clone-kind-of}@(Q q) +{ + if constexpr ((... && @\exposconceptnc{QuantityKindSpec}@)) + return kind_of; + else + return q; +} -template -concept some_quantity_spec = @\seebelownc@; +template<@\libconcept{QuantitySpec}@ Q> +consteval auto @\exposidnc{remove-kind}@(Q q) +{ + if constexpr (@\exposconceptnc{QuantityKindSpec}@) + return Q::@\exposidnc{quantity-spec}@; + else + return q; +} -// \ref{qty.types}, types +} +\end{codeblock} -template -struct quantity_spec; // \notdef +\begin{itemdecl} +template + requires(Den != 0) +consteval @\libconcept{QuantitySpec}@ auto @\liboverload{pow}{\cname{QuantitySpec}}@(Q q); +\end{itemdecl} -template -struct kind_of_; // \notdef +\begin{itemdescr} +\pnum +Computes $\tcode{q}^{\tcode{Num}/\tcode{Den}}$. -template<@\unspec@... Expr> -struct derived_quantity_spec; +\pnum +\effects +Equivalent to: +\begin{codeblock} +if constexpr (Num == 0 || q == dimensionless) + return dimensionless; +else if constexpr (@\exposidnc{ratio}@{Num, Den} == 1) + return q; +else if constexpr (@\exposconceptnc{DerivedQuantitySpec}@) + return @\exposidnc{clone-kind-of}@( + @\exposidnc{expr-pow}@(@\exposidnc{remove-kind}@(q))); +else if constexpr (Den == 1) + return @\exposidnc{clone-kind-of}@(derived_quantity_spec>{}); +else + return @\exposidnc{clone-kind-of}@( + derived_quantity_spec>{}); +\end{codeblock} +\end{itemdescr} -// \ref{qty.type}, class template \tcode{quantity} -export template<@\libconcept{some_reference}@ auto R, - @\libconcept{representation_of}@ Rep = double> -class quantity; +\begin{itemdecl} +consteval bool @\libglobal{implicitly_convertible}@(QuantitySpec auto from, QuantitySpec auto to); +\end{itemdecl} -// \ref{qty.point.type}, class template \tcode{quantity_point} -template<@\unspec@> -class quantity_point; +\begin{itemdescr} +\pnum +\returns +TBD. +\end{itemdescr} + +\begin{itemdecl} +consteval bool @\libglobal{explicitly_convertible}@(QuantitySpec auto from, QuantitySpec auto to); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +TBD. +\end{itemdescr} + +\begin{itemdecl} +consteval bool @\libglobal{castable}@(QuantitySpec auto from, QuantitySpec auto to); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +TBD. +\end{itemdescr} + +\begin{itemdecl} +consteval bool @\libglobal{interconvertible}@(QuantitySpec auto qs1, QuantitySpec auto qs2); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{implicitly_convertible(qs1, qs2) \&\& implicitly_convertible(qs2, qs1)}. +\end{itemdescr} + +\begin{codeblock} +namespace mp_units { + +template<@\libconcept{QuantitySpec}@ Q> + requires requires(Q q) { @\exposidnc{get-kind-tree-root}@(q); } +using @\exposidnc{to-kind}@ = decltype(@\exposidnc{get-kind-tree-root}@(Q{})); + +template<@\libconcept{QuantitySpec}@ Q> +consteval @\libconcept{QuantitySpec}@ auto @\exposidnc{get-kind-tree-root}@(Q q) +{ + auto defined_as_kind = [](quantity_spec) { + return (... || type_of(^Args) == ^struct is_kind); + }; + + if constexpr (@\exposconceptnc{QuantityKindSpec}@) { + return @\exposidnc{remove-kind}@(q); + } else if constexpr (defined_as_kind(q)) { + return q; + } else if constexpr (requires { Q::@\exposidnc{parent}@; }) { + return @\exposidnc{get-kind-tree-root}@(Q::@\exposidnc{parent}@); + } else if constexpr (@\exposconceptnc{DerivedQuantitySpec}@) { + return @\exposidnc{expr-map}@<@\exposidnc{to-kind}@, derived_quantity_spec, struct dimensionless>(q); + } else { + return q; + } +} } \end{codeblock} -\rSec1[mp.units.systems.syn]{Module \tcode{mp_units.systems} synopsis} -\indexmodule{mp_units.systems}% -\begin{codeblock} -export module mp_units.systems; +\begin{itemdecl} +template<@\libconcept{QuantitySpec}@ Q> +consteval @\exposconceptnc{QuantityKindSpec}@ auto @\libglobal{get_kind}@(Q q); +\end{itemdecl} -export import mp_units.core; -import std; +\begin{itemdescr} +\pnum +\returns +\tcode{kind_of<\exposidnc{get-kind-tree-root}(q)>}. +\end{itemdescr} -export namespace mp_units { +\begin{itemdecl} +consteval @\libconcept{QuantitySpec}@ auto @\libglobal{get_common_quantity_spec}@(@\libconcept{QuantitySpec}@ auto... qs) + requires @\seebelownc@; +\end{itemdecl} -} +\begin{itemdescr} +\pnum +Let +\begin{itemize} +\item +\tcode{q1} be \tcode{qs...[0]}, +\item +\tcode{q2} be \tcode{qs...[1]}, +\item +\tcode{Q1} be \tcode{decltype(q1)}, +\item +\tcode{Q2} be \tcode{decltype(q2)}, and +\item +\tcode{rest} be a pack denoting the elements of \tcode{qs} without \tcode{q1} and \tcode{q2}. +\end{itemize} + +\pnum +The expression in the \fakegrammarterm{requires-clause} is equivalent to: +\begin{codeblock} +(sizeof...(qs) != 0 && + (sizeof...(qs) == 1 || + (sizeof...(qs) == 2 && + (@\exposconceptnc{QuantitySpecConvertibleTo}@<@\exposidnc{get-kind-tree-root}@(Q1{}), @\exposidnc{get-kind-tree-root}@(Q2{})> || + @\exposconceptnc{QuantitySpecConvertibleTo}@<@\exposidnc{get-kind-tree-root}@(Q2{}), @\exposidnc{get-kind-tree-root}@(Q1{})>)) || + requires { get_common_quantity_spec(get_common_quantity_spec(q1, q2), rest...); })) \end{codeblock} -\rSec1[qty.helpers]{Helpers} +\pnum +\effects +Equivalent to: +\begin{codeblock} +if constexpr (sizeof...(qs) == 1) + return q1; +else if constexpr (sizeof...(qs) == 2) { + using QQ1 = decltype(remove_kind(q1)); + using QQ2 = decltype(remove_kind(q2)); + + if constexpr (^Q1 == ^Q2) + return q1; + else if constexpr (@\exposconceptnc{NestedQuantityKindSpecOf}@) + return QQ1{}; + else if constexpr (@\exposconceptnc{NestedQuantityKindSpecOf}@) + return QQ2{}; + else if constexpr ((@\exposconceptnc{QuantityKindSpec}@ && !@\exposconceptnc{QuantityKindSpec}@) || + (@\exposconceptnc{DerivedQuantitySpec}@ && @\exposconceptnc{NamedQuantitySpec}@ && + implicitly_convertible(q1, q2))) + return q2; + else if constexpr ((!@\exposconceptnc{QuantityKindSpec}@ && @\exposconceptnc{QuantityKindSpec}@) || + (@\exposconceptnc{NamedQuantitySpec}@ && @\exposconceptnc{DerivedQuantitySpec}@ && + implicitly_convertible(q2, q1))) + return q1; + else if constexpr (constexpr auto common_base = @\exposidnc{get-common-base}@(q1, q2)) + return [:*common_base:]; + else if constexpr (implicitly_convertible(q1, q2)) + return q2; + else if constexpr (implicitly_convertible(q2, q1)) + return q1; + else if constexpr (implicitly_convertible(@\exposidnc{get-kind-tree-root}@(q1), @\exposidnc{get-kind-tree-root}@(q2))) + return @\exposidnc{get-kind-tree-root}@(q2); + else + return @\exposidnc{get-kind-tree-root}@(q1); +} else + return get_common_quantity_spec(get_common_quantity_spec(q1, q2), rest...); +\end{codeblock} +\end{itemdescr} + +\pnum +Let the quantity kind hierarchy of \tcode{q} be the tuple +$H(\tcode{q}) = ( \tcode{q}, \tcode{q.\exposidnc{parent}}, \tcode{q.\exposidnc{parent}.\exposidnc{parent}}, \ldots )$. \begin{itemdecl} -consteval bool @\exposidnc{converts-to-base-subobject-of}@(std::meta type, std::meta template_name); +template<@\libconcept{QuantitySpec}@ A, @\libconcept{QuantitySpec}@ B> +consteval std::optional @\exposidnc{get-common-base}@(A a, B b); \end{itemdecl} \begin{itemdescr} \pnum -\expects -\tcode{is_type(type) \&\& is_template(template_name)} is \tcode{true}. +Let +\begin{itemize} +\item +$a_s$ be the number of elements in $H(\tcode{a})$, +\item +$b_s$ be the number of elements in $H(\tcode{b})$, +\item +$s$ be $min(a_s, b_s)$, +\item +$A$ be a tuple of the last $s$ elements of $H(\tcode{a})$, and +\item +$B$ be a tuple of the last $s$ elements of $H(\tcode{b})$. +\end{itemize} + +\pnum +\effects +Looks for $x$, the first pair-wise equal element in $A$ and $B$. \pnum \returns -\tcode{true} if -\tcode{[:type:]} has an unambiguous and accessible base -that is a specialization of \tcode{[:template_name:]}, and -\tcode{false} otherwise. +A reflection of $x$, if found, and \tcode{std::nullopt} otherwise. +\end{itemdescr} + +\begin{itemdecl} +template<@\libconcept{QuantitySpec}@ Child, @\libconcept{QuantitySpec}@ Parent> +consteval bool @\exposidnc{is-child-of}@(Child ch, Parent p); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +If $H(\tcode{p})$ has more elements than $H(\tcode{ch})$, returns \tcode{false}. +Otherwise, let $C$ be a tuple of the last $s$ elements of $H(\tcode{ch})$, +where $s$ is the number of elements in $H(\tcode{p})$. +Returns \tcode{$C_0$ == p}. +\end{itemdescr} + +\rSec1[qty.mag]{Magnitude} + +\rSec1[qty.unit]{Unit} + +\begin{itemdecl} +template +concept @\deflibconcept{Unit}@ = @\exposconceptnc{tag-type}@ && std::@\stdconcept{derived_from}@; +template +concept @\deflibconcept{UnitOf}@ = @\unspecnc@; +template +concept @\defexposconcept{AssociatedUnit}@ = @\unspecnc@; +\end{itemdecl} + +\rSec1[qty.ref]{Reference} + +\begin{itemdecl} +template +concept @\deflibconcept{Reference}@ = @\exposconceptnc{AssociatedUnit}@ || @\exposidnc{is-specialization-of}@(^T, ^reference); +\end{itemdecl} + +\rSec1[qty.rep]{Representation} + +\rSec2[qty.rep.traits]{Traits} + +\rSec3[qty.fp.traits]{Floating-point} + +\begin{itemdecl} +template +struct @\exposidnc{actual-value-type}@ : @\exposidnc{cond-value-type}@ {}; // see \refcpp{readable.traits} + +template + requires(!type_is_pointer(^T) && !type_is_array(^T)) && + requires { typename std::indirectly_readable_traits::value_type; } +struct @\exposidnc{actual-value-type}@ : std::indirectly_readable_traits {}; + +template +using @\exposidnc{actual-value-type-t}@ = @\exposidnc{actual-value-type}@::value_type; + +template +constexpr bool @\libglobal{treat_as_floating_point}@ = + std::chrono::treat_as_floating_point_v || + std::chrono::treat_as_floating_point_v<@\exposidnc{actual-value-type-t}@>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +The quantity types\iref{term.quantity.type} use \tcode{treat_as_floating_point} +to help determine whether implicit conversions are allowed among them. + +\pnum +\remarks +Pursuant to \refcpp{namespace.std}\iref{spec.ext}, +users may specialize \tcode{treat_as_floating_point} +for cv-unqualified program-defined types. +Such specializations shall be usable in constant expressions\irefcpp{expr.const} +and have type \tcode{const bool}. \end{itemdescr} -\rSec1[qty.traits]{Traits} +\rSec3[qty.set.traits]{Set} \begin{itemdecl} template constexpr bool @\libglobal{is_scalar}@ = - std::is_floating_point_v || (std::is_integral_v && !is_same_v); + type_is_floating_point(^Rep) || (type_is_integral(^Rep) && ^Rep != ^bool); + +template +constexpr bool @\libglobal{is_complex}@ = false; template constexpr bool @\libglobal{is_vector}@ = false; @@ -146,121 +1452,397 @@ \end{itemdecl} \begin{itemdescr} +\pnum +Some quantities are defined as having a numerical value\irefiev{112-01-29} of a specific set\irefiev{102-01-02}. +A quantity type\iref{term.quantity.type} \tcode{Q} uses these traits +to determine the set \tcode{Q::rep} should represent. + \pnum \remarks Pursuant to \refcpp{namespace.std}\iref{spec.ext}, -users may specialize \tcode{is_scalar}, \tcode{is_vector}, and \tcode{is_tensor} to \tcode{true} +users may specialize \tcode{is_scalar}, \tcode{is_complex}, \tcode{is_vector}, and \tcode{is_tensor} to \tcode{true} for cv-unqualified program-defined types which respectively represent a scalar\irefiev{102-02-18}, +a complex number\irefiev{102-02-09}, a vector\irefiev{102-03-04}, and % FIXME Undefined term. a tensor, and \tcode{false} for types which respectively do not. +Such specializations shall be usable in constant expressions\irefcpp{expr.const} +and have type \tcode{const bool}. \end{itemdescr} -\rSec1[qty.concepts]{Concepts} +\rSec2[qty.rep.concepts]{Concepts} \begin{itemdecl} -export template -concept @\deflibconcept{representation}@ = - (is_scalar || is_vector || is_tensor)&&std::regular && @\exposidnc{scalable}@; -\end{itemdecl} +template +concept @\defexposconceptnc{CommonTypeWith}@ = std::@\stdconcept{same_as}@, std::common_type_t> && + std::@\stdconcept{constructible_from}@, T> && + std::@\stdconcept{constructible_from}@, U>; -\begin{itemdecl} -export template -concept @\deflibconcept{representation_of}@ = - @\libconcept{representation}@ && ((Ch == quantity_character::scalar && is_scalar) || +template +concept @\defexposconceptnc{ScalableNumber}@ = std::@\stdconcept{regular_invocable}@, T, U> && + std::@\stdconcept{regular_invocable}@, T, U>; + +template +concept @\defexposconceptnc{CastableNumber}@ = + @\exposconceptnc{CommonTypeWith}@ && @\exposconceptnc{ScalableNumber}@>; + +template +concept @\defexposconceptnc{Scalable}@ = + @\exposconceptnc{CastableNumber}@ || + (@\exposconceptnc{CastableNumber}@<@\exposidnc{actual-value-type-t}@> && + @\exposconceptnc{ScalableNumber}@, std::intmax_t>>); + +template +concept @\defexposconceptnc{WeaklyRegular}@ = std::@\stdconcept{copyable}@ && std::@\stdconcept{equality_comparable}@; + +template +concept @\deflibconcept{Representation}@ = (is_scalar || is_complex || is_vector || is_tensor) && + @\exposconceptnc{WeaklyRegular}@ && @\exposconceptnc{Scalable}@; + +template +concept @\deflibconcept{RepresentationOf}@ = + @\libconcept{Representation}@ && ((Ch == quantity_character::scalar && is_scalar) || + (Ch == quantity_character::complex && is_complex) || (Ch == quantity_character::vector && is_vector) || (Ch == quantity_character::tensor && is_tensor)); \end{itemdecl} -% FIXME Despite the `some_` prefix, it doesn't conform to the convention -% `template_of(^std::remove_cvref_t) == ^template_name`. +\rSec1[qty.types]{Quantity types} + +\rSec2[qty.traits]{Traits} + +\rSec3[qty.val.traits]{Values} + +\begin{codeblock} +namespace mp_units { + +template +struct @\libglobal{quantity_values}@ : std::chrono::duration_values { + static constexpr Rep one() noexcept; +}; + +} +\end{codeblock} + +\pnum +The requirements on \tcode{std::chrono::duration_values}\irefcpp{time.traits.duration.values} +also apply to \tcode{quantity_values}. + +\begin{itemdecl} +static constexpr Rep one() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +if constexpr (requires { + { std::chrono::duration_values::one() } -> std::@\stdconcept{same_as}@; + }) + return std::chrono::duration_values::one(); +else + return Rep(1); +\end{codeblock} + +\pnum +\remarks +The value returned shall be the neutral element for multiplication\irefiev{102-01-19}. +\end{itemdescr} + +\rSec3[qty.compat.traits]{Compatibility} + +\pnum +The interfaces specified in this subclause +are used by the quantity types\iref{term.quantity.type} +to specify conversions with other types representing quantities. +\ref{qty.chrono} implements them for \tcode{std::chrono::duration} and \tcode{std::chrono::time_point}. + \begin{itemdecl} +template typename Traits> +concept @\defexposconceptnc{qty-like}@ = requires(const T& qty, const Traits::rep& num) { + requires !@\exposidnc{is-specialization-of}@(^T, ^quantity); + requires !@\exposidnc{is-specialization-of}@(^T, ^quantity_point); + { Traits::to_numerical_value(qty) } -> std::@\stdconcept{same_as}@::rep>; + { Traits::from_numerical_value(num) } -> std::@\stdconcept{same_as}@; + { Traits::explicit_import } -> std::same_as; + { Traits::explicit_export } -> std::same_as; + typename std::bool_constant::explicit_import>; + typename std::bool_constant::explicit_export>; +}; + template -concept @\defexposconceptnc{named-quantity-spec}@ = - (@\exposidnc{converts-to-base-subobject-of}@(^T, ^quantity_spec) && template_of(^T) != ^kind_of_); +concept @\deflibconcept{quantity_like}@ = // + @\exposconceptnc{qty-like}@ && // + requires { + typename quantity::reference, typename quantity_like_traits::rep>; + }; template -concept @\deflibconcept{some_quantity_spec}@ = - @\exposconceptnc{named-quantity-spec}@ || - detail::IntermediateDerivedQuantitySpec || - template_of(^T) == ^kind_of; +concept @\deflibconcept{quantity_point_like}@ = + @\exposconceptnc{qty-like}@ && // + requires { + typename quantity_point::reference, + typename quantity_point_like_traits::point_origin, + typename quantity_point_like_traits::rep>; + }; \end{itemdecl} -\rSec1[qty.types]{Types} +\pnum +In the following descriptions, let +\begin{itemize} +\item +\tcode{Traits} be \tcode{quantity_like_traits} or \tcode{quantity_point_like_traits}, +\item +\tcode{Q} be a type for which \tcode{Traits} is specialized, +\item +\tcode{qty} be an lvalue of type \tcode{const Q}, and +\item +\tcode{num} be an lvalue of type \tcode{const Tratis::rep}. +\end{itemize} + +\pnum +\tcode{Q} models \tcode{\exposconceptnc{qty-like}} if and only if: +\begin{itemize} +\item +\tcode{Traits::to_numerical_value(qty)} returns the numerical value\irefiev{112-01-29} of \tcode{qty}. +\item +\tcode{Traits::from_numerical_value(num)} returns a \tcode{Q} with numerical value \tcode{num}. +\item +If \tcode{Traits} is \tcode{quantity_point_like_traits}, +both numerical values are offset from \tcode{Traits::point_origin}. +\end{itemize} + +\pnum +If the following expression is \tcode{true}, the specified conversion will be explicit. +\begin{itemize} +\item +\tcode{Traits::explicit_import} for the conversion from \tcode{Q} to a quantity type. +\item +\tcode{Traits::explicit_export} for the conversion from a quantity type to \tcode{Q}. +\end{itemize} \rSec2[qty.types.general]{General} \pnum +\label{term.quantity.type} A \defnadj{quantity}{type} is a type \tcode{\placeholder{Q}} that is a specialization of \tcode{quantity} or \tcode{quantity_point}. -\tcode{\placeholder{Q}} represents a quantity\irefiev{112-01-01} -with \tcode{\placeholder{Q}::rep} as its number -and \tcode{\placeholder{Q}::reference} as its reference. +\tcode{\placeholder{Q}} represents the value of a quantity\irefiev{112-01-28} +with \tcode{\placeholdernc{Q}::rep} as its number +and \tcode{\placeholdernc{Q}::reference} as its reference. \tcode{\placeholder{Q}} is a structural type\irefcppx{temp.param}{term.structural.type} -if \tcode{\placeholder{Q}::rep} is a structural type. +if \tcode{\placeholdernc{Q}::rep} is a structural type. \pnum -Each class template defined in subclause \ref{qty.types} -has data members and special members specified below, and -has no base classes or members other than those specified. +Each class template defined in subclauses \ref{qty} and \ref{qty.pt} +have data members and special members specified below, and +have no base classes or members other than those specified. -\rSec2[qty.type]{Class template \tcode{quantity}} +\rSec2[qty.concepts]{Quantity concepts} + +\begin{itemdecl} +template +concept @\deflibconcept{Quantity}@ = @\unspecnc@; +\end{itemdecl} + +\rSec2[qty]{Class template \tcode{quantity}} \begin{codeblock} namespace mp_units { -export template<@\libconcept{some_reference}@ auto R, - @\libconcept{representation_of}@ Rep = double> -class quantity { @\unspec@ }; +template +concept @\defexposconceptnc{same-value-as}@ = + equivalent(get_unit(R1), get_unit(R2)) && std::@\stdconcept{convertible_to}@; + +template<@\libconcept{Reference}@ auto R, @\libconcept{RepresentationOf}@ Rep = double> +class @\libglobal{quantity}@ { +public: + Rep @\exposidnc{numerical-value}@; + + // member types and values + static constexpr @\libconcept{Reference}@ auto reference = R; + static constexpr @\libconcept{QuantitySpec}@ auto quantity_spec = get_quantity_spec(reference); + static constexpr @\libconcept{Dimension}@ auto dimension = quantity_spec.dimension; + static constexpr @\libconcept{Unit}@ auto unit = get_unit(reference); + using rep = Rep; + + // static member functions + + static constexpr quantity zero() noexcept + requires requires { quantity_values::zero(); } + { + return {quantity_values::zero(), R}; + } + + static constexpr quantity one() noexcept + requires requires { quantity_values::one(); } + { + return {quantity_values::one(), R}; + } + + static constexpr quantity min() noexcept + requires requires { quantity_values::min(); } + { + return {quantity_values::min(), R}; + } + + static constexpr quantity max() noexcept + requires requires { quantity_values::max(); } + { + return {quantity_values::max(), R}; + } + + // construction, assignment, destruction + + quantity() = default; + quantity(const quantity&) = default; + quantity(quantity&&) = default; + ~quantity() = default; + + template + requires @\exposconceptnc{same-value-as}@ + constexpr quantity(FwdValue&& v, R2) : @\exposidnc{numerical-value}@(std::forward(v)) + { + } +}; } \end{codeblock} +\pnum Let \tcode{\placeholder{Q}} be a specialization of \tcode{quantity}. \begin{itemize} \item -If \tcode{Rep} is a scalar, +If \tcode{Rep} is a scalar\iref{qty.set.traits}, \tcode{\placeholder{Q}} represents a scalar quantity\irefiev{102-02-19}. +% FIXME What if `Rep` is a complex number? \item -If \tcode{Rep} is a vector, +If \tcode{Rep} is a vector\iref{qty.set.traits}, \tcode{\placeholder{Q}} represents a vector\irefiev{102-03-04}. % FIXME What if `Rep` is a tensor? \end{itemize} -\rSec2[qty.point.type]{Class template \tcode{quantity_point}} +\rSec2[qty.pt.concepts]{Quantity point concepts} + +\begin{itemdecl} +template +concept @\deflibconcept{point_origin}@ = @\unspecnc@; +\end{itemdecl} + +\rSec2[qty.pt]{Class template \tcode{quantity_point}} \begin{codeblock} namespace mp_units { -export template<@\unspec@> -class quantity_point { @\unspec@ }; +template<@\unspec@> +class @\libglobal{quantity_point}@ { @\unspec@ }; } \end{codeblock} +\pnum +\label{term.quantity.point.type} A \defnadj{quantity point}{type} is a specialization of \tcode{quantity_point}. Let \tcode{\placeholder{Q}} be a quantity point type. \tcode{\placeholdernc{Q}::point_origin} represents -the origin point of a position vector\irefiev{102-03-15}. +the origin point of a position vector\irefiev{102-03-15} +or of a component\irefiev{102-03-10} thereof. \begin{itemize} \item -If \tcode{Rep} is a scalar, +If \tcode{Rep} is a scalar\iref{qty.set.traits}, \tcode{\placeholder{Q}} represents the scalar quantity\irefiev{102-02-19} of a position vector. +% FIXME What if `Rep` is a complex number? \item -If \tcode{Rep} is a vector, +If \tcode{Rep} is a vector\iref{qty.set.traits}, \tcode{\placeholder{Q}} represents a position vector. % FIXME What if `Rep` is a tensor? \end{itemize} -\rSec1[qty.compat]{Compatibility} - -\rSec1[qty.one]{Dimension one} - \rSec1[qty.systems]{Systems} \rSec1[qty.chrono]{\tcode{std::chrono} compatibility} + +\begin{codeblock} +namespace mp_units { + +template +consteval auto @\exposidnc{time-unit-from-chrono-period}@() +{ + using namespace si; + + if constexpr (is_same_v) + return nano; + else if constexpr (is_same_v) + return micro; + else if constexpr (is_same_v) + return milli; + else if constexpr (is_same_v) + return second; + else if constexpr (is_same_v) + return minute; + else if constexpr (is_same_v) + return hour; + else if constexpr (is_same_v) + return day; + else if constexpr (is_same_v) + return mag<7> * day; + else + return mag_ratio * second; +} + +template +struct @\libspec{quantity_like_traits}{std::chrono::duration}@> { + static constexpr auto reference = @\exposidnc{time-unit-from-chrono-period}@(); + using rep = Rep; + + static constexpr bool explicit_import = false; + static constexpr rep to_numerical_value(const std::chrono::duration& q) noexcept( + std::is_nothrow_copy_constructible_v) + { + return q.count(); + } + + static constexpr bool explicit_export = false; + static constexpr std::chrono::duration from_numerical_value( + const rep& v) noexcept(std::is_nothrow_copy_constructible_v) + { + return std::chrono::duration(v); + } +}; + +template +struct @\libglobal{chrono_point_origin_}@ final : absolute_point_origin { + using clock = Clock; +}; + +template +struct @\libspec{quantity_point_like_traits}{std::chrono::time_point}@< + std::chrono::time_point>> { + using @\exposidnc{Tp}@ = std::chrono::time_point>; + static constexpr auto reference = @\exposidnc{time-unit-from-chrono-period}@(); + static constexpr auto point_origin = chrono_point_origin; + using rep = Rep; + + static constexpr bool explicit_import = false; + static constexpr rep to_numerical_value(const @\exposid{Tp}@& tp) noexcept( + std::is_nothrow_copy_constructible_v) + { + return tp.time_since_epoch().count(); + } + + static constexpr bool explicit_export = false; + static constexpr @\exposidnc{Tp}@ from_numerical_value(const rep& v) noexcept( + std::is_nothrow_copy_constructible_v) + { + return @\exposidnc{Tp}@(std::chrono::duration(v)); + } +}; + +} +\end{codeblock}