diff --git a/HEAD/404.html b/HEAD/404.html index 65990413a..390b950a8 100644 --- a/HEAD/404.html +++ b/HEAD/404.html @@ -19,7 +19,7 @@ - + @@ -27,7 +27,7 @@ - + @@ -218,7 +218,7 @@
- +
mpusz/mp-units @@ -387,7 +387,7 @@
- +
mpusz/mp-units diff --git a/HEAD/api_reference/gen/defs.html b/HEAD/api_reference/gen/defs.html index f60d1fae2..52e5c7363 100644 --- a/HEAD/api_reference/gen/defs.html +++ b/HEAD/api_reference/gen/defs.html @@ -1,11 +1,11 @@ -[defs]

3 Terms and definitions [defs]

For the purposes of this document, +[defs]

3 Terms and definitions [defs]

For the purposes of this document, the terms and definitions given in IEC 60050-102:2007/AMD3:2021, IEC 60050-112:2010/AMD2:2020, ISO 80000-2:2019, and N4971, -and the following apply.
ISO and IEC maintain terminology databases +and the following apply.
ISO and IEC maintain terminology databases for use in standardization at the following addresses: diff --git a/HEAD/api_reference/gen/full.html b/HEAD/api_reference/gen/full.html index 39514cc4a..faf3deff1 100644 --- a/HEAD/api_reference/gen/full.html +++ b/HEAD/api_reference/gen/full.html @@ -1,4 +1,4 @@ -14882

1 Scope [scope]

This document describes the contents of the mp-units library.

2 References [refs]

The following documents are referred to in the text +14882

1 Scope [scope]

This document describes the contents of the mp-units library.

2 References [refs]

The following documents are referred to in the text in such a way that some or all of their content constitutes requirements of this document.
For dated references, only the edition cited applies.
For undated references, the latest edition of the referenced document @@ -6,29 +6,29 @@ Amendment 3 — International Electrotechnical Vocabulary (IEV) — Part 102: Mathematics — General concepts and linear algebra
  • IEC 60050-112:2010/AMD2:2020, Amendment 2 — International Electrotechnical Vocabulary (IEV) — -Part 112: Quantities and units
  • ISO 80000 (all parts), Quantities and units
  • The C++ Standards Committee.
    N4971: Working Draft, Standard for Programming Language C++.
    Edited by Thomas Köppe.
    Available from: https://wg21.link/N4971
  • The C++ Standards Committee.
    SD-8: Standard Library Compatibility.
    Edited by Bryce Lelbach.
    Available from: https://wg21.link/SD8
  • 3 Terms and definitions [defs]

    For the purposes of this document, +Part 112: Quantities and units
  • ISO 80000 (all parts), Quantities and units
  • The C++ Standards Committee.
    N4971: Working Draft, Standard for Programming Language C++.
    Edited by Thomas Köppe.
    Available from: https://wg21.link/N4971
  • The C++ Standards Committee.
    SD-8: Standard Library Compatibility.
    Edited by Bryce Lelbach.
    Available from: https://wg21.link/SD8
  • 3 Terms and definitions [defs]

    For the purposes of this document, the terms and definitions given in IEC 60050-102:2007/AMD3:2021, IEC 60050-112:2010/AMD2:2020, ISO 80000-2:2019, and N4971, -and the following apply.
    ISO and IEC maintain terminology databases +and the following apply.
    ISO and IEC maintain terminology databases for use in standardization at the following addresses: -

    4 Specification [spec]

    4.1 External [spec.ext]

    The specification of the mp-units library subsumes +

    4 Specification [spec]

    4.1 External [spec.ext]

    The specification of the mp-units library subsumes N4971, [description], N4971, [requirements], N4971, [concepts.equality], and SD-8, all assumingly amended for the context of this library.
    [Note 1: 
    This means that, non exhaustively,
    • ​::​mp_units2 is a reserved namespace, and
    • std​::​vector<mp_units​::​type> is a program-defined specialization and a library-defined specialization -from the point of view of the C++ standard library and the mp-units library, respectively.
    — end note]
    The mp-units library is not part of the C++ implementation.

    4.2 Categories [spec.cats]

    Detailed specifications for each of the components in the library are in +from the point of view of the C++ standard library and the mp-units library, respectively.
    — end note]
    The mp-units library is not part of the C++ implementation.

    4.2 Categories [spec.cats]

    Detailed specifications for each of the components in the library are in [qties][qties], -as shown in Table 1.
    Table 1: Library categories [tab:lib.cats]
    Clause
    Category
    Quantities library
    The quantities library ([qties]) -describes components for dealing with quantities.

    4.3 Modules [spec.mods]

    The mp-units library provides the +as shown in Table 1.
    Table 1: Library categories [tab:lib.cats]
    Clause
    Category
    Quantities library
    The quantities library ([qties]) +describes components for dealing with quantities.

    4.3 Modules [spec.mods]

    The mp-units library provides the mp-units modules, -shown in Table 2.
    Table 2: mp-units modules [tab:modules]
    mp_units
    mp_units.core
    mp_units.systems

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    The mp-units library reserves macro names that start with -MP_UNITSdigit-sequence_.

    5 Quantities library [qties]

    5.1 Summary [qties.summary]

    This Clause describes components for dealing with quantities, +shown in Table 2.
    Table 2: mp-units modules [tab:modules]
    mp_units
    mp_units.core
    mp_units.systems

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    The mp-units library reserves macro names that start with +MP_UNITSdigit-sequence_.

    5 Quantities library [qties]

    5.1 Summary [qties.summary]

    This Clause describes components for dealing with quantities, as summarized in Table 3.
    Table 3: Quantities library summary [tab:qties.summary]
    Subclause
    Module
    Helpers
    mp_units.core
    Traits
    Concepts
    Types
    Compatibility
    Dimension one
    Systems
    mp_units.systems
    std​::​chrono compatibility

    5.2 Module mp_units synopsis [mp.units.syn]

    export module mp_units; export import mp_units.core; @@ -109,7 +109,7 @@ }

    5.5 Helpers [qty.helpers]

    consteval bool converts-to-base-subobject-of(std::meta type, std::meta template_name); -
    Preconditions: is_type(type) && is_template(template_name) is true.
    Returns: true if +
    Preconditions: is_type(type) && is_template(template_name) is true.
    Returns: true if [:type:] has an unambiguous and accessible base that is a specialization of [:template_name:], and false otherwise.

    5.6 Traits [qty.traits]

    template<typename Rep> @@ -121,7 +121,7 @@ template<typename Rep> constexpr bool is_tensor = false; -
    Remarks: Pursuant to N4971, [namespace.std] ([spec.ext]), +
    Remarks: Pursuant to N4971, [namespace.std] ([spec.ext]), users may specialize is_scalar, is_vector, and is_tensor to true for cv-unqualified program-defined types which respectively represent @@ -145,12 +145,12 @@ named-quantity-spec<T> || detail::IntermediateDerivedQuantitySpec<T> || template_of(^T) == ^kind_of; -

    5.8 Types [qty.types]

    5.8.1 General [qty.types.general]

    5.8 Types [qty.types]

    5.8.1 General [qty.types.general]

    A quantity type is a type Q that is a specialization of quantity or quantity_point.
    Q represents a quantity (IEC 60050, 112-01-01) with Q​::​rep as its number and Q​::​reference as its reference.
    Q is a structural type (N4971, [temp.param]) -if Q​::​rep is a structural type.
    Each class template defined in subclause [qty.types] +if Q​::​rep is a structural type.
    Each class template defined in subclause [qty.types] has data members and special members specified below, and has no base classes or members other than those specified.

    5.8.2 Class template quantity [qty.type]

    namespace mp_units { diff --git a/HEAD/api_reference/gen/fulltoc.html b/HEAD/api_reference/gen/fulltoc.html index 59116005b..00f30d784 100644 --- a/HEAD/api_reference/gen/fulltoc.html +++ b/HEAD/api_reference/gen/fulltoc.html @@ -11,4 +11,4 @@ h3 { border-bottom-color: #b0b0b05a; } h4 { border-bottom-color: #b0b0b05a; } } -

    mp-units Library Reference Documentations

    (Generated on 2024-11-24 from the LaTeX sources by cxxdraft-htmlgen. This is not an ISO publication.)

    Note: this is an early draft. It's known to be incomplet and incorrekt, and it has lots of bad formatting.

    Contents

    1 Scope [scope]

    2 References [refs]

    3 Terms and definitions [defs]

    4 Specification [spec]

    4.1 External [spec.ext]

    4.2 Categories [spec.cats]

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    5 Quantities library [qties]

    5.2 Module mp_units synopsis [mp.units.syn]

    5.3 Module mp_units.core synopsis [mp.units.core.syn]

    5.4 Module mp_units.systems synopsis [mp.units.systems.syn]

    5.8 Types [qty.types]

    5.8.2 Class template quantity [qty.type]

    5.8.3 Class template quantity_point [qty.point.type]

    5.9 Compatibility [qty.compat]

    5.10 Dimension one [qty.one]

    5.12 std​::​chrono compatibility [qty.chrono]

    Index

    Index of library modules

    Index of library names

    Index of library concepts

    \ No newline at end of file +

    mp-units Library Reference Documentations

    (Generated on 2024-11-28 from the LaTeX sources by cxxdraft-htmlgen. This is not an ISO publication.)

    Note: this is an early draft. It's known to be incomplet and incorrekt, and it has lots of bad formatting.

    Contents

    1 Scope [scope]

    2 References [refs]

    3 Terms and definitions [defs]

    4 Specification [spec]

    4.1 External [spec.ext]

    4.2 Categories [spec.cats]

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    5 Quantities library [qties]

    5.2 Module mp_units synopsis [mp.units.syn]

    5.3 Module mp_units.core synopsis [mp.units.core.syn]

    5.4 Module mp_units.systems synopsis [mp.units.systems.syn]

    5.8 Types [qty.types]

    5.8.2 Class template quantity [qty.type]

    5.8.3 Class template quantity_point [qty.point.type]

    5.9 Compatibility [qty.compat]

    5.10 Dimension one [qty.one]

    5.12 std​::​chrono compatibility [qty.chrono]

    Index

    Index of library modules

    Index of library names

    Index of library concepts

    \ No newline at end of file diff --git a/HEAD/api_reference/gen/index.html b/HEAD/api_reference/gen/index.html index b84e25506..797037e73 100644 --- a/HEAD/api_reference/gen/index.html +++ b/HEAD/api_reference/gen/index.html @@ -12,4 +12,4 @@ h3 { border-bottom-color: #b0b0b05a; } h4 { border-bottom-color: #b0b0b05a; } } -

    mp-units Library Reference Documentations

    (Generated on 2024-11-24 from the LaTeX sources by cxxdraft-htmlgen. This is not an ISO publication.)

    Note: this is an early draft. It's known to be incomplet and incorrekt, and it has lots of bad formatting.

    Contents

    2 References [refs][refs]

    3 Terms and definitions [defs][defs]

    4 Specification [spec][spec]

    4.1 External [spec.ext]

    4.2 Categories [spec.cats]

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    5 Quantities library [qties][qties]

    5.2 Module mp_units synopsis [mp.units.syn]

    5.3 Module mp_units.core synopsis [mp.units.core.syn]

    5.4 Module mp_units.systems synopsis [mp.units.systems.syn]

    5.8 Types [qty.types]

    5.8.2 Class template quantity [qty.type]

    5.8.3 Class template quantity_point [qty.point.type]

    5.9 Compatibility [qty.compat]

    5.10 Dimension one [qty.one]

    5.12 std​::​chrono compatibility [qty.chrono]

    Index

    Index of library modules

    Index of library names

    Index of library concepts

    \ No newline at end of file +

    mp-units Library Reference Documentations

    (Generated on 2024-11-28 from the LaTeX sources by cxxdraft-htmlgen. This is not an ISO publication.)

    Note: this is an early draft. It's known to be incomplet and incorrekt, and it has lots of bad formatting.

    Contents

    2 References [refs][refs]

    3 Terms and definitions [defs][defs]

    4 Specification [spec][spec]

    4.1 External [spec.ext]

    4.2 Categories [spec.cats]

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    5 Quantities library [qties][qties]

    5.2 Module mp_units synopsis [mp.units.syn]

    5.3 Module mp_units.core synopsis [mp.units.core.syn]

    5.4 Module mp_units.systems synopsis [mp.units.systems.syn]

    5.8 Types [qty.types]

    5.8.2 Class template quantity [qty.type]

    5.8.3 Class template quantity_point [qty.point.type]

    5.9 Compatibility [qty.compat]

    5.10 Dimension one [qty.one]

    5.12 std​::​chrono compatibility [qty.chrono]

    Index

    Index of library modules

    Index of library names

    Index of library concepts

    \ No newline at end of file diff --git a/HEAD/api_reference/gen/qties.html b/HEAD/api_reference/gen/qties.html index 639969e4c..3873654df 100644 --- a/HEAD/api_reference/gen/qties.html +++ b/HEAD/api_reference/gen/qties.html @@ -1,4 +1,4 @@ -[qties]

    5 Quantities library [qties]

    5.1 Summary [qties.summary]

    This Clause describes components for dealing with quantities, +[qties]

    5 Quantities library [qties]

    5.1 Summary [qties.summary]

    This Clause describes components for dealing with quantities, as summarized in Table 3.
    Table 3: Quantities library summary [tab:qties.summary]
    Subclause
    Module
    Helpers
    mp_units.core
    Traits
    Concepts
    Types
    Compatibility
    Dimension one
    Systems
    mp_units.systems
    std​::​chrono compatibility

    5.2 Module mp_units synopsis [mp.units.syn]

    export module mp_units; export import mp_units.core; @@ -79,7 +79,7 @@ }

    5.5 Helpers [qty.helpers]

    consteval bool converts-to-base-subobject-of(std::meta type, std::meta template_name); -
    Preconditions: is_type(type) && is_template(template_name) is true.
    Returns: true if +
    Preconditions: is_type(type) && is_template(template_name) is true.
    Returns: true if [:type:] has an unambiguous and accessible base that is a specialization of [:template_name:], and false otherwise.

    5.6 Traits [qty.traits]

    template<typename Rep> @@ -91,7 +91,7 @@ template<typename Rep> constexpr bool is_tensor = false; -
    Remarks: Pursuant to N4971, [namespace.std] ([spec.ext]), +
    Remarks: Pursuant to N4971, [namespace.std] ([spec.ext]), users may specialize is_scalar, is_vector, and is_tensor to true for cv-unqualified program-defined types which respectively represent @@ -115,12 +115,12 @@ named-quantity-spec<T> || detail::IntermediateDerivedQuantitySpec<T> || template_of(^T) == ^kind_of; -

    5.8 Types [qty.types]

    5.8.1 General [qty.types.general]

    5.8 Types [qty.types]

    5.8.1 General [qty.types.general]

    A quantity type is a type Q that is a specialization of quantity or quantity_point.
    Q represents a quantity (IEC 60050, 112-01-01) with Q​::​rep as its number and Q​::​reference as its reference.
    Q is a structural type (N4971, [temp.param]) -if Q​::​rep is a structural type.
    Each class template defined in subclause [qty.types] +if Q​::​rep is a structural type.
    Each class template defined in subclause [qty.types] has data members and special members specified below, and has no base classes or members other than those specified.

    5.8.2 Class template quantity [qty.type]

    namespace mp_units { diff --git a/HEAD/api_reference/gen/qties.summary.html b/HEAD/api_reference/gen/qties.summary.html index f5a342597..6198d7f16 100644 --- a/HEAD/api_reference/gen/qties.summary.html +++ b/HEAD/api_reference/gen/qties.summary.html @@ -1,2 +1,2 @@ -[qties.summary]

    5 Quantities library [qties]

    5.1 Summary [qties.summary]

    This Clause describes components for dealing with quantities, +[qties.summary]

    5 Quantities library [qties]

    5.1 Summary [qties.summary]

    This Clause describes components for dealing with quantities, as summarized in Table 3.
    Table 3: Quantities library summary [tab:qties.summary]
    Subclause
    Module
    Helpers
    mp_units.core
    Traits
    Concepts
    Types
    Compatibility
    Dimension one
    Systems
    mp_units.systems
    std​::​chrono compatibility
    \ No newline at end of file diff --git a/HEAD/api_reference/gen/qty.helpers.html b/HEAD/api_reference/gen/qty.helpers.html index 5ad6ae751..34573e3fe 100644 --- a/HEAD/api_reference/gen/qty.helpers.html +++ b/HEAD/api_reference/gen/qty.helpers.html @@ -1,5 +1,5 @@ [qty.helpers]

    5 Quantities library [qties]

    5.5 Helpers [qty.helpers]

    consteval bool converts-to-base-subobject-of(std::meta type, std::meta template_name); -
    Preconditions: is_type(type) && is_template(template_name) is true.
    Returns: true if +
    Preconditions: is_type(type) && is_template(template_name) is true.
    Returns: true if [:type:] has an unambiguous and accessible base that is a specialization of [:template_name:], and false otherwise.
    \ No newline at end of file diff --git a/HEAD/api_reference/gen/qty.traits.html b/HEAD/api_reference/gen/qty.traits.html index 5325c164a..1caabb5ad 100644 --- a/HEAD/api_reference/gen/qty.traits.html +++ b/HEAD/api_reference/gen/qty.traits.html @@ -7,7 +7,7 @@ template<typename Rep> constexpr bool is_tensor = false; -
    Remarks: Pursuant to N4971, [namespace.std] ([spec.ext]), +
    Remarks: Pursuant to N4971, [namespace.std] ([spec.ext]), users may specialize is_scalar, is_vector, and is_tensor to true for cv-unqualified program-defined types which respectively represent diff --git a/HEAD/api_reference/gen/qty.types.general.html b/HEAD/api_reference/gen/qty.types.general.html index b3c5d0e74..a7a3ed6a2 100644 --- a/HEAD/api_reference/gen/qty.types.general.html +++ b/HEAD/api_reference/gen/qty.types.general.html @@ -1,8 +1,8 @@ -[qty.types.general]

    5 Quantities library [qties]

    5.8 Types [qty.types]

    5.8.1 General [qty.types.general]

    A quantity type +[qty.types.general]

    5 Quantities library [qties]

    5.8 Types [qty.types]

    5.8.1 General [qty.types.general]

    A quantity type is a type Q that is a specialization of quantity or quantity_point.
    Q represents a quantity (IEC 60050, 112-01-01) with Q​::​rep as its number and Q​::​reference as its reference.
    Q is a structural type (N4971, [temp.param]) -if Q​::​rep is a structural type.
    Each class template defined in subclause [qty.types] +if Q​::​rep is a structural type.
    Each class template defined in subclause [qty.types] has data members and special members specified below, and has no base classes or members other than those specified.
    \ No newline at end of file diff --git a/HEAD/api_reference/gen/qty.types.html b/HEAD/api_reference/gen/qty.types.html index 2dc42c180..635a98f03 100644 --- a/HEAD/api_reference/gen/qty.types.html +++ b/HEAD/api_reference/gen/qty.types.html @@ -1,9 +1,9 @@ -[qty.types]

    5 Quantities library [qties]

    5.8 Types [qty.types]

    5.8.1 General [qty.types.general]

    A quantity type +[qty.types]

    5 Quantities library [qties]

    5.8 Types [qty.types]

    5.8.1 General [qty.types.general]

    A quantity type is a type Q that is a specialization of quantity or quantity_point.
    Q represents a quantity (IEC 60050, 112-01-01) with Q​::​rep as its number and Q​::​reference as its reference.
    Q is a structural type (N4971, [temp.param]) -if Q​::​rep is a structural type.
    Each class template defined in subclause [qty.types] +if Q​::​rep is a structural type.
    Each class template defined in subclause [qty.types] has data members and special members specified below, and has no base classes or members other than those specified.

    5.8.2 Class template quantity [qty.type]

    namespace mp_units { diff --git a/HEAD/api_reference/gen/refs.html b/HEAD/api_reference/gen/refs.html index f030168a5..d11f66c94 100644 --- a/HEAD/api_reference/gen/refs.html +++ b/HEAD/api_reference/gen/refs.html @@ -1,4 +1,4 @@ -[refs]

    2 References [refs]

    The following documents are referred to in the text +[refs]

    2 References [refs]

    The following documents are referred to in the text in such a way that some or all of their content constitutes requirements of this document.
    For dated references, only the edition cited applies.
    For undated references, the latest edition of the referenced document diff --git a/HEAD/api_reference/gen/scope.html b/HEAD/api_reference/gen/scope.html index e6e7fd445..8ea9a59c2 100644 --- a/HEAD/api_reference/gen/scope.html +++ b/HEAD/api_reference/gen/scope.html @@ -1 +1 @@ -[scope]

    1 Scope [scope]

    This document describes the contents of the mp-units library.
    \ No newline at end of file +[scope]

    1 Scope [scope]

    This document describes the contents of the mp-units library.
    \ No newline at end of file diff --git a/HEAD/api_reference/gen/spec.cats.html b/HEAD/api_reference/gen/spec.cats.html index 9f5813c6b..e3c8a1880 100644 --- a/HEAD/api_reference/gen/spec.cats.html +++ b/HEAD/api_reference/gen/spec.cats.html @@ -1,4 +1,4 @@ -[spec.cats]

    4 Specification [spec]

    4.2 Categories [spec.cats]

    Detailed specifications for each of the components in the library are in +[spec.cats]

    4 Specification [spec]

    4.2 Categories [spec.cats]

    Detailed specifications for each of the components in the library are in [qties][qties], -as shown in Table 1.
    Table 1: Library categories [tab:lib.cats]
    Clause
    Category
    Quantities library
    The quantities library ([qties]) +as shown in Table 1.
    Table 1: Library categories [tab:lib.cats]
    Clause
    Category
    Quantities library
    The quantities library ([qties]) describes components for dealing with quantities.
    \ No newline at end of file diff --git a/HEAD/api_reference/gen/spec.ext.html b/HEAD/api_reference/gen/spec.ext.html index 679c42497..e698b803e 100644 --- a/HEAD/api_reference/gen/spec.ext.html +++ b/HEAD/api_reference/gen/spec.ext.html @@ -1,6 +1,6 @@ -[spec.ext]

    4 Specification [spec]

    4.1 External [spec.ext]

    The specification of the mp-units library subsumes +[spec.ext]

    4 Specification [spec]

    4.1 External [spec.ext]

    The specification of the mp-units library subsumes N4971, [description], N4971, [requirements], N4971, [concepts.equality], and SD-8, all assumingly amended for the context of this library.
    [Note 1: 
    This means that, non exhaustively,
    • ​::​mp_units2 is a reserved namespace, and
    • std​::​vector<mp_units​::​type> is a program-defined specialization and a library-defined specialization -from the point of view of the C++ standard library and the mp-units library, respectively.
    — end note]
    The mp-units library is not part of the C++ implementation.
    \ No newline at end of file +from the point of view of the C++ standard library and the mp-units library, respectively.
    — end note]
    The mp-units library is not part of the C++ implementation.
    \ No newline at end of file diff --git a/HEAD/api_reference/gen/spec.html b/HEAD/api_reference/gen/spec.html index 5665d3afe..bf792cc2f 100644 --- a/HEAD/api_reference/gen/spec.html +++ b/HEAD/api_reference/gen/spec.html @@ -1,12 +1,12 @@ -[spec]

    4 Specification [spec]

    4.1 External [spec.ext]

    The specification of the mp-units library subsumes +[spec]

    4 Specification [spec]

    4.1 External [spec.ext]

    The specification of the mp-units library subsumes N4971, [description], N4971, [requirements], N4971, [concepts.equality], and SD-8, all assumingly amended for the context of this library.
    [Note 1: 
    This means that, non exhaustively,
    • ​::​mp_units2 is a reserved namespace, and
    • std​::​vector<mp_units​::​type> is a program-defined specialization and a library-defined specialization -from the point of view of the C++ standard library and the mp-units library, respectively.
    — end note]
    The mp-units library is not part of the C++ implementation.

    4.2 Categories [spec.cats]

    Detailed specifications for each of the components in the library are in +from the point of view of the C++ standard library and the mp-units library, respectively.
    — end note]
    The mp-units library is not part of the C++ implementation.

    4.2 Categories [spec.cats]

    Detailed specifications for each of the components in the library are in [qties][qties], -as shown in Table 1.
    Table 1: Library categories [tab:lib.cats]
    Clause
    Category
    Quantities library
    The quantities library ([qties]) -describes components for dealing with quantities.

    4.3 Modules [spec.mods]

    The mp-units library provides the +as shown in Table 1.
    Table 1: Library categories [tab:lib.cats]
    Clause
    Category
    Quantities library
    The quantities library ([qties]) +describes components for dealing with quantities.

    4.3 Modules [spec.mods]

    The mp-units library provides the mp-units modules, -shown in Table 2.
    Table 2: mp-units modules [tab:modules]
    mp_units
    mp_units.core
    mp_units.systems

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    The mp-units library reserves macro names that start with +shown in Table 2.
    Table 2: mp-units modules [tab:modules]
    mp_units
    mp_units.core
    mp_units.systems

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    The mp-units library reserves macro names that start with MP_UNITSdigit-sequence_.
    \ No newline at end of file diff --git a/HEAD/api_reference/gen/spec.mods.html b/HEAD/api_reference/gen/spec.mods.html index 06f9e13c2..60c2ab06e 100644 --- a/HEAD/api_reference/gen/spec.mods.html +++ b/HEAD/api_reference/gen/spec.mods.html @@ -1,3 +1,3 @@ -[spec.mods]

    4 Specification [spec]

    4.3 Modules [spec.mods]

    The mp-units library provides the +[spec.mods]

    4 Specification [spec]

    4.3 Modules [spec.mods]

    The mp-units library provides the mp-units modules, shown in Table 2.
    Table 2: mp-units modules [tab:modules]
    mp_units
    mp_units.core
    mp_units.systems
    \ No newline at end of file diff --git a/HEAD/api_reference/gen/spec.reqs.html b/HEAD/api_reference/gen/spec.reqs.html index a079ef5ff..192fb66bd 100644 --- a/HEAD/api_reference/gen/spec.reqs.html +++ b/HEAD/api_reference/gen/spec.reqs.html @@ -1,2 +1,2 @@ -[spec.reqs]

    4 Specification [spec]

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    The mp-units library reserves macro names that start with +[spec.reqs]

    4 Specification [spec]

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    The mp-units library reserves macro names that start with MP_UNITSdigit-sequence_.
    \ No newline at end of file diff --git a/HEAD/api_reference/gen/spec.res.names.html b/HEAD/api_reference/gen/spec.res.names.html index bf8a2d788..85e440640 100644 --- a/HEAD/api_reference/gen/spec.res.names.html +++ b/HEAD/api_reference/gen/spec.res.names.html @@ -1,2 +1,2 @@ -[spec.res.names]

    4 Specification [spec]

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    The mp-units library reserves macro names that start with +[spec.res.names]

    4 Specification [spec]

    4.4 Library-wide requirements [spec.reqs]

    4.4.1 Reserved names [spec.res.names]

    The mp-units library reserves macro names that start with MP_UNITSdigit-sequence_.
    \ No newline at end of file diff --git a/HEAD/api_reference/index.html b/HEAD/api_reference/index.html index 592e288fd..6351b14ea 100644 --- a/HEAD/api_reference/index.html +++ b/HEAD/api_reference/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -253,7 +253,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/appendix/glossary/index.html b/HEAD/appendix/glossary/index.html index 3958cf899..92194a113 100644 --- a/HEAD/appendix/glossary/index.html +++ b/HEAD/appendix/glossary/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units @@ -2608,7 +2608,7 @@

    Other definitionsdimension of a derived quantity. -
  • Implemented as an expression template being the result of the +
  • Implemented as an symbolic expression being the result of the dimension equation on base dimensions.
  • diff --git a/HEAD/appendix/references/index.html b/HEAD/appendix/references/index.html index ad0559646..fd0ca3fa9 100644 --- a/HEAD/appendix/references/index.html +++ b/HEAD/appendix/references/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/assets/stylesheets/main.0253249f.min.css b/HEAD/assets/stylesheets/main.0253249f.min.css deleted file mode 100644 index 9a7a6982d..000000000 --- a/HEAD/assets/stylesheets/main.0253249f.min.css +++ /dev/null @@ -1 +0,0 @@ -@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}@media (prefers-reduced-motion){*,:after,:before{transition:none!important}}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:initial;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:#0000;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-scheme=default]{color-scheme:light}[data-md-color-scheme=default] img[src$="#gh-dark-mode-only"],[data-md-color-scheme=default] img[src$="#only-dark"]{display:none}:root,[data-md-color-scheme=default]{--md-hue:225deg;--md-default-fg-color:#000000de;--md-default-fg-color--light:#0000008a;--md-default-fg-color--lighter:#00000052;--md-default-fg-color--lightest:#00000012;--md-default-bg-color:#fff;--md-default-bg-color--light:#ffffffb3;--md-default-bg-color--lighter:#ffffff4d;--md-default-bg-color--lightest:#ffffff1f;--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:#4287ff;--md-code-hl-color--light:#4287ff1a;--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-del-color:#f5503d26;--md-typeset-ins-color:#0bd57026;--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-mark-color:#ffff0080;--md-typeset-table-color:#0000001f;--md-typeset-table-color--light:rgba(0,0,0,.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-warning-fg-color:#000000de;--md-warning-bg-color:#ff9;--md-footer-fg-color:#fff;--md-footer-fg-color--light:#ffffffb3;--md-footer-fg-color--lighter:#ffffff73;--md-footer-bg-color:#000000de;--md-footer-bg-color--dark:#00000052;--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #0000001a,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0003,0 0 0.05rem #00000059}.md-icon svg{fill:currentcolor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--md-text-font-family:var(--md-text-font,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;--md-code-font-family:var(--md-code-font,_),SFMono-Regular,Consolas,Menlo,monospace}aside,body,input{font-feature-settings:"kern","liga";color:var(--md-typeset-color);font-family:var(--md-text-font-family)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family)}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin-bottom:1em;margin-top:1em}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a:focus code,.md-typeset a:hover code{background-color:var(--md-accent-fg-color--transparent)}.md-typeset a code{color:currentcolor;transition:background-color 125ms}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr;font-variant-ligatures:none}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;outline-color:var(--md-accent-fg-color);overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help;text-decoration:none}.md-typeset small{opacity:.75}[dir=ltr] .md-typeset sub,[dir=ltr] .md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em}[dir=ltr] .md-typeset blockquote{padding-left:.6rem}[dir=rtl] .md-typeset blockquote{padding-right:.6rem}[dir=ltr] .md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{border-right:.2rem solid var(--md-default-fg-color--lighter)}.md-typeset blockquote{color:var(--md-default-fg-color--light);margin-left:0;margin-right:0}.md-typeset ul{list-style-type:disc}.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol,[dir=ltr] .md-typeset ul{margin-left:.625em}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em}.md-typeset ol,.md-typeset ul{padding:0}.md-typeset ol:not([hidden]),.md-typeset ul:not([hidden]){display:flow-root}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol ol ol ol,.md-typeset ul ol ol ol{list-style-type:upper-alpha}.md-typeset ol ol ol ol ol,.md-typeset ul ol ol ol ol{list-style-type:upper-roman}.md-typeset ol[type],.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol li,[dir=ltr] .md-typeset ul li{margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}[dir=ltr] .md-typeset ol li ol,[dir=ltr] .md-typeset ol li ul,[dir=ltr] .md-typeset ul li ol,[dir=ltr] .md-typeset ul li ul{margin-left:.625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin-bottom:.5em;margin-top:.5em}[dir=ltr] .md-typeset dd{margin-left:1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em}.md-typeset dd{margin-bottom:1.5em;margin-top:1em}.md-typeset img,.md-typeset svg,.md-typeset video{height:auto;max-width:100%}.md-typeset img[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:1em auto;max-width:100%;text-align:center;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block;margin:0 auto}.md-typeset figcaption{font-style:italic;margin:1em auto;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:var(--md-typeset-table-color--light);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}[dir=ltr] .md-typeset table th[role=columnheader]:after{margin-left:.5em}[dir=rtl] .md-typeset table th[role=columnheader]:after{margin-right:.5em}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}@media screen and (max-width:44.984375em){.md-content__inner>pre{margin:1em -.8rem}.md-content__inner>pre code{border-radius:0}}.md-typeset .md-author{border-radius:100%;display:block;flex-shrink:0;height:1.6rem;overflow:hidden;position:relative;transition:color 125ms,transform 125ms;width:1.6rem}.md-typeset .md-author img{display:block}.md-typeset .md-author--more{background:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--lighter);font-size:.6rem;font-weight:700;line-height:1.6rem;text-align:center}.md-typeset .md-author--long{height:2.4rem;width:2.4rem}.md-typeset a.md-author{transform:scale(1)}.md-typeset a.md-author img{border-radius:100%;filter:grayscale(100%) opacity(75%);transition:filter 125ms}.md-typeset a.md-author:focus,.md-typeset a.md-author:hover{transform:scale(1.1);z-index:1}.md-typeset a.md-author:focus img,.md-typeset a.md-author:hover img{filter:grayscale(0)}.md-banner{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color);overflow:auto}@media print{.md-banner{display:none}}.md-banner--warning{background-color:var(--md-warning-bg-color);color:var(--md-warning-fg-color)}.md-banner__inner{font-size:.7rem;margin:.6rem auto;padding:0 .8rem}[dir=ltr] .md-banner__button{float:right}[dir=rtl] .md-banner__button{float:left}.md-banner__button{color:inherit;cursor:pointer;transition:opacity .25s}.no-js .md-banner__button{display:none}.md-banner__button:hover{opacity:.7}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.984375em){body[data-md-scrolllock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;position:absolute;right:.5em;top:.5em;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .md-code__content{display:grid}@keyframes consent{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes overlay{0%{opacity:0}to{opacity:1}}.md-consent__overlay{animation:overlay .25s both;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);background-color:#0000008a;height:100%;opacity:1;position:fixed;top:0;width:100%;z-index:5}.md-consent__inner{animation:consent .5s cubic-bezier(.1,.7,.1,1) both;background-color:var(--md-default-bg-color);border:0;border-radius:.1rem;bottom:0;box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;max-height:100%;overflow:auto;padding:0;position:fixed;width:100%;z-index:5}.md-consent__form{padding:.8rem}.md-consent__settings{display:none;margin:1em 0}input:checked+.md-consent__settings{display:block}.md-consent__controls{margin-bottom:.8rem}.md-typeset .md-consent__controls .md-button{display:inline}@media screen and (max-width:44.984375em){.md-typeset .md-consent__controls .md-button{display:block;margin-top:.4rem;text-align:center;width:100%}}.md-consent label{cursor:pointer}.md-content{flex-grow:1;min-width:0}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){[dir=ltr] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=ltr] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner,[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}[dir=ltr] .md-content__button{float:right}[dir=rtl] .md-content__button{float:left}[dir=ltr] .md-content__button{margin-left:.4rem}[dir=rtl] .md-content__button{margin-right:.4rem}.md-content__button{margin:.4rem 0;padding:0}@media print{.md-content__button{display:none}}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}[dir=ltr] .md-dialog{right:.8rem}[dir=rtl] .md-dialog{left:.8rem}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:var(--md-shadow-z3);min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:4}@media print{.md-dialog{display:none}}.md-dialog--active{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-feedback{margin:2em 0 1em;text-align:center}.md-feedback fieldset{border:none;margin:0;padding:0}.md-feedback__title{font-weight:700;margin:1em auto}.md-feedback__inner{position:relative}.md-feedback__list{display:flex;flex-wrap:wrap;place-content:baseline center;position:relative}.md-feedback__list:hover .md-icon:not(:disabled){color:var(--md-default-fg-color--lighter)}:disabled .md-feedback__list{min-height:1.8rem}.md-feedback__icon{color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;margin:0 .1rem;transition:color 125ms}.md-feedback__icon:not(:disabled).md-icon:hover{color:var(--md-accent-fg-color)}.md-feedback__icon:disabled{color:var(--md-default-fg-color--lightest);pointer-events:none}.md-feedback__note{opacity:0;position:relative;transform:translateY(.4rem);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-feedback__note>*{margin:0 auto;max-width:16rem}:disabled .md-feedback__note{opacity:1;transform:translateY(0)}@media print{.md-feedback{display:none}}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{justify-content:space-between;overflow:auto;padding:.2rem}.md-footer__inner:not([hidden]){display:flex}.md-footer__link{align-items:end;display:flex;flex-grow:0.01;margin-bottom:.4rem;margin-top:1rem;max-width:100%;outline-color:var(--md-accent-fg-color);overflow:hidden;transition:opacity .25s}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}[dir=rtl] .md-footer__link svg{transform:scaleX(-1)}@media screen and (max-width:44.984375em){.md-footer__link--prev{flex-shrink:0}.md-footer__link--prev .md-footer__title{display:none}}[dir=ltr] .md-footer__link--next{margin-left:auto}[dir=rtl] .md-footer__link--next{margin-right:auto}.md-footer__link--next{text-align:right}[dir=rtl] .md-footer__link--next{text-align:left}.md-footer__title{flex-grow:1;font-size:.9rem;margin-bottom:.7rem;max-width:calc(100% - 2.4rem);padding:0 1rem;white-space:nowrap}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-copyright{width:auto}}.md-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-social{display:inline-flex;gap:.2rem;margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-social{padding:.6rem 0}}.md-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-social__link:before{line-height:1.9}.md-social__link svg{fill:currentcolor;max-height:.8rem;vertical-align:-25%}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[dir=ltr] .md-typeset .md-input{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .md-input,[dir=rtl] .md-typeset .md-input{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .md-input{border-top-left-radius:.1rem}.md-typeset .md-input{border-bottom:.1rem solid var(--md-default-fg-color--lighter);box-shadow:var(--md-shadow-z1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:border .25s,box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{border-bottom-color:var(--md-accent-fg-color);box-shadow:var(--md-shadow-z2)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem #0000,0 .2rem .4rem #0000;color:var(--md-primary-bg-color);display:block;left:0;position:sticky;right:0;top:0;z-index:4}@media print{.md-header{display:none}}.md-header[hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header--shadow{box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentcolor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.234375em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentcolor;display:block;height:1.2rem;width:auto}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;white-space:nowrap}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__topic:first-child{font-weight:700}[dir=ltr] .md-header__title{margin-left:1rem;margin-right:.4rem}[dir=rtl] .md-header__title{margin-left:.4rem;margin-right:1rem}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem}.md-header__title--active .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title--active .md-header__topic{transform:translateX(1.25rem)}.md-header__title--active .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__option>input{bottom:0}.md-header__source{display:none}@media screen and (min-width:60em){[dir=ltr] .md-header__source{margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem}.md-header__source{display:block;max-width:11.7rem;width:11.7rem}}@media screen and (min-width:76.25em){[dir=ltr] .md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-meta{color:var(--md-default-fg-color--light);font-size:.7rem;line-height:1.3}.md-meta__list{display:inline-flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.md-meta__item:not(:last-child):after{content:"·";margin-left:.2rem;margin-right:.2rem}.md-meta__link{color:var(--md-typeset-a-color)}.md-meta__link:focus,.md-meta__link:hover{color:var(--md-accent-fg-color)}.md-draft{background-color:#ff1744;border-radius:.125em;color:#fff;display:inline-block;font-weight:700;padding-left:.5714285714em;padding-right:.5714285714em}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{color:var(--md-default-fg-color--light);display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentcolor;display:block;height:2.4rem;max-width:100%;object-fit:contain;width:auto}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__link{align-items:flex-start;display:flex;gap:.4rem;margin-top:.625em;scroll-snap-align:start;transition:color 125ms}.md-nav__link--passed{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active,.md-nav__item .md-nav__link--active code{color:var(--md-typeset-a-color)}.md-nav__link .md-ellipsis{position:relative}[dir=ltr] .md-nav__link .md-icon:last-child{margin-left:auto}[dir=rtl] .md-nav__link .md-icon:last-child{margin-right:auto}.md-nav__link svg{fill:currentcolor;flex-shrink:0;height:1.3em;position:relative}.md-nav__link[for]:focus,.md-nav__link[for]:hover,.md-nav__link[href]:focus,.md-nav__link[href]:hover{color:var(--md-accent-fg-color);cursor:pointer}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentcolor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__container>.md-nav__link{margin-top:0}.md-nav__container>.md-nav__link:first-child{flex-grow:1;min-width:0}.md-nav__icon{flex-shrink:0}.md-nav__source{display:none}@media screen and (max-width:76.234375em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}[dir=ltr] .md-nav--primary .md-nav__title .md-nav__icon{left:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);font-weight:700}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;right:.2rem;top:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link svg{margin-top:.1em}.md-nav--primary .md-nav__link>.md-nav__link{padding:0}[dir=ltr] .md-nav--primary .md-nav__link .md-nav__icon{margin-right:-.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{margin-left:-.2rem}.md-nav--primary .md-nav__link .md-nav__icon{font-size:1.2rem;height:1.2rem;width:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav{background-color:initial;position:static}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.984375em){.md-nav--primary .md-nav__link[for=__toc]{display:flex}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-nav--integrated .md-nav__link[for=__toc]{display:flex}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav{margin-bottom:-.4rem}.md-nav--secondary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--secondary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--secondary .md-nav__list{padding-right:.6rem}.md-nav--secondary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--secondary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--secondary .md-nav__item>.md-nav__link{margin-left:.4rem}}@media screen and (min-width:76.25em){.md-nav{margin-bottom:-.4rem;transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--primary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--primary .md-nav__list{padding-right:.6rem}.md-nav--primary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--primary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--primary .md-nav__item>.md-nav__link{margin-left:.4rem}.md-nav__toggle~.md-nav{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .25s,visibility 0ms .25s;visibility:collapse}.md-nav__toggle~.md-nav>.md-nav__list{overflow:hidden}.md-nav__toggle.md-toggle--indeterminate~.md-nav,.md-nav__toggle:checked~.md-nav{grid-template-rows:1fr;opacity:1;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .15s .1s,visibility 0ms;visibility:visible}.md-nav__toggle.md-toggle--indeterminate~.md-nav{transition:none}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{font-weight:700}.md-nav__item--section>.md-nav__link[for]{color:var(--md-default-fg-color--light)}.md-nav__item--section>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav__item--section>.md-nav__link .md-icon,.md-nav__item--section>.md-nav__link>[for]{display:none}[dir=ltr] .md-nav__item--section>.md-nav{margin-left:-.6rem}[dir=rtl] .md-nav__item--section>.md-nav{margin-right:-.6rem}.md-nav__item--section>.md-nav{display:block;opacity:1;visibility:visible}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{border-radius:100%;height:.9rem;transition:background-color .25s;width:.9rem}.md-nav__icon:hover{background-color:var(--md-accent-fg-color--transparent)}.md-nav__icon:after{background-color:currentcolor;border-radius:100%;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:transform .25s;vertical-align:-.1rem;width:100%}[dir=rtl] .md-nav__icon:after{transform:rotate(180deg)}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon:after,.md-nav__item--nested .md-toggle--indeterminate~.md-nav__link .md-nav__icon:after{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);margin-top:0;position:sticky;top:0;z-index:1}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active.md-nav__item--section{margin:0}[dir=ltr] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-left:-.6rem}[dir=rtl] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-right:-.6rem}.md-nav--lifted>.md-nav__list>.md-nav__item>[for]{color:var(--md-default-fg-color--light)}.md-nav--lifted .md-nav[data-md-level="1"]{grid-template-rows:1fr;opacity:1;visibility:visible}[dir=ltr] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-left:.05rem solid var(--md-primary-fg-color)}[dir=rtl] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-right:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{display:block;margin-bottom:1.25em;opacity:1;visibility:visible}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__list{overflow:visible;padding-bottom:0}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__title{display:none}}.md-pagination{font-size:.8rem;font-weight:700;gap:.4rem}.md-pagination,.md-pagination>*{align-items:center;display:flex;justify-content:center}.md-pagination>*{border-radius:.2rem;height:1.8rem;min-width:1.8rem;text-align:center}.md-pagination__current{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light)}.md-pagination__link{transition:color 125ms,background-color 125ms}.md-pagination__link:focus,.md-pagination__link:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-pagination__link:focus svg,.md-pagination__link:hover svg{color:var(--md-accent-fg-color)}.md-pagination__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-pagination__link svg{fill:currentcolor;color:var(--md-default-fg-color--lighter);display:block;max-height:100%;width:1.2rem}.md-post__back{border-bottom:.05rem solid var(--md-default-fg-color--lightest);margin-bottom:1.2rem;padding-bottom:1.2rem}@media screen and (max-width:76.234375em){.md-post__back{display:none}}[dir=rtl] .md-post__back svg{transform:scaleX(-1)}.md-post__authors{display:flex;flex-direction:column;gap:.6rem;margin:0 .6rem 1.2rem}.md-post .md-post__meta a{transition:color 125ms}.md-post .md-post__meta a:focus,.md-post .md-post__meta a:hover{color:var(--md-accent-fg-color)}.md-post__title{color:var(--md-default-fg-color--light);font-weight:700}.md-post--excerpt{margin-bottom:3.2rem}.md-post--excerpt .md-post__header{align-items:center;display:flex;gap:.6rem;min-height:1.6rem}.md-post--excerpt .md-post__authors{align-items:center;display:inline-flex;flex-direction:row;gap:.2rem;margin:0;min-height:2.4rem}[dir=ltr] .md-post--excerpt .md-post__meta .md-meta__list{margin-right:.4rem}[dir=rtl] .md-post--excerpt .md-post__meta .md-meta__list{margin-left:.4rem}.md-post--excerpt .md-post__content>:first-child{--md-scroll-margin:6rem;margin-top:0}.md-post>.md-nav--secondary{margin:1em 0}.md-profile{align-items:center;display:flex;font-size:.7rem;gap:.6rem;line-height:1.4;width:100%}.md-profile__description{flex-grow:1}.md-content--post{display:flex}@media screen and (max-width:76.234375em){.md-content--post{flex-flow:column-reverse}}.md-content--post>.md-content__inner{min-width:0}@media screen and (min-width:76.25em){[dir=ltr] .md-content--post>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-content--post>.md-content__inner{margin-right:1.2rem}}@media screen and (max-width:76.234375em){.md-sidebar.md-sidebar--post{padding:0;position:static;width:100%}.md-sidebar.md-sidebar--post .md-sidebar__scrollwrap{overflow:visible}.md-sidebar.md-sidebar--post .md-sidebar__inner{padding:0}.md-sidebar.md-sidebar--post .md-post__meta{margin-left:.6rem;margin-right:.6rem}.md-sidebar.md-sidebar--post .md-nav__item{border:none;display:inline}.md-sidebar.md-sidebar--post .md-nav__list{display:inline-flex;flex-wrap:wrap;gap:.6rem;padding-bottom:.6rem;padding-top:.6rem}.md-sidebar.md-sidebar--post .md-nav__link{padding:0}.md-sidebar.md-sidebar--post .md-nav{height:auto;margin-bottom:0;position:static}}:root{--md-progress-value:0;--md-progress-delay:400ms}.md-progress{background:var(--md-primary-bg-color);height:.075rem;opacity:min(clamp(0,var(--md-progress-value),1),clamp(0,100 - var(--md-progress-value),1));position:fixed;top:0;transform:scaleX(calc(var(--md-progress-value)*1%));transform-origin:left;transition:transform .5s cubic-bezier(.19,1,.22,1),opacity .25s var(--md-progress-delay);width:100%;z-index:4}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__overlay{left:-2.2rem}[dir=rtl] .md-search__overlay{right:-2.2rem}.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){[dir=ltr] .md-search__overlay{left:0}[dir=rtl] .md-search__overlay{right:0}.md-search__overlay{background-color:#0000008a;cursor:pointer;height:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__inner{left:0}[dir=rtl] .md-search__inner{right:0}.md-search__inner{height:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__inner{float:right}[dir=rtl] .md-search__inner{float:left}.md-search__inner{padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}}@media screen and (min-width:60em) and (max-width:76.234375em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem #0000;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:#00000042;border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:#ffffff1f}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem #00000012;color:var(--md-default-fg-color)}[dir=ltr] .md-search__input{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__input{padding-left:2.2rem;padding-right:3.6rem}.md-search__input{background:#0000;font-size:.9rem;height:100%;position:relative;text-overflow:ellipsis;width:100%;z-index:2}.md-search__input::placeholder{transition:color .25s}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.984375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__input{padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input{color:inherit;font-size:.8rem}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:#0000}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}[dir=ltr] .md-search__icon[for=__search]{left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem}.md-search__icon[for=__search]{position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__icon[for=__search]{left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem}.md-search__icon[for=__search]{top:.6rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}[dir=ltr] .md-search__options{right:.5rem}[dir=rtl] .md-search__options{left:.5rem}.md-search__options{pointer-events:none;position:absolute;top:.3rem;z-index:2}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__options{right:.8rem}[dir=rtl] .md-search__options{left:.8rem}.md-search__options{top:.6rem}}[dir=ltr] .md-search__options>.md-icon{margin-left:.2rem}[dir=rtl] .md-search__options>.md-icon{margin-right:.2rem}.md-search__options>.md-icon{color:var(--md-default-fg-color--light);opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>.md-icon:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon:hover{opacity:.7}[dir=ltr] .md-search__suggest{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__suggest{padding-left:2.2rem;padding-right:3.6rem}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}@media screen and (min-width:60em){[dir=ltr] .md-search__suggest{padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}.md-search__suggest{font-size:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}[dir=ltr] .md-search__output{border-bottom-left-radius:.1rem}[dir=ltr] .md-search__output,[dir=rtl] .md-search__output{border-bottom-right-radius:.1rem}[dir=rtl] .md-search__output{border-bottom-left-radius:.1rem}.md-search__output{overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.984375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:var(--md-shadow-z3);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){[dir=ltr] .md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0;-webkit-user-select:none;user-select:none}.md-search-result__item{box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more>summary{cursor:pointer;display:block;outline:none;position:sticky;scroll-snap-align:start;top:0;z-index:1}.md-search-result__more>summary::marker{display:none}.md-search-result__more>summary::-webkit-details-marker{display:none}.md-search-result__more>summary>div{color:var(--md-typeset-a-color);font-size:.64rem;padding:.75em .8rem;transition:color .25s,background-color .25s}@media screen and (min-width:60em){[dir=ltr] .md-search-result__more>summary>div{padding-left:2.2rem}[dir=rtl] .md-search-result__more>summary>div{padding-right:2.2rem}}.md-search-result__more>summary:focus>div,.md-search-result__more>summary:hover>div{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more[open]>summary{background-color:var(--md-default-bg-color)}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){[dir=ltr] .md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem}}[dir=ltr] .md-search-result__icon{left:0}[dir=rtl] .md-search-result__icon{right:0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.984375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentcolor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result .md-typeset{color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6}.md-search-result .md-typeset h1{color:var(--md-default-fg-color);font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result .md-typeset h1 mark{text-decoration:none}.md-search-result .md-typeset h2{color:var(--md-default-fg-color);font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result .md-typeset h2 mark{text-decoration:none}.md-search-result__terms{color:var(--md-default-fg-color);display:block;font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:initial;color:var(--md-accent-fg-color);text-decoration:underline}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid #0000;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid #0000;border-right:.2rem solid #0000;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}[dir=ltr] .md-select__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.234375em){[dir=ltr] .md-sidebar--primary{left:-12.1rem}[dir=rtl] .md-sidebar--primary{right:-12.1rem}.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:5}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:var(--md-shadow-z3);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}.md-header--lifted~.md-container .md-sidebar{top:4.8rem}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{scrollbar-gutter:stable;-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap:focus-within,.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb:hover,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@supports selector(::-webkit-scrollbar){.md-sidebar__scrollwrap{scrollbar-gutter:auto}[dir=ltr] .md-sidebar__inner{padding-right:calc(100% - 11.5rem)}[dir=rtl] .md-sidebar__inner{padding-left:calc(100% - 11.5rem)}}@media screen and (max-width:76.234375em){.md-overlay{background-color:#0000008a;height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:5}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@keyframes facts{0%{height:0}to{height:.65rem}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}[dir=ltr] .md-source__icon svg{margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem}.md-source__icon svg{margin-top:.6rem}[dir=ltr] .md-source__icon+.md-source__repository{padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{padding-right:2rem}[dir=ltr] .md-source__icon+.md-source__repository{margin-left:-2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem}[dir=ltr] .md-source__repository{margin-left:.6rem}[dir=rtl] .md-source__repository{margin-right:.6rem}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{display:flex;font-size:.55rem;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source__repository--active .md-source__facts{animation:facts .25s ease-in}.md-source__fact{overflow:hidden;text-overflow:ellipsis}.md-source__repository--active .md-source__fact{animation:fact .4s ease-out}[dir=ltr] .md-source__fact:before{margin-right:.1rem}[dir=rtl] .md-source__fact:before{margin-left:.1rem}.md-source__fact:before{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}.md-source__fact:nth-child(1n+2){flex-shrink:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-source-file{margin:1em 0}[dir=ltr] .md-source-file__fact{margin-right:.6rem}[dir=rtl] .md-source-file__fact{margin-left:.6rem}.md-source-file__fact{align-items:center;color:var(--md-default-fg-color--light);display:inline-flex;font-size:.68rem;gap:.3rem}.md-source-file__fact .md-icon{flex-shrink:0;margin-bottom:.05rem}[dir=ltr] .md-source-file__fact .md-author{float:left}[dir=rtl] .md-source-file__fact .md-author{float:right}.md-source-file__fact .md-author{margin-right:.2rem}.md-source-file__fact svg{width:.9rem}:root{--md-status:url('data:image/svg+xml;charset=utf-8,');--md-status--new:url('data:image/svg+xml;charset=utf-8,');--md-status--deprecated:url('data:image/svg+xml;charset=utf-8,');--md-status--encrypted:url('data:image/svg+xml;charset=utf-8,')}.md-status:after{background-color:var(--md-default-fg-color--light);content:"";display:inline-block;height:1.125em;-webkit-mask-image:var(--md-status);mask-image:var(--md-status);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-bottom;width:1.125em}.md-status:hover:after{background-color:currentcolor}.md-status--new:after{-webkit-mask-image:var(--md-status--new);mask-image:var(--md-status--new)}.md-status--deprecated:after{-webkit-mask-image:var(--md-status--deprecated);mask-image:var(--md-status--deprecated)}.md-status--encrypted:after{-webkit-mask-image:var(--md-status--encrypted);mask-image:var(--md-status--encrypted)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);display:block;line-height:1.3;overflow:auto;width:100%;z-index:3}@media print{.md-tabs{display:none}}@media screen and (max-width:76.234375em){.md-tabs{display:none}}.md-tabs[hidden]{pointer-events:none}[dir=ltr] .md-tabs__list{margin-left:.2rem}[dir=rtl] .md-tabs__list{margin-right:.2rem}.md-tabs__list{contain:content;display:flex;list-style:none;margin:0;overflow:auto;padding:0;scrollbar-width:none;white-space:nowrap}.md-tabs__list::-webkit-scrollbar{display:none}.md-tabs__item{height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__item--active .md-tabs__link{color:inherit;opacity:1}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:flex;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}[dir=ltr] .md-tabs__link svg{margin-right:.4rem}[dir=rtl] .md-tabs__link svg{margin-left:.4rem}.md-tabs__link svg{fill:currentcolor;height:1.3em}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}:root{--md-tag-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-tags:not([hidden]){display:inline-flex;flex-wrap:wrap;gap:.5em;margin-bottom:.75em;margin-top:-.125em}.md-typeset .md-tag{align-items:center;background:var(--md-default-fg-color--lightest);border-radius:2.4rem;display:inline-flex;font-size:.64rem;font-size:min(.8em,.64rem);font-weight:700;gap:.5em;letter-spacing:normal;line-height:1.6;padding:.3125em .78125em}.md-typeset .md-tag[href]{-webkit-tap-highlight-color:transparent;color:inherit;outline:none;transition:color 125ms,background-color 125ms}.md-typeset .md-tag[href]:focus,.md-typeset .md-tag[href]:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[id]>.md-typeset .md-tag{vertical-align:text-top}.md-typeset .md-tag-icon:before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-tag-icon);mask-image:var(--md-tag-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset .md-tag-icon[href]:focus:before,.md-typeset .md-tag-icon[href]:hover:before{background-color:var(--md-accent-bg-color)}@keyframes pulse{0%{transform:scale(.95)}75%{transform:scale(1)}to{transform:scale(.95)}}:root{--md-annotation-bg-icon:url('data:image/svg+xml;charset=utf-8,');--md-annotation-icon:url('data:image/svg+xml;charset=utf-8,')}.md-tooltip{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);font-family:var(--md-text-font-family);left:clamp(var(--md-tooltip-0,0rem) + .8rem,var(--md-tooltip-x),100vw + var(--md-tooltip-0,0rem) + .8rem - var(--md-tooltip-width) - 2 * .8rem);max-width:calc(100vw - 1.6rem);opacity:0;position:absolute;top:var(--md-tooltip-y);transform:translateY(-.4rem);transition:transform 0ms .25s,opacity .25s,z-index .25s;width:var(--md-tooltip-width);z-index:0}.md-tooltip--active{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip--inline{font-weight:700;-webkit-user-select:none;user-select:none;width:auto}.md-tooltip--inline:not(.md-tooltip--active){transform:translateY(.2rem) scale(.9)}.md-tooltip--inline .md-tooltip__inner{font-size:.5rem;padding:.2rem .4rem}[hidden]+.md-tooltip--inline{display:none}.focus-visible>.md-tooltip,.md-tooltip:target{outline:var(--md-accent-fg-color) auto}.md-tooltip__inner{font-size:.64rem;padding:.8rem}.md-tooltip__inner.md-typeset>:first-child{margin-top:0}.md-tooltip__inner.md-typeset>:last-child{margin-bottom:0}.md-annotation{font-style:normal;font-weight:400;outline:none;text-align:initial;vertical-align:text-bottom;white-space:normal}[dir=rtl] .md-annotation{direction:rtl}code .md-annotation{font-family:var(--md-code-font-family);font-size:inherit}.md-annotation:not([hidden]){display:inline-block;line-height:1.25}.md-annotation__index{border-radius:.01px;cursor:pointer;display:inline-block;margin-left:.4ch;margin-right:.4ch;outline:none;overflow:hidden;position:relative;-webkit-user-select:none;user-select:none;vertical-align:text-top;z-index:0}.md-annotation .md-annotation__index{transition:z-index .25s}@media screen{.md-annotation__index{width:2.2ch}[data-md-visible]>.md-annotation__index{animation:pulse 2s infinite}.md-annotation__index:before{background:var(--md-default-bg-color);-webkit-mask-image:var(--md-annotation-bg-icon);mask-image:var(--md-annotation-bg-icon)}.md-annotation__index:after,.md-annotation__index:before{content:"";height:2.2ch;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:-.1ch;width:2.2ch;z-index:-1}.md-annotation__index:after{background-color:var(--md-default-fg-color--lighter);-webkit-mask-image:var(--md-annotation-icon);mask-image:var(--md-annotation-icon);transform:scale(1.0001);transition:background-color .25s,transform .25s}.md-tooltip--active+.md-annotation__index:after{transform:rotate(45deg)}.md-tooltip--active+.md-annotation__index:after,:hover>.md-annotation__index:after{background-color:var(--md-accent-fg-color)}}.md-tooltip--active+.md-annotation__index{animation-play-state:paused;transition-duration:0ms;z-index:2}.md-annotation__index [data-md-annotation-id]{display:inline-block}@media print{.md-annotation__index [data-md-annotation-id]{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);font-weight:700;padding:0 .6ch;white-space:nowrap}.md-annotation__index [data-md-annotation-id]:after{content:attr(data-md-annotation-id)}}.md-typeset .md-annotation-list{counter-reset:xxx;list-style:none}.md-typeset .md-annotation-list li{position:relative}[dir=ltr] .md-typeset .md-annotation-list li:before{left:-2.125em}[dir=rtl] .md-typeset .md-annotation-list li:before{right:-2.125em}.md-typeset .md-annotation-list li:before{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);content:counter(xxx);counter-increment:xxx;font-size:.8875em;font-weight:700;height:2ch;line-height:1.25;min-width:2ch;padding:0 .6ch;position:absolute;text-align:center;top:.25em}:root{--md-tooltip-width:20rem;--md-tooltip-tail:0.3rem}.md-tooltip2{-webkit-backface-visibility:hidden;backface-visibility:hidden;color:var(--md-default-fg-color);font-family:var(--md-text-font-family);opacity:0;pointer-events:none;position:absolute;top:calc(var(--md-tooltip-host-y) + var(--md-tooltip-y));transform:translateY(-.4rem);transform-origin:calc(var(--md-tooltip-host-x) + var(--md-tooltip-x)) 0;transition:transform 0ms .25s,opacity .25s,z-index .25s;width:100%;z-index:0}.md-tooltip2:before{border-left:var(--md-tooltip-tail) solid #0000;border-right:var(--md-tooltip-tail) solid #0000;content:"";display:block;left:clamp(1.5 * .8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-tail),100vw - 2 * var(--md-tooltip-tail) - 1.5 * .8rem);position:absolute;z-index:1}.md-tooltip2--top:before{border-top:var(--md-tooltip-tail) solid var(--md-default-bg-color);bottom:calc(var(--md-tooltip-tail)*-1 + .025rem);filter:drop-shadow(0 1px 0 hsla(0,0%,0%,.05))}.md-tooltip2--bottom:before{border-bottom:var(--md-tooltip-tail) solid var(--md-default-bg-color);filter:drop-shadow(0 -1px 0 hsla(0,0%,0%,.05));top:calc(var(--md-tooltip-tail)*-1 + .025rem)}.md-tooltip2--active{opacity:1;transform:translateY(0);transition:transform .4s cubic-bezier(0,1,.5,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip2__inner{scrollbar-gutter:stable;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);left:clamp(.8rem,var(--md-tooltip-host-x) - .8rem,100vw - var(--md-tooltip-width) - .8rem);max-height:40vh;max-width:calc(100vw - 1.6rem);position:relative;scrollbar-width:thin}.md-tooltip2__inner::-webkit-scrollbar{height:.2rem;width:.2rem}.md-tooltip2__inner::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-tooltip2__inner::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}[role=tooltip]>.md-tooltip2__inner{font-size:.5rem;font-weight:700;left:clamp(.8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-width)/2,100vw - var(--md-tooltip-width) - .8rem);max-width:min(100vw - 2 * .8rem,400px);padding:.2rem .4rem;-webkit-user-select:none;user-select:none;width:-moz-fit-content;width:fit-content}.md-tooltip2__inner.md-typeset>:first-child{margin-top:0}.md-tooltip2__inner.md-typeset>:last-child{margin-bottom:0}[dir=ltr] .md-top{margin-left:50%}[dir=rtl] .md-top{margin-right:50%}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color--light);cursor:pointer;display:block;font-size:.7rem;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{transform:translate(50%)}.md-top[hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}[dir=rtl] .md-top[hidden]{transform:translate(50%,.2rem)}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}[dir=ltr] .md-version__current{margin-left:1.4rem;margin-right:.4rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current{color:inherit;cursor:pointer;outline:none;position:relative;top:.05rem}[dir=ltr] .md-version__current:after{margin-left:.4rem}[dir=rtl] .md-version__current:after{margin-right:.4rem}.md-version__current:after{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.4rem}.md-version__alias{margin-left:.3rem;opacity:.7}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:3}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (hover:none),(pointer:coarse){.md-version:hover .md-version__list{animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{animation:none}}.md-version__item{line-height:1.8rem}[dir=ltr] .md-version__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border:.075rem solid #448aff;border-radius:.2rem;box-shadow:var(--md-shadow-z1);color:var(--md-admonition-fg-color);display:flow-root;font-size:.64rem;margin:1.5625em 0;padding:0 .6rem;page-break-inside:avoid;transition:box-shadow 125ms}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}.md-typeset .admonition:focus-within,.md-typeset details:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .admonition>*,.md-typeset details>*{box-sizing:border-box}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{padding-left:2rem;padding-right:.6rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{padding-left:.6rem;padding-right:2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-left-width:.2rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-right-width:.2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset .admonition-title,.md-typeset summary{background-color:#448aff1a;border:none;font-weight:700;margin:0 -.6rem;padding-bottom:.4rem;padding-top:.4rem;position:relative}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}[dir=ltr] .md-typeset .admonition-title:before,[dir=ltr] .md-typeset summary:before{left:.6rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;width:1rem}.md-typeset .admonition-title code,.md-typeset summary code{box-shadow:0 0 0 .05rem var(--md-default-fg-color--lightest)}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .admonition.note:focus-within,.md-typeset details.note:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:#448aff1a}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note)}.md-typeset .note>.admonition-title:after,.md-typeset .note>summary:after{color:#448aff}.md-typeset .admonition.abstract,.md-typeset details.abstract{border-color:#00b0ff}.md-typeset .admonition.abstract:focus-within,.md-typeset details.abstract:focus-within{box-shadow:0 0 0 .2rem #00b0ff1a}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary{background-color:#00b0ff1a}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract)}.md-typeset .abstract>.admonition-title:after,.md-typeset .abstract>summary:after{color:#00b0ff}.md-typeset .admonition.info,.md-typeset details.info{border-color:#00b8d4}.md-typeset .admonition.info:focus-within,.md-typeset details.info:focus-within{box-shadow:0 0 0 .2rem #00b8d41a}.md-typeset .info>.admonition-title,.md-typeset .info>summary{background-color:#00b8d41a}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info)}.md-typeset .info>.admonition-title:after,.md-typeset .info>summary:after{color:#00b8d4}.md-typeset .admonition.tip,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .admonition.tip:focus-within,.md-typeset details.tip:focus-within{box-shadow:0 0 0 .2rem #00bfa51a}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:#00bfa51a}.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip)}.md-typeset .tip>.admonition-title:after,.md-typeset .tip>summary:after{color:#00bfa5}.md-typeset .admonition.success,.md-typeset details.success{border-color:#00c853}.md-typeset .admonition.success:focus-within,.md-typeset details.success:focus-within{box-shadow:0 0 0 .2rem #00c8531a}.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:#00c8531a}.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success)}.md-typeset .success>.admonition-title:after,.md-typeset .success>summary:after{color:#00c853}.md-typeset .admonition.question,.md-typeset details.question{border-color:#64dd17}.md-typeset .admonition.question:focus-within,.md-typeset details.question:focus-within{box-shadow:0 0 0 .2rem #64dd171a}.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:#64dd171a}.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question)}.md-typeset .question>.admonition-title:after,.md-typeset .question>summary:after{color:#64dd17}.md-typeset .admonition.warning,.md-typeset details.warning{border-color:#ff9100}.md-typeset .admonition.warning:focus-within,.md-typeset details.warning:focus-within{box-shadow:0 0 0 .2rem #ff91001a}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:#ff91001a}.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning)}.md-typeset .warning>.admonition-title:after,.md-typeset .warning>summary:after{color:#ff9100}.md-typeset .admonition.failure,.md-typeset details.failure{border-color:#ff5252}.md-typeset .admonition.failure:focus-within,.md-typeset details.failure:focus-within{box-shadow:0 0 0 .2rem #ff52521a}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary{background-color:#ff52521a}.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure)}.md-typeset .failure>.admonition-title:after,.md-typeset .failure>summary:after{color:#ff5252}.md-typeset .admonition.danger,.md-typeset details.danger{border-color:#ff1744}.md-typeset .admonition.danger:focus-within,.md-typeset details.danger:focus-within{box-shadow:0 0 0 .2rem #ff17441a}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary{background-color:#ff17441a}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger)}.md-typeset .danger>.admonition-title:after,.md-typeset .danger>summary:after{color:#ff1744}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .admonition.bug:focus-within,.md-typeset details.bug:focus-within{box-shadow:0 0 0 .2rem #f500571a}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:#f500571a}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug)}.md-typeset .bug>.admonition-title:after,.md-typeset .bug>summary:after{color:#f50057}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .admonition.example:focus-within,.md-typeset details.example:focus-within{box-shadow:0 0 0 .2rem #7c4dff1a}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:#7c4dff1a}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example)}.md-typeset .example>.admonition-title:after,.md-typeset .example>summary:after{color:#7c4dff}.md-typeset .admonition.quote,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .admonition.quote:focus-within,.md-typeset details.quote:focus-within{box-shadow:0 0 0 .2rem #9e9e9e1a}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:#9e9e9e1a}.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote)}.md-typeset .quote>.admonition-title:after,.md-typeset .quote>summary:after{color:#9e9e9e}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}[dir=ltr] .md-typeset .footnote>ol{margin-left:0}[dir=rtl] .md-typeset .footnote>ol{margin-right:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:focus-within .footnote-backref{opacity:1;transform:translateX(0);transition:none}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentcolor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}[dir=ltr] .md-typeset .headerlink{margin-left:.5rem}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{--md-scroll-margin:3.6rem;--md-scroll-offset:0rem;scroll-margin-top:calc(var(--md-scroll-margin) - var(--md-scroll-offset))}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{--md-scroll-margin:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{--md-scroll-offset:0.2rem}.md-typeset h4:target{--md-scroll-offset:0.15rem}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.984375em){.md-typeset div.arithmatex{margin:0 -.8rem}.md-typeset div.arithmatex>*{width:min-content}}.md-typeset div.arithmatex>*{margin-left:auto!important;margin-right:auto!important;padding:0 .8rem;touch-action:auto}.md-typeset div.arithmatex>* mjx-container{margin:0!important}.md-typeset div.arithmatex mjx-assistive-mml{height:0}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{-webkit-box-decoration-break:clone;box-decoration-break:clone;color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}[dir=ltr] .md-typeset summary{padding-right:1.8rem}[dir=rtl] .md-typeset summary{padding-left:1.8rem}[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset summary{cursor:pointer;display:block;min-height:1rem;overflow:hidden}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[dir=ltr] .md-typeset summary:after{right:.4rem}[dir=rtl] .md-typeset summary:after{left:.4rem}.md-typeset summary:after{background-color:currentcolor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{transform:rotate(180deg)}.md-typeset summary::marker{display:none}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{--md-icon-size:1.125em;display:inline-flex;height:var(--md-icon-size);vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentcolor;max-height:100%;width:var(--md-icon-size)}.md-typeset .lg,.md-typeset .xl,.md-typeset .xxl,.md-typeset .xxxl{vertical-align:text-bottom}.md-typeset .middle{vertical-align:middle}.md-typeset .lg{--md-icon-size:1.5em}.md-typeset .xl{--md-icon-size:2.25em}.md-typeset .xxl{--md-icon-size:3em}.md-typeset .xxxl{--md-icon-size:4em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color--light);box-shadow:2px 0 0 0 var(--md-code-hl-color) inset;display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight span.filename{background-color:var(--md-code-bg-color);border-bottom:.05rem solid var(--md-default-fg-color--lightest);border-top-left-radius:.1rem;border-top-right-radius:.1rem;display:flow-root;font-size:.85em;font-weight:700;margin-top:1em;padding:.6617647059em 1.1764705882em;position:relative}.highlight span.filename+pre{margin-top:0}.highlight span.filename+pre>code{border-top-left-radius:0;border-top-right-radius:0}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:sticky;-webkit-user-select:none;user-select:none;z-index:3}.highlight code a[id]{position:absolute;visibility:hidden}.highlight code[data-md-copying]{display:initial}.highlight code[data-md-copying] .hll{display:contents}.highlight code[data-md-copying] .md-annotation{display:none}.highlighttable{display:flow-root}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable th.filename{flex-grow:1;padding:0;text-align:left}.highlighttable th.filename span.filename{margin-top:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-top-left-radius:.1rem;font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;padding-right:.5882352941em}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;min-width:0}.linenodiv a{color:inherit}.md-typeset .highlighttable{direction:ltr;margin:1em 0}.md-typeset .highlighttable>tbody>tr>.code>div>pre>code{border-bottom-left-radius:0;border-top-left-radius:0}.md-typeset .highlight+.result{border:.05rem solid var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-bottom-right-radius:.1rem;border-top-width:.1rem;margin-top:-1.125em;overflow:visible;padding:0 1em}.md-typeset .highlight+.result:after{clear:both;content:"";display:block}@media screen and (max-width:44.984375em){.md-content__inner>.highlight{margin:1em -.8rem}.md-content__inner>.highlight>.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.code>div>pre>code,.md-content__inner>.highlight>.highlighttable>tbody>tr>.filename span.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.linenos,.md-content__inner>.highlight>pre>code{border-radius:0}.md-content__inner>.highlight+.result{border-left-width:0;border-radius:0;border-right-width:0;margin-left:-.8rem;margin-right:-.8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"↓";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"←";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"→";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"↑";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"⌫";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"⇤";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"⇪";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"⌧";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"☰";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"⌦";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"⏏";padding-right:.4em}.md-typeset .keys .key-end:before{content:"⤓";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"⎋";padding-right:.4em}.md-typeset .keys .key-home:before{content:"⤒";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"⎀";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"⇟";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"⇞";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"⎙";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"⇥";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"⌤";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"⏎";padding-left:.4em}:root{--md-tabbed-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-tabbed-icon--next:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-flow:column wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:target{--md-scroll-offset:0.625em}.md-typeset .tabbed-set>input.focus-visible~.tabbed-labels:before{background-color:var(--md-accent-fg-color)}.md-typeset .tabbed-labels{-ms-overflow-style:none;box-shadow:0 -.05rem var(--md-default-fg-color--lightest) inset;display:flex;max-width:100%;overflow:auto;scrollbar-width:none}@media print{.md-typeset .tabbed-labels{display:contents}}@media screen{.js .md-typeset .tabbed-labels{position:relative}.js .md-typeset .tabbed-labels:before{background:var(--md-default-fg-color);bottom:0;content:"";display:block;height:2px;left:0;position:absolute;transform:translateX(var(--md-indicator-x));transition:width 225ms,background-color .25s,transform .25s;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--md-indicator-width)}}.md-typeset .tabbed-labels::-webkit-scrollbar{display:none}.md-typeset .tabbed-labels>label{border-bottom:.1rem solid #0000;border-radius:.1rem .1rem 0 0;color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;font-size:.64rem;font-weight:700;padding:.78125em 1.25em .625em;scroll-margin-inline-start:1rem;transition:background-color .25s,color .25s;white-space:nowrap;width:auto}@media print{.md-typeset .tabbed-labels>label:first-child{order:1}.md-typeset .tabbed-labels>label:nth-child(2){order:2}.md-typeset .tabbed-labels>label:nth-child(3){order:3}.md-typeset .tabbed-labels>label:nth-child(4){order:4}.md-typeset .tabbed-labels>label:nth-child(5){order:5}.md-typeset .tabbed-labels>label:nth-child(6){order:6}.md-typeset .tabbed-labels>label:nth-child(7){order:7}.md-typeset .tabbed-labels>label:nth-child(8){order:8}.md-typeset .tabbed-labels>label:nth-child(9){order:9}.md-typeset .tabbed-labels>label:nth-child(10){order:10}.md-typeset .tabbed-labels>label:nth-child(11){order:11}.md-typeset .tabbed-labels>label:nth-child(12){order:12}.md-typeset .tabbed-labels>label:nth-child(13){order:13}.md-typeset .tabbed-labels>label:nth-child(14){order:14}.md-typeset .tabbed-labels>label:nth-child(15){order:15}.md-typeset .tabbed-labels>label:nth-child(16){order:16}.md-typeset .tabbed-labels>label:nth-child(17){order:17}.md-typeset .tabbed-labels>label:nth-child(18){order:18}.md-typeset .tabbed-labels>label:nth-child(19){order:19}.md-typeset .tabbed-labels>label:nth-child(20){order:20}}.md-typeset .tabbed-labels>label:hover{color:var(--md-default-fg-color)}.md-typeset .tabbed-labels>label>[href]:first-child{color:inherit}.md-typeset .tabbed-labels--linked>label{padding:0}.md-typeset .tabbed-labels--linked>label>a{display:block;padding:.78125em 1.25em .625em}.md-typeset .tabbed-content{width:100%}@media print{.md-typeset .tabbed-content{display:contents}}.md-typeset .tabbed-block{display:none}@media print{.md-typeset .tabbed-block{display:block}.md-typeset .tabbed-block:first-child{order:1}.md-typeset .tabbed-block:nth-child(2){order:2}.md-typeset .tabbed-block:nth-child(3){order:3}.md-typeset .tabbed-block:nth-child(4){order:4}.md-typeset .tabbed-block:nth-child(5){order:5}.md-typeset .tabbed-block:nth-child(6){order:6}.md-typeset .tabbed-block:nth-child(7){order:7}.md-typeset .tabbed-block:nth-child(8){order:8}.md-typeset .tabbed-block:nth-child(9){order:9}.md-typeset .tabbed-block:nth-child(10){order:10}.md-typeset .tabbed-block:nth-child(11){order:11}.md-typeset .tabbed-block:nth-child(12){order:12}.md-typeset .tabbed-block:nth-child(13){order:13}.md-typeset .tabbed-block:nth-child(14){order:14}.md-typeset .tabbed-block:nth-child(15){order:15}.md-typeset .tabbed-block:nth-child(16){order:16}.md-typeset .tabbed-block:nth-child(17){order:17}.md-typeset .tabbed-block:nth-child(18){order:18}.md-typeset .tabbed-block:nth-child(19){order:19}.md-typeset .tabbed-block:nth-child(20){order:20}}.md-typeset .tabbed-block>.highlight:first-child>pre,.md-typeset .tabbed-block>pre:first-child{margin:0}.md-typeset .tabbed-block>.highlight:first-child>pre>code,.md-typeset .tabbed-block>pre:first-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child>.filename{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable{margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.filename span.filename,.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.linenos{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.code>div>pre>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child+.result{margin-top:-.125em}.md-typeset .tabbed-block>.tabbed-set{margin:0}.md-typeset .tabbed-button{align-self:center;border-radius:100%;color:var(--md-default-fg-color--light);cursor:pointer;display:block;height:.9rem;margin-top:.1rem;pointer-events:auto;transition:background-color .25s;width:.9rem}.md-typeset .tabbed-button:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .tabbed-button:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-tabbed-icon--prev);mask-image:var(--md-tabbed-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color .25s,transform .25s;width:100%}.md-typeset .tabbed-control{background:linear-gradient(to right,var(--md-default-bg-color) 60%,#0000);display:flex;height:1.9rem;justify-content:start;pointer-events:none;position:absolute;transition:opacity 125ms;width:1.2rem}[dir=rtl] .md-typeset .tabbed-control{transform:rotate(180deg)}.md-typeset .tabbed-control[hidden]{opacity:0}.md-typeset .tabbed-control--next{background:linear-gradient(to left,var(--md-default-bg-color) 60%,#0000);justify-content:end;right:0}.md-typeset .tabbed-control--next .tabbed-button:after{-webkit-mask-image:var(--md-tabbed-icon--next);mask-image:var(--md-tabbed-icon--next)}@media screen and (max-width:44.984375em){[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels{padding-right:.8rem}.md-content__inner>.tabbed-set .tabbed-labels{margin:0 -.8rem;max-width:100vw;scroll-padding-inline-start:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-left:.8rem}.md-content__inner>.tabbed-set .tabbed-labels:after{content:""}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-right:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-left:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-right:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{width:2rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-left:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-right:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-left:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{width:2rem}}@media screen{.md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){color:var(--md-default-fg-color)}.md-typeset .no-js .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .no-js .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .no-js .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .no-js .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .no-js .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .no-js .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .no-js .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .no-js .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .no-js .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .no-js .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .no-js .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .no-js .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .no-js .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .no-js .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .no-js .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .no-js .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .no-js .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .no-js .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .no-js .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .no-js .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.no-js .md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.no-js .md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.no-js .md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.no-js .md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.no-js .md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.no-js .md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.no-js .md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.no-js .md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.no-js .md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.no-js .md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.no-js .md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.no-js .md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.no-js .md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.no-js .md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.no-js .md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.no-js .md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.no-js .md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.no-js .md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.no-js .md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.no-js .md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){border-color:var(--md-default-fg-color)}}.md-typeset .tabbed-set>input:first-child.focus-visible~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10).focus-visible~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11).focus-visible~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12).focus-visible~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13).focus-visible~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14).focus-visible~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15).focus-visible~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16).focus-visible~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17).focus-visible~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18).focus-visible~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19).focus-visible~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2).focus-visible~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20).focus-visible~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3).focus-visible~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4).focus-visible~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5).focus-visible~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6).focus-visible~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7).focus-visible~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8).focus-visible~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9).focus-visible~.tabbed-labels>:nth-child(9){color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:first-child:checked~.tabbed-content>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-content>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-content>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-content>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-content>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-content>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-content>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-content>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-content>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-content>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-content>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-content>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-content>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-content>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-content>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-content>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-content>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-content>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-content>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-content>:nth-child(9){display:block}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}[dir=ltr] .md-typeset .task-list-item [type=checkbox]{left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}[dir=ltr] .md-typeset .task-list-indicator:before{left:-1.5em}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}@media print{.giscus,[id=__comments]{display:none}}:root>*{--md-mermaid-font-family:var(--md-text-font-family),sans-serif;--md-mermaid-edge-color:var(--md-code-fg-color);--md-mermaid-node-bg-color:var(--md-accent-fg-color--transparent);--md-mermaid-node-fg-color:var(--md-accent-fg-color);--md-mermaid-label-bg-color:var(--md-default-bg-color);--md-mermaid-label-fg-color:var(--md-code-fg-color);--md-mermaid-sequence-actor-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actor-fg-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-actor-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-actor-line-color:var(--md-default-fg-color--lighter);--md-mermaid-sequence-actorman-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actorman-line-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-box-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-box-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-label-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-label-fg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-loop-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-loop-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-loop-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-message-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-message-line-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-note-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-border-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-number-bg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-number-fg-color:var(--md-accent-bg-color)}.mermaid{line-height:normal;margin:1em 0}.md-typeset .grid{grid-gap:.4rem;display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));margin:1em 0}.md-typeset .grid.cards>ol,.md-typeset .grid.cards>ul{display:contents}.md-typeset .grid.cards>ol>li,.md-typeset .grid.cards>ul>li,.md-typeset .grid>.card{border:.05rem solid var(--md-default-fg-color--lightest);border-radius:.1rem;display:block;margin:0;padding:.8rem;transition:border .25s,box-shadow .25s}.md-typeset .grid.cards>ol>li:focus-within,.md-typeset .grid.cards>ol>li:hover,.md-typeset .grid.cards>ul>li:focus-within,.md-typeset .grid.cards>ul>li:hover,.md-typeset .grid>.card:focus-within,.md-typeset .grid>.card:hover{border-color:#0000;box-shadow:var(--md-shadow-z2)}.md-typeset .grid.cards>ol>li>hr,.md-typeset .grid.cards>ul>li>hr,.md-typeset .grid>.card>hr{margin-bottom:1em;margin-top:1em}.md-typeset .grid.cards>ol>li>:first-child,.md-typeset .grid.cards>ul>li>:first-child,.md-typeset .grid>.card>:first-child{margin-top:0}.md-typeset .grid.cards>ol>li>:last-child,.md-typeset .grid.cards>ul>li>:last-child,.md-typeset .grid>.card>:last-child{margin-bottom:0}.md-typeset .grid>*,.md-typeset .grid>.admonition,.md-typeset .grid>.highlight>*,.md-typeset .grid>.highlighttable,.md-typeset .grid>.md-typeset details,.md-typeset .grid>details,.md-typeset .grid>pre{margin-bottom:0;margin-top:0}.md-typeset .grid>.highlight>pre:only-child,.md-typeset .grid>.highlight>pre>code,.md-typeset .grid>.highlighttable,.md-typeset .grid>.highlighttable>tbody,.md-typeset .grid>.highlighttable>tbody>tr,.md-typeset .grid>.highlighttable>tbody>tr>.code,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre>code{height:100%}.md-typeset .grid>.tabbed-set{margin-bottom:0;margin-top:0}@media screen and (min-width:45em){[dir=ltr] .md-typeset .inline{float:left}[dir=rtl] .md-typeset .inline{float:right}[dir=ltr] .md-typeset .inline{margin-right:.8rem}[dir=rtl] .md-typeset .inline{margin-left:.8rem}.md-typeset .inline{margin-bottom:.8rem;margin-top:0;width:11.7rem}[dir=ltr] .md-typeset .inline.end{float:right}[dir=rtl] .md-typeset .inline.end{float:left}[dir=ltr] .md-typeset .inline.end{margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{margin-left:0;margin-right:.8rem}} \ No newline at end of file diff --git a/HEAD/assets/stylesheets/main.0253249f.min.css.map b/HEAD/assets/stylesheets/main.0253249f.min.css.map deleted file mode 100644 index 748194709..000000000 --- a/HEAD/assets/stylesheets/main.0253249f.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["src/templates/assets/stylesheets/main/components/_meta.scss","../../../../src/templates/assets/stylesheets/main.scss","src/templates/assets/stylesheets/main/_resets.scss","src/templates/assets/stylesheets/main/_colors.scss","src/templates/assets/stylesheets/main/_icons.scss","src/templates/assets/stylesheets/main/_typeset.scss","src/templates/assets/stylesheets/utilities/_break.scss","src/templates/assets/stylesheets/main/components/_author.scss","src/templates/assets/stylesheets/main/components/_banner.scss","src/templates/assets/stylesheets/main/components/_base.scss","src/templates/assets/stylesheets/main/components/_clipboard.scss","src/templates/assets/stylesheets/main/components/_code.scss","src/templates/assets/stylesheets/main/components/_consent.scss","src/templates/assets/stylesheets/main/components/_content.scss","src/templates/assets/stylesheets/main/components/_dialog.scss","src/templates/assets/stylesheets/main/components/_feedback.scss","src/templates/assets/stylesheets/main/components/_footer.scss","src/templates/assets/stylesheets/main/components/_form.scss","src/templates/assets/stylesheets/main/components/_header.scss","node_modules/material-design-color/material-color.scss","src/templates/assets/stylesheets/main/components/_nav.scss","src/templates/assets/stylesheets/main/components/_pagination.scss","src/templates/assets/stylesheets/main/components/_post.scss","src/templates/assets/stylesheets/main/components/_progress.scss","src/templates/assets/stylesheets/main/components/_search.scss","src/templates/assets/stylesheets/main/components/_select.scss","src/templates/assets/stylesheets/main/components/_sidebar.scss","src/templates/assets/stylesheets/main/components/_source.scss","src/templates/assets/stylesheets/main/components/_status.scss","src/templates/assets/stylesheets/main/components/_tabs.scss","src/templates/assets/stylesheets/main/components/_tag.scss","src/templates/assets/stylesheets/main/components/_tooltip.scss","src/templates/assets/stylesheets/main/components/_tooltip2.scss","src/templates/assets/stylesheets/main/components/_top.scss","src/templates/assets/stylesheets/main/components/_version.scss","src/templates/assets/stylesheets/main/extensions/markdown/_admonition.scss","src/templates/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/templates/assets/stylesheets/main/extensions/markdown/_toc.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/templates/assets/stylesheets/main/integrations/_giscus.scss","src/templates/assets/stylesheets/main/integrations/_mermaid.scss","src/templates/assets/stylesheets/main/modifiers/_grid.scss","src/templates/assets/stylesheets/main/modifiers/_inline.scss"],"names":[],"mappings":"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,4DACE,oBlC05GJ,CkCv5GI,sFACE,gClCy5GN,CkCp5GE,oEACE,0BlCu5GJ,CkCp5GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCs5GN,CkCl5GI,gFACE,alCo5GN,CkCz6GE,4DACE,oBlC46GJ,CkCz6GI,sFACE,gClC26GN,CkCt6GE,oEACE,0BlCy6GJ,CkCt6GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCw6GN,CkCp6GI,gFACE,alCs6GN,CkC37GE,0DACE,oBlC87GJ,CkC37GI,oFACE,gClC67GN,CkCx7GE,kEACE,0BlC27GJ,CkCx7GI,gFACE,wBAlBG,CAmBH,oDAAA,CAAA,4ClC07GN,CkCt7GI,8EACE,alCw7GN,CkC78GE,oDACE,oBlCg9GJ,CkC78GI,8EACE,gClC+8GN,CkC18GE,4DACE,0BlC68GJ,CkC18GI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yClC48GN,CkCx8GI,wEACE,alC08GN,CkC/9GE,4DACE,oBlCk+GJ,CkC/9GI,sFACE,gClCi+GN,CkC59GE,oEACE,0BlC+9GJ,CkC59GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClC89GN,CkC19GI,gFACE,alC49GN,CkCj/GE,wDACE,oBlCo/GJ,CkCj/GI,kFACE,gClCm/GN,CkC9+GE,gEACE,0BlCi/GJ,CkC9+GI,8EACE,wBAlBG,CAmBH,mDAAA,CAAA,2ClCg/GN,CkC5+GI,4EACE,alC8+GN,CmClpHA,MACE,qMnCqpHF,CmC5oHE,sBAEE,uCAAA,CADA,gBnCgpHJ,CmC5oHI,mCACE,anC8oHN,CmC/oHI,mCACE,cnC8oHN,CmC1oHM,4BACE,sBnC4oHR,CmCzoHQ,mCACE,gCnC2oHV,CmCvoHQ,2DACE,SAAA,CAEA,uBAAA,CADA,enC0oHV,CmCroHQ,yGACE,SAAA,CACA,uBnCuoHV,CmCnoHQ,yCACE,YnCqoHV,CmC9nHE,0BACE,eAAA,CACA,enCgoHJ,CmC7nHI,+BACE,oBnC+nHN,CmC1nHE,gDACE,YnC4nHJ,CmCxnHE,8BAIE,+BAAA,CAHA,oBAAA,CAEA,WAAA,CAGA,SAAA,CAKA,4BAAA,CAJA,4DACE,CAHF,0BnC4nHJ,CmCnnHI,aAdF,8BAeI,+BAAA,CACA,SAAA,CACA,uBnCsnHJ,CACF,CmCnnHI,wCACE,6BnCqnHN,CmCjnHI,oCACE,+BnCmnHN,CmC/mHI,qCAKE,6BAAA,CADA,UAAA,CAHA,oBAAA,CAEA,YAAA,CAGA,2CAAA,CAAA,mCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CAPA,WnCwnHN,CmC3mHQ,mDACE,oBnC6mHV,CoC3tHE,kCAEE,iBpCiuHJ,CoCnuHE,kCAEE,kBpCiuHJ,CoCnuHE,wBAGE,yCAAA,CAFA,oBAAA,CAGA,SAAA,CACA,mCpC8tHJ,CoCztHI,aAVF,wBAWI,YpC4tHJ,CACF,CoCxtHE,6FAEE,SAAA,CACA,mCpC0tHJ,CoCptHE,4FAEE,+BpCstHJ,CoCltHE,oBACE,yBAAA,CACA,uBAAA,CAGA,yEpCktHJ,CKnlHI,sC+BrHE,qDACE,uBpC2sHN,CACF,CoCtsHE,kEACE,yBpCwsHJ,CoCpsHE,sBACE,0BpCssHJ,CqCjwHE,2BACE,arCowHJ,CK/kHI,0CgCtLF,2BAKI,erCowHJ,CqCjwHI,6BACE,iBrCmwHN,CACF,CqC/vHI,6BAEE,0BAAA,CAAA,2BAAA,CADA,eAAA,CAEA,iBrCiwHN,CqC9vHM,2CACE,kBrCgwHR,CqC1vHI,6CACE,QrC4vHN,CsCxxHE,uBACE,4CtC4xHJ,CsCvxHE,8CAJE,kCAAA,CAAA,0BtC+xHJ,CsC3xHE,uBACE,4CtC0xHJ,CsCrxHE,4BAEE,kCAAA,CAAA,0BAAA,CADA,qCtCwxHJ,CsCpxHI,mCACE,atCsxHN,CsClxHI,kCACE,atCoxHN,CsC/wHE,0BAKE,eAAA,CAJA,aAAA,CAEA,YAAA,CACA,aAAA,CAFA,kBAAA,CAAA,mBtCoxHJ,CsC9wHI,uCACE,etCgxHN,CsC5wHI,sCACE,kBtC8wHN,CuC3zHA,MACE,oLvC8zHF,CuCrzHE,oBAGE,iBAAA,CAEA,gBAAA,CADA,avCuzHJ,CuCnzHI,wCACE,uBvCqzHN,CuCjzHI,gCAEE,eAAA,CADA,gBvCozHN,CuC7yHM,wCACE,mBvC+yHR,CuCzyHE,8BAKE,oBvC6yHJ,CuClzHE,8BAKE,mBvC6yHJ,CuClzHE,8BAUE,4BvCwyHJ,CuClzHE,4DAWE,6BvCuyHJ,CuClzHE,8BAWE,4BvCuyHJ,CuClzHE,oBASE,cAAA,CANA,aAAA,CACA,eAAA,CAIA,evC0yHJ,CuCpyHI,kCACE,uCAAA,CACA,oBvCsyHN,CuClyHI,wCAEE,uCAAA,CADA,YvCqyHN,CuChyHI,oCAEE,WvC6yHN,CuC/yHI,oCAEE,UvC6yHN,CuC/yHI,0BAOE,6BAAA,CADA,UAAA,CADA,WAAA,CAGA,yCAAA,CAAA,iCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CAVA,iBAAA,CAEA,UAAA,CAUA,sBAAA,CADA,yBAAA,CARA,UvC2yHN,CuC/xHM,oCACE,wBvCiyHR,CuC5xHI,4BACE,YvC8xHN,CuCzxHI,4CACE,YvC2xHN,CwCr3HE,+DACE,sBAAA,CAEA,mBAAA,CACA,0BAAA,CACA,uBxCu3HJ,CwCp3HI,2EAGE,iBAAA,CADA,eAAA,CADA,yBxCw3HN,CwCj3HE,mEACE,0BxCm3HJ,CwC/2HE,oBACE,qBxCi3HJ,CwC72HE,gBACE,oBxC+2HJ,CwC32HE,gBACE,qBxC62HJ,CwCz2HE,iBACE,kBxC22HJ,CwCv2HE,kBACE,kBxCy2HJ,CyCl5HE,6BACE,sCzCq5HJ,CyCl5HE,cACE,yCzCo5HJ,CyCx4HE,sIACE,oCzC04HJ,CyCl4HE,2EACE,qCzCo4HJ,CyC13HE,wGACE,oCzC43HJ,CyCn3HE,yFACE,qCzCq3HJ,CyCh3HE,6BACE,kCzCk3HJ,CyC52HE,6CACE,sCzC82HJ,CyCv2HE,4DACE,sCzCy2HJ,CyCl2HE,4DACE,qCzCo2HJ,CyC31HE,yFACE,qCzC61HJ,CyCr1HE,2EACE,sCzCu1HJ,CyC50HE,wHACE,qCzC80HJ,CyCz0HE,8BAGE,mBAAA,CADA,gBAAA,CADA,gBzC60HJ,CyCx0HE,eACE,4CzC00HJ,CyCv0HE,eACE,4CzCy0HJ,CyCr0HE,gBAIE,+CAAA,CACA,kDAAA,CAJA,aAAA,CAEA,wBAAA,CADA,wBzC00HJ,CyCn0HE,yBAOE,wCAAA,CACA,+DAAA,CACA,4BAAA,CACA,6BAAA,CARA,iBAAA,CAGA,eAAA,CACA,eAAA,CAFA,cAAA,CADA,oCAAA,CAFA,iBzC80HJ,CyCl0HI,6BACE,YzCo0HN,CyCj0HM,kCACE,wBAAA,CACA,yBzCm0HR,CyC7zHE,iCAaE,wCAAA,CACA,+DAAA,CAJA,uCAAA,CACA,0BAAA,CALA,UAAA,CAJA,oBAAA,CAOA,2BAAA,CADA,2BAAA,CADA,2BAAA,CANA,eAAA,CAWA,wBAAA,CAAA,gBAAA,CAPA,SzCs0HJ,CyCpzHE,sBACE,iBAAA,CACA,iBzCszHJ,CyCjzHE,iCAKE,ezC+yHJ,CyC5yHI,sCACE,gBzC8yHN,CyC1yHI,gDACE,YzC4yHN,CyClyHA,gBACE,iBzCqyHF,CyCjyHE,yCACE,aAAA,CACA,SzCmyHJ,CyC9xHE,mBACE,YzCgyHJ,CyC3xHE,oBACE,QzC6xHJ,CyCzxHE,4BACE,WAAA,CACA,SAAA,CACA,ezC2xHJ,CyCxxHI,0CACE,YzC0xHN,CyCpxHE,yBAKE,wCAAA,CAEA,+BAAA,CADA,4BAAA,CAHA,eAAA,CADA,oDAAA,CAEA,wBAAA,CAAA,gBzCyxHJ,CyClxHE,2BAEE,+DAAA,CADA,2BzCqxHJ,CyCjxHI,+BACE,uCAAA,CACA,gBzCmxHN,CyC9wHE,sBACE,MAAA,CACA,WzCgxHJ,CyC3wHA,aACE,azC8wHF,CyCpwHE,4BAEE,aAAA,CADA,YzCwwHJ,CyCpwHI,wDAEE,2BAAA,CADA,wBzCuwHN,CyCjwHE,+BAKE,2CAAA,CAEA,+BAAA,CADA,gCAAA,CADA,sBAAA,CAHA,mBAAA,CACA,gBAAA,CAFA,azCywHJ,CyChwHI,qCAEE,UAAA,CACA,UAAA,CAFA,azCowHN,CK34HI,0CoCsJF,8BACE,iBzCyvHF,CyC/uHE,wSAGE,ezCqvHJ,CyCjvHE,sCAEE,mBAAA,CACA,eAAA,CADA,oBAAA,CADA,kBAAA,CAAA,mBzCqvHJ,CACF,C0CllII,yDAIE,+BAAA,CACA,8BAAA,CAFA,aAAA,CADA,QAAA,CADA,iB1CwlIN,C0ChlII,uBAEE,uCAAA,CADA,c1CmlIN,C0C9hIM,iHAEE,WAlDkB,CAiDlB,kB1CyiIR,C0C1iIM,6HAEE,WAlDkB,CAiDlB,kB1CqjIR,C0CtjIM,6HAEE,WAlDkB,CAiDlB,kB1CikIR,C0ClkIM,oHAEE,WAlDkB,CAiDlB,kB1C6kIR,C0C9kIM,0HAEE,WAlDkB,CAiDlB,kB1CylIR,C0C1lIM,uHAEE,WAlDkB,CAiDlB,kB1CqmIR,C0CtmIM,uHAEE,WAlDkB,CAiDlB,kB1CinIR,C0ClnIM,6HAEE,WAlDkB,CAiDlB,kB1C6nIR,C0C9nIM,yCAEE,WAlDkB,CAiDlB,kB1CioIR,C0CloIM,yCAEE,WAlDkB,CAiDlB,kB1CqoIR,C0CtoIM,0CAEE,WAlDkB,CAiDlB,kB1CyoIR,C0C1oIM,uCAEE,WAlDkB,CAiDlB,kB1C6oIR,C0C9oIM,wCAEE,WAlDkB,CAiDlB,kB1CipIR,C0ClpIM,sCAEE,WAlDkB,CAiDlB,kB1CqpIR,C0CtpIM,wCAEE,WAlDkB,CAiDlB,kB1CypIR,C0C1pIM,oCAEE,WAlDkB,CAiDlB,kB1C6pIR,C0C9pIM,2CAEE,WAlDkB,CAiDlB,kB1CiqIR,C0ClqIM,qCAEE,WAlDkB,CAiDlB,kB1CqqIR,C0CtqIM,oCAEE,WAlDkB,CAiDlB,kB1CyqIR,C0C1qIM,kCAEE,WAlDkB,CAiDlB,kB1C6qIR,C0C9qIM,qCAEE,WAlDkB,CAiDlB,kB1CirIR,C0ClrIM,mCAEE,WAlDkB,CAiDlB,kB1CqrIR,C0CtrIM,qCAEE,WAlDkB,CAiDlB,kB1CyrIR,C0C1rIM,wCAEE,WAlDkB,CAiDlB,kB1C6rIR,C0C9rIM,sCAEE,WAlDkB,CAiDlB,kB1CisIR,C0ClsIM,2CAEE,WAlDkB,CAiDlB,kB1CqsIR,C0C1rIM,iCAEE,WAPkB,CAMlB,iB1C6rIR,C0C9rIM,uCAEE,WAPkB,CAMlB,iB1CisIR,C0ClsIM,mCAEE,WAPkB,CAMlB,iB1CqsIR,C2CvxIA,MACE,2LAAA,CACA,yL3C0xIF,C2CjxIE,wBAKE,mBAAA,CAHA,YAAA,CACA,qBAAA,CACA,YAAA,CAHA,iB3CwxIJ,C2C9wII,8BAGE,QAAA,CACA,SAAA,CAHA,iBAAA,CACA,O3CkxIN,C2C7wIM,qCACE,0B3C+wIR,C2ClvIM,kEACE,0C3CovIR,C2C9uIE,2BAME,uBAAA,CADA,+DAAA,CAJA,YAAA,CACA,cAAA,CACA,aAAA,CACA,oB3CkvIJ,C2C7uII,aATF,2BAUI,gB3CgvIJ,CACF,C2C7uII,cAGE,+BACE,iB3C6uIN,C2C1uIM,sCAQE,qCAAA,CANA,QAAA,CAKA,UAAA,CAHA,aAAA,CAEA,UAAA,CAHA,MAAA,CAFA,iBAAA,CAaA,2CAAA,CALA,2DACE,CAGF,kDAAA,CARA,+B3CkvIR,CACF,C2CpuII,8CACE,Y3CsuIN,C2CluII,iCAUE,+BAAA,CACA,6BAAA,CALA,uCAAA,CAEA,cAAA,CAPA,aAAA,CAGA,gBAAA,CACA,eAAA,CAFA,8BAAA,CAMA,+BAAA,CAGA,2CACE,CANF,kBAAA,CALA,U3C8uIN,C2C/tIM,aAII,6CACE,O3C8tIV,C2C/tIQ,8CACE,O3CiuIV,C2CluIQ,8CACE,O3CouIV,C2CruIQ,8CACE,O3CuuIV,C2CxuIQ,8CACE,O3C0uIV,C2C3uIQ,8CACE,O3C6uIV,C2C9uIQ,8CACE,O3CgvIV,C2CjvIQ,8CACE,O3CmvIV,C2CpvIQ,8CACE,O3CsvIV,C2CvvIQ,+CACE,Q3CyvIV,C2C1vIQ,+CACE,Q3C4vIV,C2C7vIQ,+CACE,Q3C+vIV,C2ChwIQ,+CACE,Q3CkwIV,C2CnwIQ,+CACE,Q3CqwIV,C2CtwIQ,+CACE,Q3CwwIV,C2CzwIQ,+CACE,Q3C2wIV,C2C5wIQ,+CACE,Q3C8wIV,C2C/wIQ,+CACE,Q3CixIV,C2ClxIQ,+CACE,Q3CoxIV,C2CrxIQ,+CACE,Q3CuxIV,CACF,C2ClxIM,uCACE,gC3CoxIR,C2ChxIM,oDACE,a3CkxIR,C2C7wII,yCACE,S3C+wIN,C2C3wIM,2CACE,aAAA,CACA,8B3C6wIR,C2CvwIE,4BACE,U3CywIJ,C2CtwII,aAJF,4BAKI,gB3CywIJ,CACF,C2CrwIE,0BACE,Y3CuwIJ,C2CpwII,aAJF,0BAKI,a3CuwIJ,C2CnwIM,sCACE,O3CqwIR,C2CtwIM,uCACE,O3CwwIR,C2CzwIM,uCACE,O3C2wIR,C2C5wIM,uCACE,O3C8wIR,C2C/wIM,uCACE,O3CixIR,C2ClxIM,uCACE,O3CoxIR,C2CrxIM,uCACE,O3CuxIR,C2CxxIM,uCACE,O3C0xIR,C2C3xIM,uCACE,O3C6xIR,C2C9xIM,wCACE,Q3CgyIR,C2CjyIM,wCACE,Q3CmyIR,C2CpyIM,wCACE,Q3CsyIR,C2CvyIM,wCACE,Q3CyyIR,C2C1yIM,wCACE,Q3C4yIR,C2C7yIM,wCACE,Q3C+yIR,C2ChzIM,wCACE,Q3CkzIR,C2CnzIM,wCACE,Q3CqzIR,C2CtzIM,wCACE,Q3CwzIR,C2CzzIM,wCACE,Q3C2zIR,C2C5zIM,wCACE,Q3C8zIR,CACF,C2CxzII,+FAEE,Q3C0zIN,C2CvzIM,yGACE,wBAAA,CACA,yB3C0zIR,C2CjzIM,2DAEE,wBAAA,CACA,yBAAA,CAFA,Q3CqzIR,C2C9yIM,iEACE,Q3CgzIR,C2C7yIQ,qLAGE,wBAAA,CACA,yBAAA,CAFA,Q3CizIV,C2C3yIQ,6FACE,wBAAA,CACA,yB3C6yIV,C2CxyIM,yDACE,kB3C0yIR,C2CryII,sCACE,Q3CuyIN,C2ClyIE,2BAEE,iBAAA,CAOA,kBAAA,CAHA,uCAAA,CAEA,cAAA,CAPA,aAAA,CAGA,YAAA,CACA,gBAAA,CAEA,mBAAA,CAGA,gCAAA,CAPA,W3C2yIJ,C2CjyII,iCAEE,uDAAA,CADA,+B3CoyIN,C2C/xII,iCAKE,6BAAA,CADA,UAAA,CAHA,aAAA,CAEA,WAAA,CAGA,8CAAA,CAAA,sCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CACA,+CACE,CATF,U3CyyIN,C2C1xIE,4BAOE,yEACE,CANF,YAAA,CAGA,aAAA,CAFA,qBAAA,CAGA,mBAAA,CALA,iBAAA,CAYA,wBAAA,CATA,Y3CgyIJ,C2CpxII,sCACE,wB3CsxIN,C2ClxII,oCACE,S3CoxIN,C2ChxII,kCAGE,wEACE,CAFF,mBAAA,CADA,O3CoxIN,C2C1wIM,uDACE,8CAAA,CAAA,sC3C4wIR,CKn5II,0CsCqJF,wDAEE,kB3CowIF,C2CtwIA,wDAEE,mB3CowIF,C2CtwIA,8CAGE,eAAA,CAFA,eAAA,CAGA,iC3CkwIF,C2C9vIE,8DACE,mB3CiwIJ,C2ClwIE,8DACE,kB3CiwIJ,C2ClwIE,oDAEE,U3CgwIJ,C2C5vIE,8EAEE,kB3C+vIJ,C2CjwIE,8EAEE,mB3C+vIJ,C2CjwIE,8EAGE,kB3C8vIJ,C2CjwIE,8EAGE,mB3C8vIJ,C2CjwIE,oEACE,U3CgwIJ,C2C1vIE,8EAEE,mB3C6vIJ,C2C/vIE,8EAEE,kB3C6vIJ,C2C/vIE,8EAGE,mB3C4vIJ,C2C/vIE,8EAGE,kB3C4vIJ,C2C/vIE,oEACE,U3C8vIJ,CACF,C2ChvIE,cAHF,olDAII,gC3CmvIF,C2ChvIE,g8GACE,uC3CkvIJ,CACF,C2C7uIA,4sDACE,+B3CgvIF,C2C5uIA,wmDACE,a3C+uIF,C4CnnJA,MACE,qWAAA,CACA,8W5CsnJF,C4C7mJE,4BAEE,oBAAA,CADA,iB5CinJJ,C4C5mJI,sDAEE,S5C+mJN,C4CjnJI,sDAEE,U5C+mJN,C4CjnJI,4CACE,iBAAA,CAEA,S5C8mJN,C4CzmJE,+CAEE,SAAA,CADA,U5C4mJJ,C4CvmJE,kDAEE,W5CknJJ,C4CpnJE,kDAEE,Y5CknJJ,C4CpnJE,wCAOE,qDAAA,CADA,UAAA,CADA,aAAA,CAGA,0CAAA,CAAA,kCAAA,CAEA,4BAAA,CAAA,oBAAA,CADA,6BAAA,CAAA,qBAAA,CAEA,yBAAA,CAAA,iBAAA,CAVA,iBAAA,CAEA,SAAA,CACA,Y5CgnJJ,C4CrmJE,gEACE,wB1B2Wa,C0B1Wb,mDAAA,CAAA,2C5CumJJ,C6CvpJA,aAQE,wBACE,Y7CspJF,CACF,C8ChqJA,QACE,8DAAA,CAGA,+CAAA,CACA,iEAAA,CACA,oDAAA,CACA,sDAAA,CACA,mDAAA,CAGA,qEAAA,CACA,qEAAA,CACA,wEAAA,CACA,0EAAA,CACA,wEAAA,CACA,yEAAA,CACA,kEAAA,CACA,+DAAA,CACA,oEAAA,CACA,oEAAA,CACA,mEAAA,CACA,gEAAA,CACA,uEAAA,CACA,mEAAA,CACA,qEAAA,CACA,oEAAA,CACA,gEAAA,CACA,wEAAA,CACA,qEAAA,CACA,+D9C8pJF,C8CxpJA,SAEE,kBAAA,CADA,Y9C4pJF,C+C9rJE,kBAUE,cAAA,CATA,YAAA,CACA,kEACE,CAQF,Y/C0rJJ,C+CtrJI,sDACE,gB/CwrJN,C+ClrJI,oFAKE,wDAAA,CACA,mBAAA,CAJA,aAAA,CAEA,QAAA,CADA,aAAA,CAIA,sC/CorJN,C+C/qJM,iOACE,kBAAA,CACA,8B/CkrJR,C+C9qJM,6FACE,iBAAA,CAAA,c/CirJR,C+C7qJM,2HACE,Y/CgrJR,C+C5qJM,wHACE,e/C+qJR,C+ChqJI,yMAGE,eAAA,CAAA,Y/CwqJN,C+C1pJI,ybAOE,W/CgqJN,C+C5pJI,8BACE,eAAA,CAAA,Y/C8pJN,CK1lJI,mC2ChKA,8BACE,UhDkwJJ,CgDnwJE,8BACE,WhDkwJJ,CgDnwJE,8BAGE,kBhDgwJJ,CgDnwJE,8BAGE,iBhDgwJJ,CgDnwJE,oBAKE,mBAAA,CADA,YAAA,CAFA,ahDiwJJ,CgD3vJI,kCACE,WhD8vJN,CgD/vJI,kCACE,UhD8vJN,CgD/vJI,kCAEE,iBAAA,CAAA,chD6vJN,CgD/vJI,kCAEE,aAAA,CAAA,kBhD6vJN,CACF","file":"main.css"} \ No newline at end of file diff --git a/HEAD/assets/stylesheets/main.6f8fc17f.min.css b/HEAD/assets/stylesheets/main.6f8fc17f.min.css new file mode 100644 index 000000000..a0d06b0eb --- /dev/null +++ b/HEAD/assets/stylesheets/main.6f8fc17f.min.css @@ -0,0 +1 @@ +@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}@media (prefers-reduced-motion){*,:after,:before{transition:none!important}}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:initial;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:#0000;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-scheme=default]{color-scheme:light}[data-md-color-scheme=default] img[src$="#gh-dark-mode-only"],[data-md-color-scheme=default] img[src$="#only-dark"]{display:none}:root,[data-md-color-scheme=default]{--md-hue:225deg;--md-default-fg-color:#000000de;--md-default-fg-color--light:#0000008a;--md-default-fg-color--lighter:#00000052;--md-default-fg-color--lightest:#00000012;--md-default-bg-color:#fff;--md-default-bg-color--light:#ffffffb3;--md-default-bg-color--lighter:#ffffff4d;--md-default-bg-color--lightest:#ffffff1f;--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:#4287ff;--md-code-hl-color--light:#4287ff1a;--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-del-color:#f5503d26;--md-typeset-ins-color:#0bd57026;--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-mark-color:#ffff0080;--md-typeset-table-color:#0000001f;--md-typeset-table-color--light:rgba(0,0,0,.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-warning-fg-color:#000000de;--md-warning-bg-color:#ff9;--md-footer-fg-color:#fff;--md-footer-fg-color--light:#ffffffb3;--md-footer-fg-color--lighter:#ffffff73;--md-footer-bg-color:#000000de;--md-footer-bg-color--dark:#00000052;--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #0000001a,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0003,0 0 0.05rem #00000059}.md-icon svg{fill:currentcolor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--md-text-font-family:var(--md-text-font,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;--md-code-font-family:var(--md-code-font,_),SFMono-Regular,Consolas,Menlo,monospace}aside,body,input{font-feature-settings:"kern","liga";color:var(--md-typeset-color);font-family:var(--md-text-font-family)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family)}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin-bottom:1em;margin-top:1em}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset h5 code{text-transform:none}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a:focus code,.md-typeset a:hover code{background-color:var(--md-accent-fg-color--transparent)}.md-typeset a code{color:currentcolor;transition:background-color 125ms}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr;font-variant-ligatures:none}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;outline-color:var(--md-accent-fg-color);overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help;text-decoration:none}.md-typeset small{opacity:.75}[dir=ltr] .md-typeset sub,[dir=ltr] .md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em}[dir=ltr] .md-typeset blockquote{padding-left:.6rem}[dir=rtl] .md-typeset blockquote{padding-right:.6rem}[dir=ltr] .md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{border-right:.2rem solid var(--md-default-fg-color--lighter)}.md-typeset blockquote{color:var(--md-default-fg-color--light);margin-left:0;margin-right:0}.md-typeset ul{list-style-type:disc}.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol,[dir=ltr] .md-typeset ul{margin-left:.625em}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em}.md-typeset ol,.md-typeset ul{padding:0}.md-typeset ol:not([hidden]),.md-typeset ul:not([hidden]){display:flow-root}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol ol ol ol,.md-typeset ul ol ol ol{list-style-type:upper-alpha}.md-typeset ol ol ol ol ol,.md-typeset ul ol ol ol ol{list-style-type:upper-roman}.md-typeset ol[type],.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol li,[dir=ltr] .md-typeset ul li{margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}[dir=ltr] .md-typeset ol li ol,[dir=ltr] .md-typeset ol li ul,[dir=ltr] .md-typeset ul li ol,[dir=ltr] .md-typeset ul li ul{margin-left:.625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin-bottom:.5em;margin-top:.5em}[dir=ltr] .md-typeset dd{margin-left:1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em}.md-typeset dd{margin-bottom:1.5em;margin-top:1em}.md-typeset img,.md-typeset svg,.md-typeset video{height:auto;max-width:100%}.md-typeset img[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:1em auto;max-width:100%;text-align:center;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block;margin:0 auto}.md-typeset figcaption{font-style:italic;margin:1em auto;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:var(--md-typeset-table-color--light);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}[dir=ltr] .md-typeset table th[role=columnheader]:after{margin-left:.5em}[dir=rtl] .md-typeset table th[role=columnheader]:after{margin-right:.5em}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}@media screen and (max-width:44.984375em){.md-content__inner>pre{margin:1em -.8rem}.md-content__inner>pre code{border-radius:0}}.md-typeset .md-author{border-radius:100%;display:block;flex-shrink:0;height:1.6rem;overflow:hidden;position:relative;transition:color 125ms,transform 125ms;width:1.6rem}.md-typeset .md-author img{display:block}.md-typeset .md-author--more{background:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--lighter);font-size:.6rem;font-weight:700;line-height:1.6rem;text-align:center}.md-typeset .md-author--long{height:2.4rem;width:2.4rem}.md-typeset a.md-author{transform:scale(1)}.md-typeset a.md-author img{border-radius:100%;filter:grayscale(100%) opacity(75%);transition:filter 125ms}.md-typeset a.md-author:focus,.md-typeset a.md-author:hover{transform:scale(1.1);z-index:1}.md-typeset a.md-author:focus img,.md-typeset a.md-author:hover img{filter:grayscale(0)}.md-banner{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color);overflow:auto}@media print{.md-banner{display:none}}.md-banner--warning{background-color:var(--md-warning-bg-color);color:var(--md-warning-fg-color)}.md-banner__inner{font-size:.7rem;margin:.6rem auto;padding:0 .8rem}[dir=ltr] .md-banner__button{float:right}[dir=rtl] .md-banner__button{float:left}.md-banner__button{color:inherit;cursor:pointer;transition:opacity .25s}.no-js .md-banner__button{display:none}.md-banner__button:hover{opacity:.7}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.984375em){body[data-md-scrolllock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;position:absolute;right:.5em;top:.5em;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .md-code__content{display:grid}@keyframes consent{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes overlay{0%{opacity:0}to{opacity:1}}.md-consent__overlay{animation:overlay .25s both;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);background-color:#0000008a;height:100%;opacity:1;position:fixed;top:0;width:100%;z-index:5}.md-consent__inner{animation:consent .5s cubic-bezier(.1,.7,.1,1) both;background-color:var(--md-default-bg-color);border:0;border-radius:.1rem;bottom:0;box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;max-height:100%;overflow:auto;padding:0;position:fixed;width:100%;z-index:5}.md-consent__form{padding:.8rem}.md-consent__settings{display:none;margin:1em 0}input:checked+.md-consent__settings{display:block}.md-consent__controls{margin-bottom:.8rem}.md-typeset .md-consent__controls .md-button{display:inline}@media screen and (max-width:44.984375em){.md-typeset .md-consent__controls .md-button{display:block;margin-top:.4rem;text-align:center;width:100%}}.md-consent label{cursor:pointer}.md-content{flex-grow:1;min-width:0}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){[dir=ltr] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=ltr] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner,[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}[dir=ltr] .md-content__button{float:right}[dir=rtl] .md-content__button{float:left}[dir=ltr] .md-content__button{margin-left:.4rem}[dir=rtl] .md-content__button{margin-right:.4rem}.md-content__button{margin:.4rem 0;padding:0}@media print{.md-content__button{display:none}}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}[dir=ltr] .md-dialog{right:.8rem}[dir=rtl] .md-dialog{left:.8rem}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:var(--md-shadow-z3);min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:4}@media print{.md-dialog{display:none}}.md-dialog--active{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-feedback{margin:2em 0 1em;text-align:center}.md-feedback fieldset{border:none;margin:0;padding:0}.md-feedback__title{font-weight:700;margin:1em auto}.md-feedback__inner{position:relative}.md-feedback__list{display:flex;flex-wrap:wrap;place-content:baseline center;position:relative}.md-feedback__list:hover .md-icon:not(:disabled){color:var(--md-default-fg-color--lighter)}:disabled .md-feedback__list{min-height:1.8rem}.md-feedback__icon{color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;margin:0 .1rem;transition:color 125ms}.md-feedback__icon:not(:disabled).md-icon:hover{color:var(--md-accent-fg-color)}.md-feedback__icon:disabled{color:var(--md-default-fg-color--lightest);pointer-events:none}.md-feedback__note{opacity:0;position:relative;transform:translateY(.4rem);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-feedback__note>*{margin:0 auto;max-width:16rem}:disabled .md-feedback__note{opacity:1;transform:translateY(0)}@media print{.md-feedback{display:none}}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{justify-content:space-between;overflow:auto;padding:.2rem}.md-footer__inner:not([hidden]){display:flex}.md-footer__link{align-items:end;display:flex;flex-grow:0.01;margin-bottom:.4rem;margin-top:1rem;max-width:100%;outline-color:var(--md-accent-fg-color);overflow:hidden;transition:opacity .25s}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}[dir=rtl] .md-footer__link svg{transform:scaleX(-1)}@media screen and (max-width:44.984375em){.md-footer__link--prev{flex-shrink:0}.md-footer__link--prev .md-footer__title{display:none}}[dir=ltr] .md-footer__link--next{margin-left:auto}[dir=rtl] .md-footer__link--next{margin-right:auto}.md-footer__link--next{text-align:right}[dir=rtl] .md-footer__link--next{text-align:left}.md-footer__title{flex-grow:1;font-size:.9rem;margin-bottom:.7rem;max-width:calc(100% - 2.4rem);padding:0 1rem;white-space:nowrap}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-copyright{width:auto}}.md-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-social{display:inline-flex;gap:.2rem;margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-social{padding:.6rem 0}}.md-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-social__link:before{line-height:1.9}.md-social__link svg{fill:currentcolor;max-height:.8rem;vertical-align:-25%}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[dir=ltr] .md-typeset .md-input{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .md-input,[dir=rtl] .md-typeset .md-input{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .md-input{border-top-left-radius:.1rem}.md-typeset .md-input{border-bottom:.1rem solid var(--md-default-fg-color--lighter);box-shadow:var(--md-shadow-z1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:border .25s,box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{border-bottom-color:var(--md-accent-fg-color);box-shadow:var(--md-shadow-z2)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem #0000,0 .2rem .4rem #0000;color:var(--md-primary-bg-color);display:block;left:0;position:sticky;right:0;top:0;z-index:4}@media print{.md-header{display:none}}.md-header[hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header--shadow{box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentcolor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.234375em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentcolor;display:block;height:1.2rem;width:auto}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;white-space:nowrap}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__topic:first-child{font-weight:700}[dir=ltr] .md-header__title{margin-left:1rem;margin-right:.4rem}[dir=rtl] .md-header__title{margin-left:.4rem;margin-right:1rem}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem}.md-header__title--active .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title--active .md-header__topic{transform:translateX(1.25rem)}.md-header__title--active .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__option>input{bottom:0}.md-header__source{display:none}@media screen and (min-width:60em){[dir=ltr] .md-header__source{margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem}.md-header__source{display:block;max-width:11.7rem;width:11.7rem}}@media screen and (min-width:76.25em){[dir=ltr] .md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-meta{color:var(--md-default-fg-color--light);font-size:.7rem;line-height:1.3}.md-meta__list{display:inline-flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.md-meta__item:not(:last-child):after{content:"·";margin-left:.2rem;margin-right:.2rem}.md-meta__link{color:var(--md-typeset-a-color)}.md-meta__link:focus,.md-meta__link:hover{color:var(--md-accent-fg-color)}.md-draft{background-color:#ff1744;border-radius:.125em;color:#fff;display:inline-block;font-weight:700;padding-left:.5714285714em;padding-right:.5714285714em}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{color:var(--md-default-fg-color--light);display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentcolor;display:block;height:2.4rem;max-width:100%;object-fit:contain;width:auto}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__link{align-items:flex-start;display:flex;gap:.4rem;margin-top:.625em;scroll-snap-align:start;transition:color 125ms}.md-nav__link--passed{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active,.md-nav__item .md-nav__link--active code{color:var(--md-typeset-a-color)}.md-nav__link .md-ellipsis{position:relative}[dir=ltr] .md-nav__link .md-icon:last-child{margin-left:auto}[dir=rtl] .md-nav__link .md-icon:last-child{margin-right:auto}.md-nav__link svg{fill:currentcolor;flex-shrink:0;height:1.3em;position:relative}.md-nav__link[for]:focus,.md-nav__link[for]:hover,.md-nav__link[href]:focus,.md-nav__link[href]:hover{color:var(--md-accent-fg-color);cursor:pointer}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentcolor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__container>.md-nav__link{margin-top:0}.md-nav__container>.md-nav__link:first-child{flex-grow:1;min-width:0}.md-nav__icon{flex-shrink:0}.md-nav__source{display:none}@media screen and (max-width:76.234375em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}[dir=ltr] .md-nav--primary .md-nav__title .md-nav__icon{left:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);font-weight:700}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;right:.2rem;top:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link svg{margin-top:.1em}.md-nav--primary .md-nav__link>.md-nav__link{padding:0}[dir=ltr] .md-nav--primary .md-nav__link .md-nav__icon{margin-right:-.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{margin-left:-.2rem}.md-nav--primary .md-nav__link .md-nav__icon{font-size:1.2rem;height:1.2rem;width:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav{background-color:initial;position:static}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.984375em){.md-nav--primary .md-nav__link[for=__toc]{display:flex}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-nav--integrated .md-nav__link[for=__toc]{display:flex}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav{margin-bottom:-.4rem}.md-nav--secondary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--secondary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--secondary .md-nav__list{padding-right:.6rem}.md-nav--secondary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--secondary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--secondary .md-nav__item>.md-nav__link{margin-left:.4rem}}@media screen and (min-width:76.25em){.md-nav{margin-bottom:-.4rem;transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--primary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--primary .md-nav__list{padding-right:.6rem}.md-nav--primary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--primary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--primary .md-nav__item>.md-nav__link{margin-left:.4rem}.md-nav__toggle~.md-nav{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .25s,visibility 0ms .25s;visibility:collapse}.md-nav__toggle~.md-nav>.md-nav__list{overflow:hidden}.md-nav__toggle.md-toggle--indeterminate~.md-nav,.md-nav__toggle:checked~.md-nav{grid-template-rows:1fr;opacity:1;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .15s .1s,visibility 0ms;visibility:visible}.md-nav__toggle.md-toggle--indeterminate~.md-nav{transition:none}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{font-weight:700}.md-nav__item--section>.md-nav__link[for]{color:var(--md-default-fg-color--light)}.md-nav__item--section>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav__item--section>.md-nav__link .md-icon,.md-nav__item--section>.md-nav__link>[for]{display:none}[dir=ltr] .md-nav__item--section>.md-nav{margin-left:-.6rem}[dir=rtl] .md-nav__item--section>.md-nav{margin-right:-.6rem}.md-nav__item--section>.md-nav{display:block;opacity:1;visibility:visible}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{border-radius:100%;height:.9rem;transition:background-color .25s;width:.9rem}.md-nav__icon:hover{background-color:var(--md-accent-fg-color--transparent)}.md-nav__icon:after{background-color:currentcolor;border-radius:100%;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:transform .25s;vertical-align:-.1rem;width:100%}[dir=rtl] .md-nav__icon:after{transform:rotate(180deg)}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon:after,.md-nav__item--nested .md-toggle--indeterminate~.md-nav__link .md-nav__icon:after{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);margin-top:0;position:sticky;top:0;z-index:1}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active.md-nav__item--section{margin:0}[dir=ltr] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-left:-.6rem}[dir=rtl] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-right:-.6rem}.md-nav--lifted>.md-nav__list>.md-nav__item>[for]{color:var(--md-default-fg-color--light)}.md-nav--lifted .md-nav[data-md-level="1"]{grid-template-rows:1fr;opacity:1;visibility:visible}[dir=ltr] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-left:.05rem solid var(--md-primary-fg-color)}[dir=rtl] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-right:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{display:block;margin-bottom:1.25em;opacity:1;visibility:visible}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__list{overflow:visible;padding-bottom:0}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__title{display:none}}.md-pagination{font-size:.8rem;font-weight:700;gap:.4rem}.md-pagination,.md-pagination>*{align-items:center;display:flex;justify-content:center}.md-pagination>*{border-radius:.2rem;height:1.8rem;min-width:1.8rem;text-align:center}.md-pagination__current{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light)}.md-pagination__link{transition:color 125ms,background-color 125ms}.md-pagination__link:focus,.md-pagination__link:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-pagination__link:focus svg,.md-pagination__link:hover svg{color:var(--md-accent-fg-color)}.md-pagination__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-pagination__link svg{fill:currentcolor;color:var(--md-default-fg-color--lighter);display:block;max-height:100%;width:1.2rem}.md-post__back{border-bottom:.05rem solid var(--md-default-fg-color--lightest);margin-bottom:1.2rem;padding-bottom:1.2rem}@media screen and (max-width:76.234375em){.md-post__back{display:none}}[dir=rtl] .md-post__back svg{transform:scaleX(-1)}.md-post__authors{display:flex;flex-direction:column;gap:.6rem;margin:0 .6rem 1.2rem}.md-post .md-post__meta a{transition:color 125ms}.md-post .md-post__meta a:focus,.md-post .md-post__meta a:hover{color:var(--md-accent-fg-color)}.md-post__title{color:var(--md-default-fg-color--light);font-weight:700}.md-post--excerpt{margin-bottom:3.2rem}.md-post--excerpt .md-post__header{align-items:center;display:flex;gap:.6rem;min-height:1.6rem}.md-post--excerpt .md-post__authors{align-items:center;display:inline-flex;flex-direction:row;gap:.2rem;margin:0;min-height:2.4rem}[dir=ltr] .md-post--excerpt .md-post__meta .md-meta__list{margin-right:.4rem}[dir=rtl] .md-post--excerpt .md-post__meta .md-meta__list{margin-left:.4rem}.md-post--excerpt .md-post__content>:first-child{--md-scroll-margin:6rem;margin-top:0}.md-post>.md-nav--secondary{margin:1em 0}.md-profile{align-items:center;display:flex;font-size:.7rem;gap:.6rem;line-height:1.4;width:100%}.md-profile__description{flex-grow:1}.md-content--post{display:flex}@media screen and (max-width:76.234375em){.md-content--post{flex-flow:column-reverse}}.md-content--post>.md-content__inner{min-width:0}@media screen and (min-width:76.25em){[dir=ltr] .md-content--post>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-content--post>.md-content__inner{margin-right:1.2rem}}@media screen and (max-width:76.234375em){.md-sidebar.md-sidebar--post{padding:0;position:static;width:100%}.md-sidebar.md-sidebar--post .md-sidebar__scrollwrap{overflow:visible}.md-sidebar.md-sidebar--post .md-sidebar__inner{padding:0}.md-sidebar.md-sidebar--post .md-post__meta{margin-left:.6rem;margin-right:.6rem}.md-sidebar.md-sidebar--post .md-nav__item{border:none;display:inline}.md-sidebar.md-sidebar--post .md-nav__list{display:inline-flex;flex-wrap:wrap;gap:.6rem;padding-bottom:.6rem;padding-top:.6rem}.md-sidebar.md-sidebar--post .md-nav__link{padding:0}.md-sidebar.md-sidebar--post .md-nav{height:auto;margin-bottom:0;position:static}}:root{--md-progress-value:0;--md-progress-delay:400ms}.md-progress{background:var(--md-primary-bg-color);height:.075rem;opacity:min(clamp(0,var(--md-progress-value),1),clamp(0,100 - var(--md-progress-value),1));position:fixed;top:0;transform:scaleX(calc(var(--md-progress-value)*1%));transform-origin:left;transition:transform .5s cubic-bezier(.19,1,.22,1),opacity .25s var(--md-progress-delay);width:100%;z-index:4}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__overlay{left:-2.2rem}[dir=rtl] .md-search__overlay{right:-2.2rem}.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){[dir=ltr] .md-search__overlay{left:0}[dir=rtl] .md-search__overlay{right:0}.md-search__overlay{background-color:#0000008a;cursor:pointer;height:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__inner{left:0}[dir=rtl] .md-search__inner{right:0}.md-search__inner{height:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__inner{float:right}[dir=rtl] .md-search__inner{float:left}.md-search__inner{padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}}@media screen and (min-width:60em) and (max-width:76.234375em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem #0000;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:#00000042;border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:#ffffff1f}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem #00000012;color:var(--md-default-fg-color)}[dir=ltr] .md-search__input{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__input{padding-left:2.2rem;padding-right:3.6rem}.md-search__input{background:#0000;font-size:.9rem;height:100%;position:relative;text-overflow:ellipsis;width:100%;z-index:2}.md-search__input::placeholder{transition:color .25s}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.984375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__input{padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input{color:inherit;font-size:.8rem}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:#0000}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}[dir=ltr] .md-search__icon[for=__search]{left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem}.md-search__icon[for=__search]{position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__icon[for=__search]{left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem}.md-search__icon[for=__search]{top:.6rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}[dir=ltr] .md-search__options{right:.5rem}[dir=rtl] .md-search__options{left:.5rem}.md-search__options{pointer-events:none;position:absolute;top:.3rem;z-index:2}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__options{right:.8rem}[dir=rtl] .md-search__options{left:.8rem}.md-search__options{top:.6rem}}[dir=ltr] .md-search__options>.md-icon{margin-left:.2rem}[dir=rtl] .md-search__options>.md-icon{margin-right:.2rem}.md-search__options>.md-icon{color:var(--md-default-fg-color--light);opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>.md-icon:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon:hover{opacity:.7}[dir=ltr] .md-search__suggest{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__suggest{padding-left:2.2rem;padding-right:3.6rem}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}@media screen and (min-width:60em){[dir=ltr] .md-search__suggest{padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}.md-search__suggest{font-size:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}[dir=ltr] .md-search__output{border-bottom-left-radius:.1rem}[dir=ltr] .md-search__output,[dir=rtl] .md-search__output{border-bottom-right-radius:.1rem}[dir=rtl] .md-search__output{border-bottom-left-radius:.1rem}.md-search__output{overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.984375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:var(--md-shadow-z3);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){[dir=ltr] .md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0;-webkit-user-select:none;user-select:none}.md-search-result__item{box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more>summary{cursor:pointer;display:block;outline:none;position:sticky;scroll-snap-align:start;top:0;z-index:1}.md-search-result__more>summary::marker{display:none}.md-search-result__more>summary::-webkit-details-marker{display:none}.md-search-result__more>summary>div{color:var(--md-typeset-a-color);font-size:.64rem;padding:.75em .8rem;transition:color .25s,background-color .25s}@media screen and (min-width:60em){[dir=ltr] .md-search-result__more>summary>div{padding-left:2.2rem}[dir=rtl] .md-search-result__more>summary>div{padding-right:2.2rem}}.md-search-result__more>summary:focus>div,.md-search-result__more>summary:hover>div{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more[open]>summary{background-color:var(--md-default-bg-color)}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){[dir=ltr] .md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem}}[dir=ltr] .md-search-result__icon{left:0}[dir=rtl] .md-search-result__icon{right:0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.984375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentcolor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result .md-typeset{color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6}.md-search-result .md-typeset h1{color:var(--md-default-fg-color);font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result .md-typeset h1 mark{text-decoration:none}.md-search-result .md-typeset h2{color:var(--md-default-fg-color);font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result .md-typeset h2 mark{text-decoration:none}.md-search-result__terms{color:var(--md-default-fg-color);display:block;font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:initial;color:var(--md-accent-fg-color);text-decoration:underline}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid #0000;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid #0000;border-right:.2rem solid #0000;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}[dir=ltr] .md-select__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.234375em){[dir=ltr] .md-sidebar--primary{left:-12.1rem}[dir=rtl] .md-sidebar--primary{right:-12.1rem}.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:5}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:var(--md-shadow-z3);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}.md-header--lifted~.md-container .md-sidebar{top:4.8rem}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{scrollbar-gutter:stable;-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap:focus-within,.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb:hover,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@supports selector(::-webkit-scrollbar){.md-sidebar__scrollwrap{scrollbar-gutter:auto}[dir=ltr] .md-sidebar__inner{padding-right:calc(100% - 11.5rem)}[dir=rtl] .md-sidebar__inner{padding-left:calc(100% - 11.5rem)}}@media screen and (max-width:76.234375em){.md-overlay{background-color:#0000008a;height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:5}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@keyframes facts{0%{height:0}to{height:.65rem}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}[dir=ltr] .md-source__icon svg{margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem}.md-source__icon svg{margin-top:.6rem}[dir=ltr] .md-source__icon+.md-source__repository{padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{padding-right:2rem}[dir=ltr] .md-source__icon+.md-source__repository{margin-left:-2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem}[dir=ltr] .md-source__repository{margin-left:.6rem}[dir=rtl] .md-source__repository{margin-right:.6rem}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{display:flex;font-size:.55rem;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source__repository--active .md-source__facts{animation:facts .25s ease-in}.md-source__fact{overflow:hidden;text-overflow:ellipsis}.md-source__repository--active .md-source__fact{animation:fact .4s ease-out}[dir=ltr] .md-source__fact:before{margin-right:.1rem}[dir=rtl] .md-source__fact:before{margin-left:.1rem}.md-source__fact:before{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}.md-source__fact:nth-child(1n+2){flex-shrink:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-source-file{margin:1em 0}[dir=ltr] .md-source-file__fact{margin-right:.6rem}[dir=rtl] .md-source-file__fact{margin-left:.6rem}.md-source-file__fact{align-items:center;color:var(--md-default-fg-color--light);display:inline-flex;font-size:.68rem;gap:.3rem}.md-source-file__fact .md-icon{flex-shrink:0;margin-bottom:.05rem}[dir=ltr] .md-source-file__fact .md-author{float:left}[dir=rtl] .md-source-file__fact .md-author{float:right}.md-source-file__fact .md-author{margin-right:.2rem}.md-source-file__fact svg{width:.9rem}:root{--md-status:url('data:image/svg+xml;charset=utf-8,');--md-status--new:url('data:image/svg+xml;charset=utf-8,');--md-status--deprecated:url('data:image/svg+xml;charset=utf-8,');--md-status--encrypted:url('data:image/svg+xml;charset=utf-8,')}.md-status:after{background-color:var(--md-default-fg-color--light);content:"";display:inline-block;height:1.125em;-webkit-mask-image:var(--md-status);mask-image:var(--md-status);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-bottom;width:1.125em}.md-status:hover:after{background-color:currentcolor}.md-status--new:after{-webkit-mask-image:var(--md-status--new);mask-image:var(--md-status--new)}.md-status--deprecated:after{-webkit-mask-image:var(--md-status--deprecated);mask-image:var(--md-status--deprecated)}.md-status--encrypted:after{-webkit-mask-image:var(--md-status--encrypted);mask-image:var(--md-status--encrypted)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);display:block;line-height:1.3;overflow:auto;width:100%;z-index:3}@media print{.md-tabs{display:none}}@media screen and (max-width:76.234375em){.md-tabs{display:none}}.md-tabs[hidden]{pointer-events:none}[dir=ltr] .md-tabs__list{margin-left:.2rem}[dir=rtl] .md-tabs__list{margin-right:.2rem}.md-tabs__list{contain:content;display:flex;list-style:none;margin:0;overflow:auto;padding:0;scrollbar-width:none;white-space:nowrap}.md-tabs__list::-webkit-scrollbar{display:none}.md-tabs__item{height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__item--active .md-tabs__link{color:inherit;opacity:1}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:flex;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}[dir=ltr] .md-tabs__link svg{margin-right:.4rem}[dir=rtl] .md-tabs__link svg{margin-left:.4rem}.md-tabs__link svg{fill:currentcolor;height:1.3em}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}:root{--md-tag-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-tags:not([hidden]){display:inline-flex;flex-wrap:wrap;gap:.5em;margin-bottom:.75em;margin-top:-.125em}.md-typeset .md-tag{align-items:center;background:var(--md-default-fg-color--lightest);border-radius:2.4rem;display:inline-flex;font-size:.64rem;font-size:min(.8em,.64rem);font-weight:700;gap:.5em;letter-spacing:normal;line-height:1.6;padding:.3125em .78125em}.md-typeset .md-tag[href]{-webkit-tap-highlight-color:transparent;color:inherit;outline:none;transition:color 125ms,background-color 125ms}.md-typeset .md-tag[href]:focus,.md-typeset .md-tag[href]:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[id]>.md-typeset .md-tag{vertical-align:text-top}.md-typeset .md-tag-icon:before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-tag-icon);mask-image:var(--md-tag-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset .md-tag-icon[href]:focus:before,.md-typeset .md-tag-icon[href]:hover:before{background-color:var(--md-accent-bg-color)}@keyframes pulse{0%{transform:scale(.95)}75%{transform:scale(1)}to{transform:scale(.95)}}:root{--md-annotation-bg-icon:url('data:image/svg+xml;charset=utf-8,');--md-annotation-icon:url('data:image/svg+xml;charset=utf-8,')}.md-tooltip{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);font-family:var(--md-text-font-family);left:clamp(var(--md-tooltip-0,0rem) + .8rem,var(--md-tooltip-x),100vw + var(--md-tooltip-0,0rem) + .8rem - var(--md-tooltip-width) - 2 * .8rem);max-width:calc(100vw - 1.6rem);opacity:0;position:absolute;top:var(--md-tooltip-y);transform:translateY(-.4rem);transition:transform 0ms .25s,opacity .25s,z-index .25s;width:var(--md-tooltip-width);z-index:0}.md-tooltip--active{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip--inline{font-weight:700;-webkit-user-select:none;user-select:none;width:auto}.md-tooltip--inline:not(.md-tooltip--active){transform:translateY(.2rem) scale(.9)}.md-tooltip--inline .md-tooltip__inner{font-size:.5rem;padding:.2rem .4rem}[hidden]+.md-tooltip--inline{display:none}.focus-visible>.md-tooltip,.md-tooltip:target{outline:var(--md-accent-fg-color) auto}.md-tooltip__inner{font-size:.64rem;padding:.8rem}.md-tooltip__inner.md-typeset>:first-child{margin-top:0}.md-tooltip__inner.md-typeset>:last-child{margin-bottom:0}.md-annotation{font-style:normal;font-weight:400;outline:none;text-align:initial;vertical-align:text-bottom;white-space:normal}[dir=rtl] .md-annotation{direction:rtl}code .md-annotation{font-family:var(--md-code-font-family);font-size:inherit}.md-annotation:not([hidden]){display:inline-block;line-height:1.25}.md-annotation__index{border-radius:.01px;cursor:pointer;display:inline-block;margin-left:.4ch;margin-right:.4ch;outline:none;overflow:hidden;position:relative;-webkit-user-select:none;user-select:none;vertical-align:text-top;z-index:0}.md-annotation .md-annotation__index{transition:z-index .25s}@media screen{.md-annotation__index{width:2.2ch}[data-md-visible]>.md-annotation__index{animation:pulse 2s infinite}.md-annotation__index:before{background:var(--md-default-bg-color);-webkit-mask-image:var(--md-annotation-bg-icon);mask-image:var(--md-annotation-bg-icon)}.md-annotation__index:after,.md-annotation__index:before{content:"";height:2.2ch;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:-.1ch;width:2.2ch;z-index:-1}.md-annotation__index:after{background-color:var(--md-default-fg-color--lighter);-webkit-mask-image:var(--md-annotation-icon);mask-image:var(--md-annotation-icon);transform:scale(1.0001);transition:background-color .25s,transform .25s}.md-tooltip--active+.md-annotation__index:after{transform:rotate(45deg)}.md-tooltip--active+.md-annotation__index:after,:hover>.md-annotation__index:after{background-color:var(--md-accent-fg-color)}}.md-tooltip--active+.md-annotation__index{animation-play-state:paused;transition-duration:0ms;z-index:2}.md-annotation__index [data-md-annotation-id]{display:inline-block}@media print{.md-annotation__index [data-md-annotation-id]{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);font-weight:700;padding:0 .6ch;white-space:nowrap}.md-annotation__index [data-md-annotation-id]:after{content:attr(data-md-annotation-id)}}.md-typeset .md-annotation-list{counter-reset:xxx;list-style:none}.md-typeset .md-annotation-list li{position:relative}[dir=ltr] .md-typeset .md-annotation-list li:before{left:-2.125em}[dir=rtl] .md-typeset .md-annotation-list li:before{right:-2.125em}.md-typeset .md-annotation-list li:before{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);content:counter(xxx);counter-increment:xxx;font-size:.8875em;font-weight:700;height:2ch;line-height:1.25;min-width:2ch;padding:0 .6ch;position:absolute;text-align:center;top:.25em}:root{--md-tooltip-width:20rem;--md-tooltip-tail:0.3rem}.md-tooltip2{-webkit-backface-visibility:hidden;backface-visibility:hidden;color:var(--md-default-fg-color);font-family:var(--md-text-font-family);opacity:0;pointer-events:none;position:absolute;top:calc(var(--md-tooltip-host-y) + var(--md-tooltip-y));transform:translateY(-.4rem);transform-origin:calc(var(--md-tooltip-host-x) + var(--md-tooltip-x)) 0;transition:transform 0ms .25s,opacity .25s,z-index .25s;width:100%;z-index:0}.md-tooltip2:before{border-left:var(--md-tooltip-tail) solid #0000;border-right:var(--md-tooltip-tail) solid #0000;content:"";display:block;left:clamp(1.5 * .8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-tail),100vw - 2 * var(--md-tooltip-tail) - 1.5 * .8rem);position:absolute;z-index:1}.md-tooltip2--top:before{border-top:var(--md-tooltip-tail) solid var(--md-default-bg-color);bottom:calc(var(--md-tooltip-tail)*-1 + .025rem);filter:drop-shadow(0 1px 0 hsla(0,0%,0%,.05))}.md-tooltip2--bottom:before{border-bottom:var(--md-tooltip-tail) solid var(--md-default-bg-color);filter:drop-shadow(0 -1px 0 hsla(0,0%,0%,.05));top:calc(var(--md-tooltip-tail)*-1 + .025rem)}.md-tooltip2--active{opacity:1;transform:translateY(0);transition:transform .4s cubic-bezier(0,1,.5,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip2__inner{scrollbar-gutter:stable;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);left:clamp(.8rem,var(--md-tooltip-host-x) - .8rem,100vw - var(--md-tooltip-width) - .8rem);max-height:40vh;max-width:calc(100vw - 1.6rem);position:relative;scrollbar-width:thin}.md-tooltip2__inner::-webkit-scrollbar{height:.2rem;width:.2rem}.md-tooltip2__inner::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-tooltip2__inner::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}[role=tooltip]>.md-tooltip2__inner{font-size:.5rem;font-weight:700;left:clamp(.8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-width)/2,100vw - var(--md-tooltip-width) - .8rem);max-width:min(100vw - 2 * .8rem,400px);padding:.2rem .4rem;-webkit-user-select:none;user-select:none;width:-moz-fit-content;width:fit-content}.md-tooltip2__inner.md-typeset>:first-child{margin-top:0}.md-tooltip2__inner.md-typeset>:last-child{margin-bottom:0}[dir=ltr] .md-top{margin-left:50%}[dir=rtl] .md-top{margin-right:50%}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color--light);cursor:pointer;display:block;font-size:.7rem;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{transform:translate(50%)}.md-top[hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}[dir=rtl] .md-top[hidden]{transform:translate(50%,.2rem)}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}[dir=ltr] .md-version__current{margin-left:1.4rem;margin-right:.4rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current{color:inherit;cursor:pointer;outline:none;position:relative;top:.05rem}[dir=ltr] .md-version__current:after{margin-left:.4rem}[dir=rtl] .md-version__current:after{margin-right:.4rem}.md-version__current:after{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.4rem}.md-version__alias{margin-left:.3rem;opacity:.7}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:3}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (hover:none),(pointer:coarse){.md-version:hover .md-version__list{animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{animation:none}}.md-version__item{line-height:1.8rem}[dir=ltr] .md-version__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border:.075rem solid #448aff;border-radius:.2rem;box-shadow:var(--md-shadow-z1);color:var(--md-admonition-fg-color);display:flow-root;font-size:.64rem;margin:1.5625em 0;padding:0 .6rem;page-break-inside:avoid;transition:box-shadow 125ms}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}.md-typeset .admonition:focus-within,.md-typeset details:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .admonition>*,.md-typeset details>*{box-sizing:border-box}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{padding-left:2rem;padding-right:.6rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{padding-left:.6rem;padding-right:2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-left-width:.2rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-right-width:.2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset .admonition-title,.md-typeset summary{background-color:#448aff1a;border:none;font-weight:700;margin:0 -.6rem;padding-bottom:.4rem;padding-top:.4rem;position:relative}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}[dir=ltr] .md-typeset .admonition-title:before,[dir=ltr] .md-typeset summary:before{left:.6rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;width:1rem}.md-typeset .admonition-title code,.md-typeset summary code{box-shadow:0 0 0 .05rem var(--md-default-fg-color--lightest)}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .admonition.note:focus-within,.md-typeset details.note:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:#448aff1a}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note)}.md-typeset .note>.admonition-title:after,.md-typeset .note>summary:after{color:#448aff}.md-typeset .admonition.abstract,.md-typeset details.abstract{border-color:#00b0ff}.md-typeset .admonition.abstract:focus-within,.md-typeset details.abstract:focus-within{box-shadow:0 0 0 .2rem #00b0ff1a}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary{background-color:#00b0ff1a}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract)}.md-typeset .abstract>.admonition-title:after,.md-typeset .abstract>summary:after{color:#00b0ff}.md-typeset .admonition.info,.md-typeset details.info{border-color:#00b8d4}.md-typeset .admonition.info:focus-within,.md-typeset details.info:focus-within{box-shadow:0 0 0 .2rem #00b8d41a}.md-typeset .info>.admonition-title,.md-typeset .info>summary{background-color:#00b8d41a}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info)}.md-typeset .info>.admonition-title:after,.md-typeset .info>summary:after{color:#00b8d4}.md-typeset .admonition.tip,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .admonition.tip:focus-within,.md-typeset details.tip:focus-within{box-shadow:0 0 0 .2rem #00bfa51a}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:#00bfa51a}.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip)}.md-typeset .tip>.admonition-title:after,.md-typeset .tip>summary:after{color:#00bfa5}.md-typeset .admonition.success,.md-typeset details.success{border-color:#00c853}.md-typeset .admonition.success:focus-within,.md-typeset details.success:focus-within{box-shadow:0 0 0 .2rem #00c8531a}.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:#00c8531a}.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success)}.md-typeset .success>.admonition-title:after,.md-typeset .success>summary:after{color:#00c853}.md-typeset .admonition.question,.md-typeset details.question{border-color:#64dd17}.md-typeset .admonition.question:focus-within,.md-typeset details.question:focus-within{box-shadow:0 0 0 .2rem #64dd171a}.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:#64dd171a}.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question)}.md-typeset .question>.admonition-title:after,.md-typeset .question>summary:after{color:#64dd17}.md-typeset .admonition.warning,.md-typeset details.warning{border-color:#ff9100}.md-typeset .admonition.warning:focus-within,.md-typeset details.warning:focus-within{box-shadow:0 0 0 .2rem #ff91001a}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:#ff91001a}.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning)}.md-typeset .warning>.admonition-title:after,.md-typeset .warning>summary:after{color:#ff9100}.md-typeset .admonition.failure,.md-typeset details.failure{border-color:#ff5252}.md-typeset .admonition.failure:focus-within,.md-typeset details.failure:focus-within{box-shadow:0 0 0 .2rem #ff52521a}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary{background-color:#ff52521a}.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure)}.md-typeset .failure>.admonition-title:after,.md-typeset .failure>summary:after{color:#ff5252}.md-typeset .admonition.danger,.md-typeset details.danger{border-color:#ff1744}.md-typeset .admonition.danger:focus-within,.md-typeset details.danger:focus-within{box-shadow:0 0 0 .2rem #ff17441a}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary{background-color:#ff17441a}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger)}.md-typeset .danger>.admonition-title:after,.md-typeset .danger>summary:after{color:#ff1744}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .admonition.bug:focus-within,.md-typeset details.bug:focus-within{box-shadow:0 0 0 .2rem #f500571a}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:#f500571a}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug)}.md-typeset .bug>.admonition-title:after,.md-typeset .bug>summary:after{color:#f50057}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .admonition.example:focus-within,.md-typeset details.example:focus-within{box-shadow:0 0 0 .2rem #7c4dff1a}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:#7c4dff1a}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example)}.md-typeset .example>.admonition-title:after,.md-typeset .example>summary:after{color:#7c4dff}.md-typeset .admonition.quote,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .admonition.quote:focus-within,.md-typeset details.quote:focus-within{box-shadow:0 0 0 .2rem #9e9e9e1a}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:#9e9e9e1a}.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote)}.md-typeset .quote>.admonition-title:after,.md-typeset .quote>summary:after{color:#9e9e9e}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}[dir=ltr] .md-typeset .footnote>ol{margin-left:0}[dir=rtl] .md-typeset .footnote>ol{margin-right:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:focus-within .footnote-backref{opacity:1;transform:translateX(0);transition:none}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentcolor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}[dir=ltr] .md-typeset .headerlink{margin-left:.5rem}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{--md-scroll-margin:3.6rem;--md-scroll-offset:0rem;scroll-margin-top:calc(var(--md-scroll-margin) - var(--md-scroll-offset))}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{--md-scroll-margin:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{--md-scroll-offset:0.2rem}.md-typeset h4:target{--md-scroll-offset:0.15rem}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.984375em){.md-typeset div.arithmatex{margin:0 -.8rem}.md-typeset div.arithmatex>*{width:min-content}}.md-typeset div.arithmatex>*{margin-left:auto!important;margin-right:auto!important;padding:0 .8rem;touch-action:auto}.md-typeset div.arithmatex>* mjx-container{margin:0!important}.md-typeset div.arithmatex mjx-assistive-mml{height:0}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{-webkit-box-decoration-break:clone;box-decoration-break:clone;color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}[dir=ltr] .md-typeset summary{padding-right:1.8rem}[dir=rtl] .md-typeset summary{padding-left:1.8rem}[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset summary{cursor:pointer;display:block;min-height:1rem;overflow:hidden}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[dir=ltr] .md-typeset summary:after{right:.4rem}[dir=rtl] .md-typeset summary:after{left:.4rem}.md-typeset summary:after{background-color:currentcolor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{transform:rotate(180deg)}.md-typeset summary::marker{display:none}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{--md-icon-size:1.125em;display:inline-flex;height:var(--md-icon-size);vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentcolor;max-height:100%;width:var(--md-icon-size)}.md-typeset .lg,.md-typeset .xl,.md-typeset .xxl,.md-typeset .xxxl{vertical-align:text-bottom}.md-typeset .middle{vertical-align:middle}.md-typeset .lg{--md-icon-size:1.5em}.md-typeset .xl{--md-icon-size:2.25em}.md-typeset .xxl{--md-icon-size:3em}.md-typeset .xxxl{--md-icon-size:4em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color--light);box-shadow:2px 0 0 0 var(--md-code-hl-color) inset;display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight span.filename{background-color:var(--md-code-bg-color);border-bottom:.05rem solid var(--md-default-fg-color--lightest);border-top-left-radius:.1rem;border-top-right-radius:.1rem;display:flow-root;font-size:.85em;font-weight:700;margin-top:1em;padding:.6617647059em 1.1764705882em;position:relative}.highlight span.filename+pre{margin-top:0}.highlight span.filename+pre>code{border-top-left-radius:0;border-top-right-radius:0}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:sticky;-webkit-user-select:none;user-select:none;z-index:3}.highlight code a[id]{position:absolute;visibility:hidden}.highlight code[data-md-copying]{display:initial}.highlight code[data-md-copying] .hll{display:contents}.highlight code[data-md-copying] .md-annotation{display:none}.highlighttable{display:flow-root}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable th.filename{flex-grow:1;padding:0;text-align:left}.highlighttable th.filename span.filename{margin-top:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-top-left-radius:.1rem;font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;padding-right:.5882352941em}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;min-width:0}.linenodiv a{color:inherit}.md-typeset .highlighttable{direction:ltr;margin:1em 0}.md-typeset .highlighttable>tbody>tr>.code>div>pre>code{border-bottom-left-radius:0;border-top-left-radius:0}.md-typeset .highlight+.result{border:.05rem solid var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-bottom-right-radius:.1rem;border-top-width:.1rem;margin-top:-1.125em;overflow:visible;padding:0 1em}.md-typeset .highlight+.result:after{clear:both;content:"";display:block}@media screen and (max-width:44.984375em){.md-content__inner>.highlight{margin:1em -.8rem}.md-content__inner>.highlight>.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.code>div>pre>code,.md-content__inner>.highlight>.highlighttable>tbody>tr>.filename span.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.linenos,.md-content__inner>.highlight>pre>code{border-radius:0}.md-content__inner>.highlight+.result{border-left-width:0;border-radius:0;border-right-width:0;margin-left:-.8rem;margin-right:-.8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"↓";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"←";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"→";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"↑";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"⌫";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"⇤";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"⇪";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"⌧";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"☰";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"⌦";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"⏏";padding-right:.4em}.md-typeset .keys .key-end:before{content:"⤓";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"⎋";padding-right:.4em}.md-typeset .keys .key-home:before{content:"⤒";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"⎀";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"⇟";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"⇞";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"⎙";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"⇥";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"⌤";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"⏎";padding-left:.4em}:root{--md-tabbed-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-tabbed-icon--next:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-flow:column wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:target{--md-scroll-offset:0.625em}.md-typeset .tabbed-set>input.focus-visible~.tabbed-labels:before{background-color:var(--md-accent-fg-color)}.md-typeset .tabbed-labels{-ms-overflow-style:none;box-shadow:0 -.05rem var(--md-default-fg-color--lightest) inset;display:flex;max-width:100%;overflow:auto;scrollbar-width:none}@media print{.md-typeset .tabbed-labels{display:contents}}@media screen{.js .md-typeset .tabbed-labels{position:relative}.js .md-typeset .tabbed-labels:before{background:var(--md-default-fg-color);bottom:0;content:"";display:block;height:2px;left:0;position:absolute;transform:translateX(var(--md-indicator-x));transition:width 225ms,background-color .25s,transform .25s;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--md-indicator-width)}}.md-typeset .tabbed-labels::-webkit-scrollbar{display:none}.md-typeset .tabbed-labels>label{border-bottom:.1rem solid #0000;border-radius:.1rem .1rem 0 0;color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;font-size:.64rem;font-weight:700;padding:.78125em 1.25em .625em;scroll-margin-inline-start:1rem;transition:background-color .25s,color .25s;white-space:nowrap;width:auto}@media print{.md-typeset .tabbed-labels>label:first-child{order:1}.md-typeset .tabbed-labels>label:nth-child(2){order:2}.md-typeset .tabbed-labels>label:nth-child(3){order:3}.md-typeset .tabbed-labels>label:nth-child(4){order:4}.md-typeset .tabbed-labels>label:nth-child(5){order:5}.md-typeset .tabbed-labels>label:nth-child(6){order:6}.md-typeset .tabbed-labels>label:nth-child(7){order:7}.md-typeset .tabbed-labels>label:nth-child(8){order:8}.md-typeset .tabbed-labels>label:nth-child(9){order:9}.md-typeset .tabbed-labels>label:nth-child(10){order:10}.md-typeset .tabbed-labels>label:nth-child(11){order:11}.md-typeset .tabbed-labels>label:nth-child(12){order:12}.md-typeset .tabbed-labels>label:nth-child(13){order:13}.md-typeset .tabbed-labels>label:nth-child(14){order:14}.md-typeset .tabbed-labels>label:nth-child(15){order:15}.md-typeset .tabbed-labels>label:nth-child(16){order:16}.md-typeset .tabbed-labels>label:nth-child(17){order:17}.md-typeset .tabbed-labels>label:nth-child(18){order:18}.md-typeset .tabbed-labels>label:nth-child(19){order:19}.md-typeset .tabbed-labels>label:nth-child(20){order:20}}.md-typeset .tabbed-labels>label:hover{color:var(--md-default-fg-color)}.md-typeset .tabbed-labels>label>[href]:first-child{color:inherit}.md-typeset .tabbed-labels--linked>label{padding:0}.md-typeset .tabbed-labels--linked>label>a{display:block;padding:.78125em 1.25em .625em}.md-typeset .tabbed-content{width:100%}@media print{.md-typeset .tabbed-content{display:contents}}.md-typeset .tabbed-block{display:none}@media print{.md-typeset .tabbed-block{display:block}.md-typeset .tabbed-block:first-child{order:1}.md-typeset .tabbed-block:nth-child(2){order:2}.md-typeset .tabbed-block:nth-child(3){order:3}.md-typeset .tabbed-block:nth-child(4){order:4}.md-typeset .tabbed-block:nth-child(5){order:5}.md-typeset .tabbed-block:nth-child(6){order:6}.md-typeset .tabbed-block:nth-child(7){order:7}.md-typeset .tabbed-block:nth-child(8){order:8}.md-typeset .tabbed-block:nth-child(9){order:9}.md-typeset .tabbed-block:nth-child(10){order:10}.md-typeset .tabbed-block:nth-child(11){order:11}.md-typeset .tabbed-block:nth-child(12){order:12}.md-typeset .tabbed-block:nth-child(13){order:13}.md-typeset .tabbed-block:nth-child(14){order:14}.md-typeset .tabbed-block:nth-child(15){order:15}.md-typeset .tabbed-block:nth-child(16){order:16}.md-typeset .tabbed-block:nth-child(17){order:17}.md-typeset .tabbed-block:nth-child(18){order:18}.md-typeset .tabbed-block:nth-child(19){order:19}.md-typeset .tabbed-block:nth-child(20){order:20}}.md-typeset .tabbed-block>.highlight:first-child>pre,.md-typeset .tabbed-block>pre:first-child{margin:0}.md-typeset .tabbed-block>.highlight:first-child>pre>code,.md-typeset .tabbed-block>pre:first-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child>.filename{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable{margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.filename span.filename,.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.linenos{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.code>div>pre>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child+.result{margin-top:-.125em}.md-typeset .tabbed-block>.tabbed-set{margin:0}.md-typeset .tabbed-button{align-self:center;border-radius:100%;color:var(--md-default-fg-color--light);cursor:pointer;display:block;height:.9rem;margin-top:.1rem;pointer-events:auto;transition:background-color .25s;width:.9rem}.md-typeset .tabbed-button:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .tabbed-button:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-tabbed-icon--prev);mask-image:var(--md-tabbed-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color .25s,transform .25s;width:100%}.md-typeset .tabbed-control{background:linear-gradient(to right,var(--md-default-bg-color) 60%,#0000);display:flex;height:1.9rem;justify-content:start;pointer-events:none;position:absolute;transition:opacity 125ms;width:1.2rem}[dir=rtl] .md-typeset .tabbed-control{transform:rotate(180deg)}.md-typeset .tabbed-control[hidden]{opacity:0}.md-typeset .tabbed-control--next{background:linear-gradient(to left,var(--md-default-bg-color) 60%,#0000);justify-content:end;right:0}.md-typeset .tabbed-control--next .tabbed-button:after{-webkit-mask-image:var(--md-tabbed-icon--next);mask-image:var(--md-tabbed-icon--next)}@media screen and (max-width:44.984375em){[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels{padding-right:.8rem}.md-content__inner>.tabbed-set .tabbed-labels{margin:0 -.8rem;max-width:100vw;scroll-padding-inline-start:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-left:.8rem}.md-content__inner>.tabbed-set .tabbed-labels:after{content:""}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-right:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-left:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-right:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{width:2rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-left:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-right:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-left:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{width:2rem}}@media screen{.md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){color:var(--md-default-fg-color)}.md-typeset .no-js .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .no-js .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .no-js .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .no-js .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .no-js .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .no-js .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .no-js .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .no-js .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .no-js .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .no-js .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .no-js .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .no-js .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .no-js .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .no-js .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .no-js .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .no-js .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .no-js .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .no-js .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .no-js .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .no-js .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.no-js .md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.no-js .md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.no-js .md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.no-js .md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.no-js .md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.no-js .md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.no-js .md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.no-js .md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.no-js .md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.no-js .md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.no-js .md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.no-js .md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.no-js .md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.no-js .md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.no-js .md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.no-js .md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.no-js .md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.no-js .md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.no-js .md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.no-js .md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){border-color:var(--md-default-fg-color)}}.md-typeset .tabbed-set>input:first-child.focus-visible~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10).focus-visible~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11).focus-visible~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12).focus-visible~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13).focus-visible~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14).focus-visible~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15).focus-visible~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16).focus-visible~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17).focus-visible~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18).focus-visible~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19).focus-visible~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2).focus-visible~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20).focus-visible~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3).focus-visible~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4).focus-visible~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5).focus-visible~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6).focus-visible~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7).focus-visible~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8).focus-visible~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9).focus-visible~.tabbed-labels>:nth-child(9){color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:first-child:checked~.tabbed-content>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-content>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-content>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-content>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-content>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-content>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-content>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-content>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-content>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-content>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-content>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-content>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-content>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-content>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-content>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-content>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-content>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-content>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-content>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-content>:nth-child(9){display:block}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}[dir=ltr] .md-typeset .task-list-item [type=checkbox]{left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}[dir=ltr] .md-typeset .task-list-indicator:before{left:-1.5em}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}@media print{.giscus,[id=__comments]{display:none}}:root>*{--md-mermaid-font-family:var(--md-text-font-family),sans-serif;--md-mermaid-edge-color:var(--md-code-fg-color);--md-mermaid-node-bg-color:var(--md-accent-fg-color--transparent);--md-mermaid-node-fg-color:var(--md-accent-fg-color);--md-mermaid-label-bg-color:var(--md-default-bg-color);--md-mermaid-label-fg-color:var(--md-code-fg-color);--md-mermaid-sequence-actor-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actor-fg-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-actor-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-actor-line-color:var(--md-default-fg-color--lighter);--md-mermaid-sequence-actorman-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actorman-line-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-box-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-box-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-label-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-label-fg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-loop-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-loop-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-loop-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-message-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-message-line-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-note-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-border-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-number-bg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-number-fg-color:var(--md-accent-bg-color)}.mermaid{line-height:normal;margin:1em 0}.md-typeset .grid{grid-gap:.4rem;display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));margin:1em 0}.md-typeset .grid.cards>ol,.md-typeset .grid.cards>ul{display:contents}.md-typeset .grid.cards>ol>li,.md-typeset .grid.cards>ul>li,.md-typeset .grid>.card{border:.05rem solid var(--md-default-fg-color--lightest);border-radius:.1rem;display:block;margin:0;padding:.8rem;transition:border .25s,box-shadow .25s}.md-typeset .grid.cards>ol>li:focus-within,.md-typeset .grid.cards>ol>li:hover,.md-typeset .grid.cards>ul>li:focus-within,.md-typeset .grid.cards>ul>li:hover,.md-typeset .grid>.card:focus-within,.md-typeset .grid>.card:hover{border-color:#0000;box-shadow:var(--md-shadow-z2)}.md-typeset .grid.cards>ol>li>hr,.md-typeset .grid.cards>ul>li>hr,.md-typeset .grid>.card>hr{margin-bottom:1em;margin-top:1em}.md-typeset .grid.cards>ol>li>:first-child,.md-typeset .grid.cards>ul>li>:first-child,.md-typeset .grid>.card>:first-child{margin-top:0}.md-typeset .grid.cards>ol>li>:last-child,.md-typeset .grid.cards>ul>li>:last-child,.md-typeset .grid>.card>:last-child{margin-bottom:0}.md-typeset .grid>*,.md-typeset .grid>.admonition,.md-typeset .grid>.highlight>*,.md-typeset .grid>.highlighttable,.md-typeset .grid>.md-typeset details,.md-typeset .grid>details,.md-typeset .grid>pre{margin-bottom:0;margin-top:0}.md-typeset .grid>.highlight>pre:only-child,.md-typeset .grid>.highlight>pre>code,.md-typeset .grid>.highlighttable,.md-typeset .grid>.highlighttable>tbody,.md-typeset .grid>.highlighttable>tbody>tr,.md-typeset .grid>.highlighttable>tbody>tr>.code,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre>code{height:100%}.md-typeset .grid>.tabbed-set{margin-bottom:0;margin-top:0}@media screen and (min-width:45em){[dir=ltr] .md-typeset .inline{float:left}[dir=rtl] .md-typeset .inline{float:right}[dir=ltr] .md-typeset .inline{margin-right:.8rem}[dir=rtl] .md-typeset .inline{margin-left:.8rem}.md-typeset .inline{margin-bottom:.8rem;margin-top:0;width:11.7rem}[dir=ltr] .md-typeset .inline.end{float:right}[dir=rtl] .md-typeset .inline.end{float:left}[dir=ltr] .md-typeset .inline.end{margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{margin-left:0;margin-right:.8rem}} \ No newline at end of file diff --git a/HEAD/assets/stylesheets/main.6f8fc17f.min.css.map b/HEAD/assets/stylesheets/main.6f8fc17f.min.css.map new file mode 100644 index 000000000..8ba5ce39e --- /dev/null +++ b/HEAD/assets/stylesheets/main.6f8fc17f.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["src/templates/assets/stylesheets/main/components/_meta.scss","../../../../src/templates/assets/stylesheets/main.scss","src/templates/assets/stylesheets/main/_resets.scss","src/templates/assets/stylesheets/main/_colors.scss","src/templates/assets/stylesheets/main/_icons.scss","src/templates/assets/stylesheets/main/_typeset.scss","src/templates/assets/stylesheets/utilities/_break.scss","src/templates/assets/stylesheets/main/components/_author.scss","src/templates/assets/stylesheets/main/components/_banner.scss","src/templates/assets/stylesheets/main/components/_base.scss","src/templates/assets/stylesheets/main/components/_clipboard.scss","src/templates/assets/stylesheets/main/components/_code.scss","src/templates/assets/stylesheets/main/components/_consent.scss","src/templates/assets/stylesheets/main/components/_content.scss","src/templates/assets/stylesheets/main/components/_dialog.scss","src/templates/assets/stylesheets/main/components/_feedback.scss","src/templates/assets/stylesheets/main/components/_footer.scss","src/templates/assets/stylesheets/main/components/_form.scss","src/templates/assets/stylesheets/main/components/_header.scss","node_modules/material-design-color/material-color.scss","src/templates/assets/stylesheets/main/components/_nav.scss","src/templates/assets/stylesheets/main/components/_pagination.scss","src/templates/assets/stylesheets/main/components/_post.scss","src/templates/assets/stylesheets/main/components/_progress.scss","src/templates/assets/stylesheets/main/components/_search.scss","src/templates/assets/stylesheets/main/components/_select.scss","src/templates/assets/stylesheets/main/components/_sidebar.scss","src/templates/assets/stylesheets/main/components/_source.scss","src/templates/assets/stylesheets/main/components/_status.scss","src/templates/assets/stylesheets/main/components/_tabs.scss","src/templates/assets/stylesheets/main/components/_tag.scss","src/templates/assets/stylesheets/main/components/_tooltip.scss","src/templates/assets/stylesheets/main/components/_tooltip2.scss","src/templates/assets/stylesheets/main/components/_top.scss","src/templates/assets/stylesheets/main/components/_version.scss","src/templates/assets/stylesheets/main/extensions/markdown/_admonition.scss","src/templates/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/templates/assets/stylesheets/main/extensions/markdown/_toc.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/templates/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/templates/assets/stylesheets/main/integrations/_giscus.scss","src/templates/assets/stylesheets/main/integrations/_mermaid.scss","src/templates/assets/stylesheets/main/modifiers/_grid.scss","src/templates/assets/stylesheets/main/modifiers/_inline.scss"],"names":[],"mappings":"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FACE,alCq4GN,CkC15GE,4DACE,oBlC65GJ,CkC15GI,sFACE,gClC45GN,CkCv5GE,oEACE,0BlC05GJ,CkCv5GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCy5GN,CkCr5GI,gFACE,alCu5GN,CkC56GE,4DACE,oBlC+6GJ,CkC56GI,sFACE,gClC86GN,CkCz6GE,oEACE,0BlC46GJ,CkCz6GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClC26GN,CkCv6GI,gFACE,alCy6GN,CkC97GE,0DACE,oBlCi8GJ,CkC97GI,oFACE,gClCg8GN,CkC37GE,kEACE,0BlC87GJ,CkC37GI,gFACE,wBAlBG,CAmBH,oDAAA,CAAA,4ClC67GN,CkCz7GI,8EACE,alC27GN,CkCh9GE,oDACE,oBlCm9GJ,CkCh9GI,8EACE,gClCk9GN,CkC78GE,4DACE,0BlCg9GJ,CkC78GI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yClC+8GN,CkC38GI,wEACE,alC68GN,CkCl+GE,4DACE,oBlCq+GJ,CkCl+GI,sFACE,gClCo+GN,CkC/9GE,oEACE,0BlCk+GJ,CkC/9GI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6ClCi+GN,CkC79GI,gFACE,alC+9GN,CkCp/GE,wDACE,oBlCu/GJ,CkCp/GI,kFACE,gClCs/GN,CkCj/GE,gEACE,0BlCo/GJ,CkCj/GI,8EACE,wBAlBG,CAmBH,mDAAA,CAAA,2ClCm/GN,CkC/+GI,4EACE,alCi/GN,CmCrpHA,MACE,qMnCwpHF,CmC/oHE,sBAEE,uCAAA,CADA,gBnCmpHJ,CmC/oHI,mCACE,anCipHN,CmClpHI,mCACE,cnCipHN,CmC7oHM,4BACE,sBnC+oHR,CmC5oHQ,mCACE,gCnC8oHV,CmC1oHQ,2DACE,SAAA,CAEA,uBAAA,CADA,enC6oHV,CmCxoHQ,yGACE,SAAA,CACA,uBnC0oHV,CmCtoHQ,yCACE,YnCwoHV,CmCjoHE,0BACE,eAAA,CACA,enCmoHJ,CmChoHI,+BACE,oBnCkoHN,CmC7nHE,gDACE,YnC+nHJ,CmC3nHE,8BAIE,+BAAA,CAHA,oBAAA,CAEA,WA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","file":"main.css"} \ No newline at end of file diff --git a/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/index.html b/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/index.html index 41a972200..a3e9c9d3b 100644 --- a/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/index.html +++ b/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/index.html @@ -23,7 +23,7 @@ - + @@ -31,7 +31,7 @@ - + @@ -253,7 +253,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/index.html b/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/index.html index 29c4b6a7b..7307745f2 100644 --- a/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/index.html +++ b/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2023/12/09/mp-units-210-released/index.html b/HEAD/blog/2023/12/09/mp-units-210-released/index.html index 6f0773e9f..20f5442a8 100644 --- a/HEAD/blog/2023/12/09/mp-units-210-released/index.html +++ b/HEAD/blog/2023/12/09/mp-units-210-released/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/index.html b/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/index.html index 949d05a57..145444f9e 100644 --- a/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/index.html +++ b/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/06/14/mp-units-220-released/index.html b/HEAD/blog/2024/06/14/mp-units-220-released/index.html index 6fdea124d..a1eb86af1 100644 --- a/HEAD/blog/2024/06/14/mp-units-220-released/index.html +++ b/HEAD/blog/2024/06/14/mp-units-220-released/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/index.html b/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/index.html index d9b10fb70..85058e64a 100644 --- a/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/index.html +++ b/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/09/27/mp-units-230-released/index.html b/HEAD/blog/2024/09/27/mp-units-230-released/index.html index 6c809b5cd..936e29c53 100644 --- a/HEAD/blog/2024/09/27/mp-units-230-released/index.html +++ b/HEAD/blog/2024/09/27/mp-units-230-released/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/index.html b/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/index.html index 3f9d14e17..a31b7e464 100644 --- a/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/index.html +++ b/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/index.html b/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/index.html index eb872c64e..4be32efec 100644 --- a/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/index.html +++ b/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/index.html b/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/index.html index e6ee2ea75..844370507 100644 --- a/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/index.html +++ b/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/index.html b/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/index.html index 2dfa444c0..d8e9e5b04 100644 --- a/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/index.html +++ b/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/index.html b/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/index.html index 4f05ae74e..8c76745d5 100644 --- a/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/index.html +++ b/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/11/05/mp-units-240-released/index.html b/HEAD/blog/2024/11/05/mp-units-240-released/index.html index 85f2b56e2..dc03c73ed 100644 --- a/HEAD/blog/2024/11/05/mp-units-240-released/index.html +++ b/HEAD/blog/2024/11/05/mp-units-240-released/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -428,7 +428,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/index.html b/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/index.html index d3817d44e..c593efb97 100644 --- a/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/index.html +++ b/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/index.html @@ -23,7 +23,7 @@ - + @@ -31,7 +31,7 @@ - + @@ -253,7 +253,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/archive/2023/index.html b/HEAD/blog/archive/2023/index.html index 1cee158e0..8f2b1c447 100644 --- a/HEAD/blog/archive/2023/index.html +++ b/HEAD/blog/archive/2023/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/archive/2024/index.html b/HEAD/blog/archive/2024/index.html index 0ffaeb29b..16c9628e4 100644 --- a/HEAD/blog/archive/2024/index.html +++ b/HEAD/blog/archive/2024/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/archive/2024/page/2/index.html b/HEAD/blog/archive/2024/page/2/index.html index e012e9eed..61c534c91 100644 --- a/HEAD/blog/archive/2024/page/2/index.html +++ b/HEAD/blog/archive/2024/page/2/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/category/metrology/index.html b/HEAD/blog/category/metrology/index.html index fbe78e5ae..5cdc0cb56 100644 --- a/HEAD/blog/category/metrology/index.html +++ b/HEAD/blog/category/metrology/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/category/releases/index.html b/HEAD/blog/category/releases/index.html index 228e87c57..bbbf1c4c6 100644 --- a/HEAD/blog/category/releases/index.html +++ b/HEAD/blog/category/releases/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/category/wg21-updates/index.html b/HEAD/blog/category/wg21-updates/index.html index 336d9e892..d22e2b9a3 100644 --- a/HEAD/blog/category/wg21-updates/index.html +++ b/HEAD/blog/category/wg21-updates/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/index.html b/HEAD/blog/index.html index 89a5c28be..ca0eb3871 100644 --- a/HEAD/blog/index.html +++ b/HEAD/blog/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/blog/page/2/index.html b/HEAD/blog/page/2/index.html index 99f735215..7202cacf1 100644 --- a/HEAD/blog/page/2/index.html +++ b/HEAD/blog/page/2/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/feed_rss_created.xml b/HEAD/feed_rss_created.xml index 9a26aafa5..3891edb39 100644 --- a/HEAD/feed_rss_created.xml +++ b/HEAD/feed_rss_created.xml @@ -1 +1 @@ - mp-unitsThe quantities and units library for C++https://mpusz.github.io/mp-units/HEAD/mp-units Teamhttps://github.com/mpusz/mp-unitsen Sun, 24 Nov 2024 13:25:08 -0000 Sun, 24 Nov 2024 13:25:08 -0000 1440 MkDocs RSS plugin - v1.16.0 International System of Quantities (ISQ): Part 6 - Challenges mpusz Metrology <h1>International System of Quantities (ISQ): Part 6 - Challenges</h1><p>This article might be the last one from our series. This time, we will discuss the challenges andissues with modeling of the ISQ in software.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/ Mon, 11 Nov 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/ mp-units 2.4.0 released! mpusz Releases <h1>mp-units 2.4.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.4.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.4.0">Conan</a>.</strong></p><p>This release was unexpected. We planned a significant new feature to happen next, but whilepreparing for it, and also while writing API Reference documentation, we made so many vital fixesand improvements that we decided that they deserve a dedicated release first.</p><p>This post describes the most significant improvements while a much longer list of the changesintroduced by the new version can be found in our <a href="../../release_notes.md#2.4.0">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/05/mp-units-240-released/ Tue, 05 Nov 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/05/mp-units-240-released/ International System of Quantities (ISQ): Part 5 - Benefits mpusz Metrology <h1>International System of Quantities (ISQ): Part 5 - Benefits</h1><p>In the previous articles, we introduced the International System of Quantities, described how wecan model and implement it in a programming language, and presented the issues of software thatdoes not use such abstraction to implement a units library.</p><p>Some of the issues raised in <a href="isq-part-2-problems-when-isq-is-not-used.md">Part 2</a> of our serieswere addressed in <a href="isq-part-3-modeling-isq.md">Part 3</a> already. This article will presenthow our ISQ model elegantly addresses the remaining problems.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/ Mon, 04 Nov 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/ International System of Quantities (ISQ): Part 4 - Implementing ISQ mpusz Metrology <h1>International System of Quantities (ISQ): Part 4 - Implementing ISQ</h1><p>Up until now, we have introduced the International System of Quantities and described how we canmodel its main aspects. This article will present how to implement those models in a programminglanguage, and we will point out some of the first issues that stand in our way.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/ Mon, 28 Oct 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/ International System of Quantities (ISQ): Part 3 - Modeling ISQ mpusz Metrology <h1>International System of Quantities (ISQ): Part 3 - Modeling ISQ</h1><p>The physical units libraries on the market typically only focus on modeling one or more systemsof units. However, as we have learned, this is not the only system kind to model. Another,and maybe even more important, is a system of quantities. The most important example here isthe International System of Quantities (ISQ) defined by ISO/IEC 80000.</p><p>This article continues our series about the International System of Quantities. This time, we willlearn about the main ideas behind the ISQ and describe how it can be modelled in a programminglanguage.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/ Mon, 21 Oct 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/ International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used mpusz Metrology <h1>International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used</h1><p>This article is the next one in our series about the ISQ. After introducing the basic terms andsystems, this article will talk about the issues we face when we base the quantities and unitslibrary on just units or dimensions.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/ Mon, 14 Oct 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/ International System of Quantities (ISQ): Part 1 - Introduction mpusz Metrology <h1>International System of Quantities (ISQ): Part 1 - Introduction</h1><p>This post starts a series of articles about the International System of Quantities (ISQ).In this series, we will describe:</p><ul><li>What is ISQ?</li><li>Which engineering problems does ISQ help to solve and how?</li><li>How to model and implement it in the programming language?</li><li>What is missing in the ISQ, and why is that a problem?</li></ul>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/ Mon, 07 Oct 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/ mp-units 2.3.0 released! mpusz Releases <h1>mp-units 2.3.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.3.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.3.0">Conan</a>.</strong></p><p>This release fine-tunes many key features of the library. This post describes the most interestingimprovements, while a much longer list of the changes introduced by the new version can be found inour <a href="../../release_notes.md#2.3.0">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/09/27/mp-units-230-released/ Fri, 27 Sep 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/09/27/mp-units-230-released/ Report from the St. Louis 2024 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the St. Louis 2024 ISO C++ Committee meeting</h1><p>We made significant progress in the standardization of this library during the ISO C++ Committeemeeting in St. Louis.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/ Tue, 02 Jul 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/ mp-units 2.2.0 released! mpusz Releases <h1>mp-units 2.2.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.2.1">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.2.1">Conan</a>.</strong></p><p>Among other features, this release provides long-awaited support for C++20 modules, redesigns andenhances text output formatting, and greatly simplifies quantity point usage. This post describesthose and a few other smaller interesting improvements, while a much longer list of the mostsignificant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.2.1">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/06/14/mp-units-220-released/ Fri, 14 Jun 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/06/14/mp-units-220-released/ Report from the Tokyo 2024 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the Tokyo 2024 ISO C++ Committee meeting</h1><p>The Tokyo 2024 meeting was a very important step in the standardization of this library. SeveralWG21 groups reviewed proposals, and the feedback was really good.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/ Mon, 15 Apr 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/ mp-units 2.1.0 released! mpusz Releases <h1>mp-units 2.1.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.1.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.1.0">Conan</a>.</strong></p><p>The list of the most significant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.1.0">Release Notes</a>. We will also describe the most important of themin this post.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/12/09/mp-units-210-released/ Sat, 09 Dec 2023 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/12/09/mp-units-210-released/ Report from the Kona 2023 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the Kona 2023 ISO C++ Committee meeting</h1><p><strong>Several groups in the ISO C++ Committee reviewed the <a href="https://wg21.link/p1935">P1935: A C++ Approach to Physical Units</a>proposal in Belfast 2019 and Prague 2020. All those groups expressed interest in the potentialstandardization of such a library and encouraged further work. The authors also got valuableinitial feedback that highly influenced the design of the V2 version of the mp-units library.</strong></p><p>In the following years, we scoped on getting more feedback from the production and design. Thisresulted in version 2 of the <strong>mp-units</strong> library that resolved many issues the users and Committeemembers raised. The features and interfaces of this version are close to being the best we can getwith the current version of the C++ language standard.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/ Sun, 12 Nov 2023 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/ What's new in mp-units 2.0? mpusz Releases <h1>What's new in mp-units 2.0?</h1><p><strong>After a year of hard work, we've just released mp-units 2.0.0. It can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.0.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.0.0">Conan</a>.</strong></p><p>The list of the most significant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.0.0">Release Notes</a>. We will also describe some of them in this post.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/ Sun, 24 Sep 2023 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/ \ No newline at end of file + mp-unitsThe quantities and units library for C++https://mpusz.github.io/mp-units/HEAD/mp-units Teamhttps://github.com/mpusz/mp-unitsen Thu, 28 Nov 2024 22:10:42 -0000 Thu, 28 Nov 2024 22:10:42 -0000 1440 MkDocs RSS plugin - v1.16.0 International System of Quantities (ISQ): Part 6 - Challenges mpusz Metrology <h1>International System of Quantities (ISQ): Part 6 - Challenges</h1><p>This article might be the last one from our series. This time, we will discuss the challenges andissues with modeling of the ISQ in software.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/ Mon, 11 Nov 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/ mp-units 2.4.0 released! mpusz Releases <h1>mp-units 2.4.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.4.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.4.0">Conan</a>.</strong></p><p>This release was unexpected. We planned a significant new feature to happen next, but whilepreparing for it, and also while writing API Reference documentation, we made so many vital fixesand improvements that we decided that they deserve a dedicated release first.</p><p>This post describes the most significant improvements while a much longer list of the changesintroduced by the new version can be found in our <a href="../../release_notes.md#2.4.0">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/05/mp-units-240-released/ Tue, 05 Nov 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/05/mp-units-240-released/ International System of Quantities (ISQ): Part 5 - Benefits mpusz Metrology <h1>International System of Quantities (ISQ): Part 5 - Benefits</h1><p>In the previous articles, we introduced the International System of Quantities, described how wecan model and implement it in a programming language, and presented the issues of software thatdoes not use such abstraction to implement a units library.</p><p>Some of the issues raised in <a href="isq-part-2-problems-when-isq-is-not-used.md">Part 2</a> of our serieswere addressed in <a href="isq-part-3-modeling-isq.md">Part 3</a> already. This article will presenthow our ISQ model elegantly addresses the remaining problems.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/ Mon, 04 Nov 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/ International System of Quantities (ISQ): Part 4 - Implementing ISQ mpusz Metrology <h1>International System of Quantities (ISQ): Part 4 - Implementing ISQ</h1><p>Up until now, we have introduced the International System of Quantities and described how we canmodel its main aspects. This article will present how to implement those models in a programminglanguage, and we will point out some of the first issues that stand in our way.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/ Mon, 28 Oct 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/ International System of Quantities (ISQ): Part 3 - Modeling ISQ mpusz Metrology <h1>International System of Quantities (ISQ): Part 3 - Modeling ISQ</h1><p>The physical units libraries on the market typically only focus on modeling one or more systemsof units. However, as we have learned, this is not the only system kind to model. Another,and maybe even more important, is a system of quantities. The most important example here isthe International System of Quantities (ISQ) defined by ISO/IEC 80000.</p><p>This article continues our series about the International System of Quantities. This time, we willlearn about the main ideas behind the ISQ and describe how it can be modelled in a programminglanguage.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/ Mon, 21 Oct 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/ International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used mpusz Metrology <h1>International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used</h1><p>This article is the next one in our series about the ISQ. After introducing the basic terms andsystems, this article will talk about the issues we face when we base the quantities and unitslibrary on just units or dimensions.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/ Mon, 14 Oct 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/ International System of Quantities (ISQ): Part 1 - Introduction mpusz Metrology <h1>International System of Quantities (ISQ): Part 1 - Introduction</h1><p>This post starts a series of articles about the International System of Quantities (ISQ).In this series, we will describe:</p><ul><li>What is ISQ?</li><li>Which engineering problems does ISQ help to solve and how?</li><li>How to model and implement it in the programming language?</li><li>What is missing in the ISQ, and why is that a problem?</li></ul>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/ Mon, 07 Oct 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/ mp-units 2.3.0 released! mpusz Releases <h1>mp-units 2.3.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.3.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.3.0">Conan</a>.</strong></p><p>This release fine-tunes many key features of the library. This post describes the most interestingimprovements, while a much longer list of the changes introduced by the new version can be found inour <a href="../../release_notes.md#2.3.0">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/09/27/mp-units-230-released/ Fri, 27 Sep 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/09/27/mp-units-230-released/ Report from the St. Louis 2024 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the St. Louis 2024 ISO C++ Committee meeting</h1><p>We made significant progress in the standardization of this library during the ISO C++ Committeemeeting in St. Louis.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/ Tue, 02 Jul 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/ mp-units 2.2.0 released! mpusz Releases <h1>mp-units 2.2.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.2.1">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.2.1">Conan</a>.</strong></p><p>Among other features, this release provides long-awaited support for C++20 modules, redesigns andenhances text output formatting, and greatly simplifies quantity point usage. This post describesthose and a few other smaller interesting improvements, while a much longer list of the mostsignificant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.2.1">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/06/14/mp-units-220-released/ Fri, 14 Jun 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/06/14/mp-units-220-released/ Report from the Tokyo 2024 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the Tokyo 2024 ISO C++ Committee meeting</h1><p>The Tokyo 2024 meeting was a very important step in the standardization of this library. SeveralWG21 groups reviewed proposals, and the feedback was really good.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/ Mon, 15 Apr 2024 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/ mp-units 2.1.0 released! mpusz Releases <h1>mp-units 2.1.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.1.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.1.0">Conan</a>.</strong></p><p>The list of the most significant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.1.0">Release Notes</a>. We will also describe the most important of themin this post.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/12/09/mp-units-210-released/ Sat, 09 Dec 2023 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/12/09/mp-units-210-released/ Report from the Kona 2023 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the Kona 2023 ISO C++ Committee meeting</h1><p><strong>Several groups in the ISO C++ Committee reviewed the <a href="https://wg21.link/p1935">P1935: A C++ Approach to Physical Units</a>proposal in Belfast 2019 and Prague 2020. All those groups expressed interest in the potentialstandardization of such a library and encouraged further work. The authors also got valuableinitial feedback that highly influenced the design of the V2 version of the mp-units library.</strong></p><p>In the following years, we scoped on getting more feedback from the production and design. Thisresulted in version 2 of the <strong>mp-units</strong> library that resolved many issues the users and Committeemembers raised. The features and interfaces of this version are close to being the best we can getwith the current version of the C++ language standard.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/ Sun, 12 Nov 2023 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/ What's new in mp-units 2.0? mpusz Releases <h1>What's new in mp-units 2.0?</h1><p><strong>After a year of hard work, we've just released mp-units 2.0.0. It can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.0.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.0.0">Conan</a>.</strong></p><p>The list of the most significant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.0.0">Release Notes</a>. We will also describe some of them in this post.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/ Sun, 24 Sep 2023 00:00:00 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/ \ No newline at end of file diff --git a/HEAD/feed_rss_updated.xml b/HEAD/feed_rss_updated.xml index 2d86102cb..67fad4b34 100644 --- a/HEAD/feed_rss_updated.xml +++ b/HEAD/feed_rss_updated.xml @@ -1 +1 @@ - mp-unitsThe quantities and units library for C++https://mpusz.github.io/mp-units/HEAD/mp-units Teamhttps://github.com/mpusz/mp-unitsen Sun, 24 Nov 2024 13:25:08 -0000 Sun, 24 Nov 2024 13:25:08 -0000 1440 MkDocs RSS plugin - v1.16.0 International System of Quantities (ISQ): Part 6 - Challenges mpusz Metrology <h1>International System of Quantities (ISQ): Part 6 - Challenges</h1><p>This article might be the last one from our series. This time, we will discuss the challenges andissues with modeling of the ISQ in software.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/ Tue, 12 Nov 2024 08:53:31 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/ International System of Quantities (ISQ): Part 1 - Introduction mpusz Metrology <h1>International System of Quantities (ISQ): Part 1 - Introduction</h1><p>This post starts a series of articles about the International System of Quantities (ISQ).In this series, we will describe:</p><ul><li>What is ISQ?</li><li>Which engineering problems does ISQ help to solve and how?</li><li>How to model and implement it in the programming language?</li><li>What is missing in the ISQ, and why is that a problem?</li></ul>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/ International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used mpusz Metrology <h1>International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used</h1><p>This article is the next one in our series about the ISQ. After introducing the basic terms andsystems, this article will talk about the issues we face when we base the quantities and unitslibrary on just units or dimensions.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/ International System of Quantities (ISQ): Part 3 - Modeling ISQ mpusz Metrology <h1>International System of Quantities (ISQ): Part 3 - Modeling ISQ</h1><p>The physical units libraries on the market typically only focus on modeling one or more systemsof units. However, as we have learned, this is not the only system kind to model. Another,and maybe even more important, is a system of quantities. The most important example here isthe International System of Quantities (ISQ) defined by ISO/IEC 80000.</p><p>This article continues our series about the International System of Quantities. This time, we willlearn about the main ideas behind the ISQ and describe how it can be modelled in a programminglanguage.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/ International System of Quantities (ISQ): Part 4 - Implementing ISQ mpusz Metrology <h1>International System of Quantities (ISQ): Part 4 - Implementing ISQ</h1><p>Up until now, we have introduced the International System of Quantities and described how we canmodel its main aspects. This article will present how to implement those models in a programminglanguage, and we will point out some of the first issues that stand in our way.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/ International System of Quantities (ISQ): Part 5 - Benefits mpusz Metrology <h1>International System of Quantities (ISQ): Part 5 - Benefits</h1><p>In the previous articles, we introduced the International System of Quantities, described how wecan model and implement it in a programming language, and presented the issues of software thatdoes not use such abstraction to implement a units library.</p><p>Some of the issues raised in <a href="isq-part-2-problems-when-isq-is-not-used.md">Part 2</a> of our serieswere addressed in <a href="isq-part-3-modeling-isq.md">Part 3</a> already. This article will presenthow our ISQ model elegantly addresses the remaining problems.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/ mp-units 2.4.0 released! mpusz Releases <h1>mp-units 2.4.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.4.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.4.0">Conan</a>.</strong></p><p>This release was unexpected. We planned a significant new feature to happen next, but whilepreparing for it, and also while writing API Reference documentation, we made so many vital fixesand improvements that we decided that they deserve a dedicated release first.</p><p>This post describes the most significant improvements while a much longer list of the changesintroduced by the new version can be found in our <a href="../../release_notes.md#2.4.0">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/05/mp-units-240-released/ Tue, 05 Nov 2024 18:46:13 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/05/mp-units-240-released/ mp-units 2.3.0 released! mpusz Releases <h1>mp-units 2.3.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.3.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.3.0">Conan</a>.</strong></p><p>This release fine-tunes many key features of the library. This post describes the most interestingimprovements, while a much longer list of the changes introduced by the new version can be found inour <a href="../../release_notes.md#2.3.0">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/09/27/mp-units-230-released/ Mon, 30 Sep 2024 17:11:10 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/09/27/mp-units-230-released/ What's new in mp-units 2.0? mpusz Releases <h1>What's new in mp-units 2.0?</h1><p><strong>After a year of hard work, we've just released mp-units 2.0.0. It can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.0.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.0.0">Conan</a>.</strong></p><p>The list of the most significant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.0.0">Release Notes</a>. We will also describe some of them in this post.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/ mp-units 2.1.0 released! mpusz Releases <h1>mp-units 2.1.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.1.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.1.0">Conan</a>.</strong></p><p>The list of the most significant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.1.0">Release Notes</a>. We will also describe the most important of themin this post.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/12/09/mp-units-210-released/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/12/09/mp-units-210-released/ mp-units 2.2.0 released! mpusz Releases <h1>mp-units 2.2.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.2.1">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.2.1">Conan</a>.</strong></p><p>Among other features, this release provides long-awaited support for C++20 modules, redesigns andenhances text output formatting, and greatly simplifies quantity point usage. This post describesthose and a few other smaller interesting improvements, while a much longer list of the mostsignificant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.2.1">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/06/14/mp-units-220-released/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/06/14/mp-units-220-released/ Report from the Kona 2023 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the Kona 2023 ISO C++ Committee meeting</h1><p><strong>Several groups in the ISO C++ Committee reviewed the <a href="https://wg21.link/p1935">P1935: A C++ Approach to Physical Units</a>proposal in Belfast 2019 and Prague 2020. All those groups expressed interest in the potentialstandardization of such a library and encouraged further work. The authors also got valuableinitial feedback that highly influenced the design of the V2 version of the mp-units library.</strong></p><p>In the following years, we scoped on getting more feedback from the production and design. Thisresulted in version 2 of the <strong>mp-units</strong> library that resolved many issues the users and Committeemembers raised. The features and interfaces of this version are close to being the best we can getwith the current version of the C++ language standard.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/ Report from the St. Louis 2024 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the St. Louis 2024 ISO C++ Committee meeting</h1><p>We made significant progress in the standardization of this library during the ISO C++ Committeemeeting in St. Louis.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/ Report from the Tokyo 2024 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the Tokyo 2024 ISO C++ Committee meeting</h1><p>The Tokyo 2024 meeting was a very important step in the standardization of this library. SeveralWG21 groups reviewed proposals, and the feedback was really good.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/ \ No newline at end of file + mp-unitsThe quantities and units library for C++https://mpusz.github.io/mp-units/HEAD/mp-units Teamhttps://github.com/mpusz/mp-unitsen Thu, 28 Nov 2024 22:10:42 -0000 Thu, 28 Nov 2024 22:10:42 -0000 1440 MkDocs RSS plugin - v1.16.0 International System of Quantities (ISQ): Part 6 - Challenges mpusz Metrology <h1>International System of Quantities (ISQ): Part 6 - Challenges</h1><p>This article might be the last one from our series. This time, we will discuss the challenges andissues with modeling of the ISQ in software.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/ Tue, 12 Nov 2024 08:53:31 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/ International System of Quantities (ISQ): Part 1 - Introduction mpusz Metrology <h1>International System of Quantities (ISQ): Part 1 - Introduction</h1><p>This post starts a series of articles about the International System of Quantities (ISQ).In this series, we will describe:</p><ul><li>What is ISQ?</li><li>Which engineering problems does ISQ help to solve and how?</li><li>How to model and implement it in the programming language?</li><li>What is missing in the ISQ, and why is that a problem?</li></ul>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/ International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used mpusz Metrology <h1>International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used</h1><p>This article is the next one in our series about the ISQ. After introducing the basic terms andsystems, this article will talk about the issues we face when we base the quantities and unitslibrary on just units or dimensions.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/ International System of Quantities (ISQ): Part 3 - Modeling ISQ mpusz Metrology <h1>International System of Quantities (ISQ): Part 3 - Modeling ISQ</h1><p>The physical units libraries on the market typically only focus on modeling one or more systemsof units. However, as we have learned, this is not the only system kind to model. Another,and maybe even more important, is a system of quantities. The most important example here isthe International System of Quantities (ISQ) defined by ISO/IEC 80000.</p><p>This article continues our series about the International System of Quantities. This time, we willlearn about the main ideas behind the ISQ and describe how it can be modelled in a programminglanguage.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/ International System of Quantities (ISQ): Part 4 - Implementing ISQ mpusz Metrology <h1>International System of Quantities (ISQ): Part 4 - Implementing ISQ</h1><p>Up until now, we have introduced the International System of Quantities and described how we canmodel its main aspects. This article will present how to implement those models in a programminglanguage, and we will point out some of the first issues that stand in our way.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/ International System of Quantities (ISQ): Part 5 - Benefits mpusz Metrology <h1>International System of Quantities (ISQ): Part 5 - Benefits</h1><p>In the previous articles, we introduced the International System of Quantities, described how wecan model and implement it in a programming language, and presented the issues of software thatdoes not use such abstraction to implement a units library.</p><p>Some of the issues raised in <a href="isq-part-2-problems-when-isq-is-not-used.md">Part 2</a> of our serieswere addressed in <a href="isq-part-3-modeling-isq.md">Part 3</a> already. This article will presenthow our ISQ model elegantly addresses the remaining problems.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/ Mon, 11 Nov 2024 22:37:29 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/ mp-units 2.4.0 released! mpusz Releases <h1>mp-units 2.4.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.4.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.4.0">Conan</a>.</strong></p><p>This release was unexpected. We planned a significant new feature to happen next, but whilepreparing for it, and also while writing API Reference documentation, we made so many vital fixesand improvements that we decided that they deserve a dedicated release first.</p><p>This post describes the most significant improvements while a much longer list of the changesintroduced by the new version can be found in our <a href="../../release_notes.md#2.4.0">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/11/05/mp-units-240-released/ Tue, 05 Nov 2024 18:46:13 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/11/05/mp-units-240-released/ mp-units 2.3.0 released! mpusz Releases <h1>mp-units 2.3.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.3.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.3.0">Conan</a>.</strong></p><p>This release fine-tunes many key features of the library. This post describes the most interestingimprovements, while a much longer list of the changes introduced by the new version can be found inour <a href="../../release_notes.md#2.3.0">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/09/27/mp-units-230-released/ Mon, 30 Sep 2024 17:11:10 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/09/27/mp-units-230-released/ What's new in mp-units 2.0? mpusz Releases <h1>What's new in mp-units 2.0?</h1><p><strong>After a year of hard work, we've just released mp-units 2.0.0. It can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.0.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.0.0">Conan</a>.</strong></p><p>The list of the most significant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.0.0">Release Notes</a>. We will also describe some of them in this post.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/ mp-units 2.1.0 released! mpusz Releases <h1>mp-units 2.1.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.1.0">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.1.0">Conan</a>.</strong></p><p>The list of the most significant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.1.0">Release Notes</a>. We will also describe the most important of themin this post.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/12/09/mp-units-210-released/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/12/09/mp-units-210-released/ mp-units 2.2.0 released! mpusz Releases <h1>mp-units 2.2.0 released!</h1><p><strong>A new product version can be obtained from<a href="https://github.com/mpusz/mp-units/releases/tag/v2.2.1">GitHub</a> and<a href="https://conan.io/center/recipes/mp-units?version=2.2.1">Conan</a>.</strong></p><p>Among other features, this release provides long-awaited support for C++20 modules, redesigns andenhances text output formatting, and greatly simplifies quantity point usage. This post describesthose and a few other smaller interesting improvements, while a much longer list of the mostsignificant changes introduced by the new version can be found in our<a href="../../release_notes.md#2.2.1">Release Notes</a>.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/06/14/mp-units-220-released/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/06/14/mp-units-220-released/ Report from the Kona 2023 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the Kona 2023 ISO C++ Committee meeting</h1><p><strong>Several groups in the ISO C++ Committee reviewed the <a href="https://wg21.link/p1935">P1935: A C++ Approach to Physical Units</a>proposal in Belfast 2019 and Prague 2020. All those groups expressed interest in the potentialstandardization of such a library and encouraged further work. The authors also got valuableinitial feedback that highly influenced the design of the V2 version of the mp-units library.</strong></p><p>In the following years, we scoped on getting more feedback from the production and design. Thisresulted in version 2 of the <strong>mp-units</strong> library that resolved many issues the users and Committeemembers raised. The features and interfaces of this version are close to being the best we can getwith the current version of the C++ language standard.</p>https://mpusz.github.io/mp-units/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/ Report from the St. Louis 2024 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the St. Louis 2024 ISO C++ Committee meeting</h1><p>We made significant progress in the standardization of this library during the ISO C++ Committeemeeting in St. Louis.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/ Report from the Tokyo 2024 ISO C++ Committee meeting mpusz WG21 Updates <h1>Report from the Tokyo 2024 ISO C++ Committee meeting</h1><p>The Tokyo 2024 meeting was a very important step in the standardization of this library. SeveralWG21 groups reviewed proposals, and the feedback was really good.</p>https://mpusz.github.io/mp-units/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/ Mon, 30 Sep 2024 12:47:28 +0000mp-unitshttps://mpusz.github.io/mp-units/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/ \ No newline at end of file diff --git a/HEAD/getting_started/contributing/index.html b/HEAD/getting_started/contributing/index.html index 26ca0f265..50498274c 100644 --- a/HEAD/getting_started/contributing/index.html +++ b/HEAD/getting_started/contributing/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/getting_started/cpp_compiler_support/index.html b/HEAD/getting_started/cpp_compiler_support/index.html index 6d077e5af..2d5f5b3a6 100644 --- a/HEAD/getting_started/cpp_compiler_support/index.html +++ b/HEAD/getting_started/cpp_compiler_support/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/getting_started/faq/index.html b/HEAD/getting_started/faq/index.html index cec3d8117..170d560ca 100644 --- a/HEAD/getting_started/faq/index.html +++ b/HEAD/getting_started/faq/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units @@ -2420,7 +2420,7 @@

    Why de

    Some users could expect to see 42 kWh or 42 kW h in the output. It is not the case and for a very good reason. As stated in -Simplifying the resulting expression templates, +Simplifying the resulting symbolic expressions, to be able to reason about and simplify units, the library needs to order them in an appropriate order.

    Maybe this default order could be improved a bit, but according to international standards, diff --git a/HEAD/getting_started/installation_and_usage/index.html b/HEAD/getting_started/installation_and_usage/index.html index e82699b40..ab2beb793 100644 --- a/HEAD/getting_started/installation_and_usage/index.html +++ b/HEAD/getting_started/installation_and_usage/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@

    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/getting_started/introduction/index.html b/HEAD/getting_started/introduction/index.html index 0a33097fa..653d26907 100644 --- a/HEAD/getting_started/introduction/index.html +++ b/HEAD/getting_started/introduction/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units @@ -2274,7 +2274,7 @@

    With the User's Experience in MindSFINAE,
  • usage of strong types for framework entities (instead of type aliases),
  • -
  • usage of expression templates to improve the readability of generated types,
  • +
  • usage of symbolic expressions to improve the readability of generated types,
  • limiting the number of template arguments to the bare minimum.
  • diff --git a/HEAD/getting_started/look_and_feel/index.html b/HEAD/getting_started/look_and_feel/index.html index e0293ec9b..7efbc9ed2 100644 --- a/HEAD/getting_started/look_and_feel/index.html +++ b/HEAD/getting_started/look_and_feel/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/getting_started/project_structure/index.html b/HEAD/getting_started/project_structure/index.html index 9a8397ee3..4ccb5fa91 100644 --- a/HEAD/getting_started/project_structure/index.html +++ b/HEAD/getting_started/project_structure/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/getting_started/quick_start/index.html b/HEAD/getting_started/quick_start/index.html index 0ef86ab0a..dda4aac0a 100644 --- a/HEAD/getting_started/quick_start/index.html +++ b/HEAD/getting_started/quick_start/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/index.html b/HEAD/index.html index 641930774..ff083fa61 100644 --- a/HEAD/index.html +++ b/HEAD/index.html @@ -23,7 +23,7 @@ - + @@ -31,7 +31,7 @@ - + @@ -253,7 +253,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/release_notes/index.html b/HEAD/release_notes/index.html index a83e20d26..3c8137763 100644 --- a/HEAD/release_notes/index.html +++ b/HEAD/release_notes/index.html @@ -23,7 +23,7 @@ - + @@ -31,7 +31,7 @@ - + @@ -253,7 +253,7 @@
    - +
    mpusz/mp-units @@ -424,7 +424,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/search/search_index.json b/HEAD/search/search_index.json index c6426420f..8e035d7f8 100644 --- a/HEAD/search/search_index.json +++ b/HEAD/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to mp-units!","text":"

    mp-units is a compile-time enabled feature-rich Modern C++ modular/header-only library that provides compile-time dimensional analysis and unit/quantity manipulation. Its key strengths include safety, performance, and developer experience.

    The library source code is hosted on GitHub with a permissive MIT license.

    Supported compilers

    This library tries to provide the best user experience possible with the C++ language. To achieve that, it extensively uses the latest C++ language features.

    Even though the library benefits from the latest C++ versions (if available), C++20 is enough to compile and use all of the library's functionality.

    Please refer to C++ compiler support chapter for more details.

    C++ modulesHeader files
    #include <iostream>\n#include <print>\nimport mp_units;\n\nusing namespace mp_units;\n\ninline constexpr struct smoot final : named_unit<\"smoot\", mag<67> * usc::inch> {} smoot;\n\nint main()\n{\n  constexpr quantity dist = 364.4 * smoot;\n  std::println(\"Harvard Bridge length = {::N[.1f]} ({::N[.1f]}, {::N[.2f]}) \u00b1 1 \u03b5ar\",\n               dist, dist.in(usc::foot), dist.in(si::metre));\n}\n
    #include <mp-units/format.h>\n#include <mp-units/systems/si.h>\n#include <mp-units/systems/usc.h>\n#include <print>\n\nusing namespace mp_units;\n\ninline constexpr struct smoot final : named_unit<\"smoot\", mag<67> * usc::inch> {} smoot;\n\nint main()\n{\n  constexpr quantity dist = 364.4 * smoot;\n  std::println(\"Harvard Bridge length = {::N[.1f]} ({::N[.1f]}, {::N[.2f]}) \u00b1 1 \u03b5ar\",\n               dist, dist.in(usc::foot), dist.in(si::metre));\n}\n

    Output:

    Harvard Bridge length = 364.4 smoot (2034.6 ft, 620.14 m) \u00b1 1 \u03b5ar\n

    Try it on Compiler Explorer

    What is smoot?

    The smoot (/\u02c8smu\u02d0t/) is a nonstandard unit of length created as part of an MIT fraternity prank. It is named after Oliver R. Smoot, a fraternity pledge to Lambda Chi Alpha, who, in October 1958, lay on the Harvard Bridge (between Boston and Cambridge, Massachusetts) and was used by his fraternity brothers to measure the length of the bridge.

    One smoot equals Oliver Smoot's height at the time of the prank (five feet and seven inches). The bridge's length was measured to be 364.4 smoots plus or minus one ear, with the \"plus or minus\" intended to express the measurement uncertainty.

    Oliver Smoot graduated from MIT with the class of 1962, became a lawyer, and later became chairman of the American National Standards Institute (ANSI) and president of the International Organization for Standardization (ISO).

    More on the smoot unit of length can be found at https://en.wikipedia.org/wiki/Smoot.

    Important: Help needed!

    The mp-units library might be the subject of ISO standardization for C++29. More on this can be found in the following ISO C++ proposals:

    • P1935: A C++ Approach to Physical Units,
    • P2980: A motivation, scope, and plan for a physical quantities and units library,
    • P3045: Quantities and units library.

    We are actively looking for parties interested in field-trialing the library.

    "},{"location":"api_reference/","title":"API Reference - mp-units","text":""},{"location":"release_notes/","title":"Release Notes","text":""},{"location":"release_notes/#mp-units","title":"mp-units","text":""},{"location":"release_notes/#2.5.0","title":"2.5.0 WIP","text":""},{"location":"release_notes/#2.4.0","title":"2.4.0 November 5, 2024","text":"
    • (!) feat: phase_velocity and group_velocity aliases removed from ISQ by ISO
    • feat: iec::bit using-declared in iec::unit_symbols
    • feat: common unit symbols now use EQUIV{u1, u2, ...} syntax
    • feat: scaled_unit symbol printing improved ([] around the entire unit, small magnitude values do not use a power of 10 anymore)
    • feat: scaled_unit does not have a priority over derived_unit anymore
    • feat: fractional exponents support added to mag_power
    • feat: tag types are now required to be empty
    • feat: magnitude text now obeys formatting parameters and knows how to print constants
    • feat: added support for printing powers of magnitude constants
    • feat: SymbolicConstant concept added
    • feat: common_unit selection algorithm improved to make rev + rad return rad
    • feat: litre text symbol changed from l to L to avoid ambiguity with 1
    • feat: alternative litre unit symbol L added to prevent ambiguities with 1
    • feat: Unicode unit symbols
    • feat: \u03c0 added as an alias for pi
    • feat: expr_pow extended to remove redundancy in callers
    • feat: DerivedDimensionExpr, DerivedQuantitySpecExpr and DerivedUnitExpr removed
    • feat: MagnitudeSpecExpr and PowerVBase removed and some functions renamed to limit possible ambiguity in overload resolution
    • feat: std::is_object constraint applied to value_type_t
    • feat: quantity_values are now defined on top of std::chrono::duration_values
    • feat: from now on truncation is not allowed in compound assignment
    • feat(example): currency example now uses chrono::time_point and has better interfaces
    • feat(example): treat_as_floating_point specializations for examples' types removed
    • (!) refactor: all iec quantity specifications are now deprecated and moved to isq
    • (!) refactor: mag_constant now takes a symbol and a value and the class deriving from it must be final
    • (!) refactor: op==(U1, U2) now checks for the same type (old behavior available as equivalent(U1, U2)) + convertible now verifies associated quantity_spec as well
    • (!) refactor: ascii -> portable, unicode -> utf8, 'A' -> 'P'
    • (!) refactor: char_traits removed from fixed_string
    • (!) refactor: convertibility traits and concepts refactored to use bool flags instead of wrappers
    • (!) refactor: tag types should not expose their members
    • refactor: convertible(U1, U2) implementation simplified
    • refactor: abs moved to constexpr_math.h
    • refactor: unit_symbol_impl simplified
    • refactor: unit_symbol_formatting moved to a dedicated header file
    • refactor: shorten_T removed
    • refactor: magnitude interface cleanup
    • refactor: derived_from_the_same_base_dimension no longer needed
    • refactor: one_of usage removed from the fixed_string deduction guides
    • refactor: quantity.h is not needed in constants.h (unit.h is enough)
    • refactor: SameDimension concept is not needed and can be inlined in DimensionOf
    • refactor: framework Unicode symbols are now spelled using their codes
    • refactor: QuantitySpecWithNoSpecifiers removed and kind_of definition simplified
    • refactor: treat_as_floating_point simplified and extended to use std::chrono::treat_as_floating_point_v
    • refactor: wrapped_type_t reuses std::indirectly_readable_traits
    • refactor: expr_fractions takes direct OneType type now instead of a trait
    • refactor: Unicode symbols description reworked based on the latest SG16 recommendations
    • refactor: Mutable concept applied to quantity and quantity_point
    • refactor: explicit cleanup for deduction guides of quantity and quantity_point
    • refactor: point_origin_interface::op- cleanup
    • refactor: QuantityLikeImpl refactored to conform to API Reference by @JohelEGP
    • refactor: get_complexity refactored to be 0-based and not account for a number of arguments in a list
    • refactor: get_complexity refactored to returned maximum complexity of an element (instead of the sum of elements)
    • refactor(test): derived_quantity refactored to child_quantity
    • fix: missing are_ingredients_convertible overloads added
    • fix: constraints for magnitude added for scaled_unit and fixed common_unit instantiating it incorrectly
    • fix: according to ISO 80000-16 % should always be prefixed with space
    • fix: extraneous space in unit symbol having only denominators removed
    • fix: explicit cast added to less for magnitudes to fix clang-arm64 conversion error
    • fix: common_unit handling fixed for some corner cases
    • fix: math functions constraints fixed
    • fix: operator*(M, U u) fixed for U being scaled_unit
    • fix: subsumption of QuantityKindSpec fixed
    • fix: ValuePreservingTo fixed to apply std::remove_cvref_t on FromRep
    • fix: compound assignment operations on quantities now behave the same as on the underlying representation types
    • fix: QuantityConvertibleTo used in quantity_point compound assignment
    • fix: convertible_kinds implementation fixed
    • fix(tests): freestanding build fixed
    • test: unit tests for the inverse of mag_constant added
    • test: Unicode symbols used in unit tests
    • test: more std::chrono tests added
    • test: commutativity tests added to ISQ
    • test: one kind_of test added to reference tests
    • test: pow<0> and pow<1> tests added for dimensions
    • test: more get_common_quantity_spec tests added
    • build: setting of some test_package CMake options enabled only for a cxx_modules build
    • ci: sudo apt update added for documentation.yml in hope that it will resolve missing system packages issue
    • ci: MSVC added to the CI
    • ci: the latest not-released docs will use \"HEAD\" as a version from now on
    • ci: documentation action should run on a new tag as well
    • ci: documentation action limited only to commits that change documentation files
    • ci: non-documentation actions should not run on tags
    • docs: missing systems added to the \"Project Structure\" chapter
    • docs: graphs of ISQ kind hierarchies improved
    • docs: category of the ISO meeting reports changed to \"WG21 Updates\"
    • docs: blog comments support added
    • docs: Part 1-5 articles of the ISQ series added
    • docs: \"Symbols of scaled units\" chapter added + minor updates to scaled and common units chapters
    • docs: conan profile updated to present gcc-14 instead of gcc-12 which is no longer supported
    • docs: \"Why derived units order is not preserved from the multiplication?\" chapter added to FAQ
    • docs: \"Many shades of the same unit\" extended with a note about the derived units order
    • docs: API Reference now presents index.html instead of full.hml
    "},{"location":"release_notes/#2.3.0","title":"2.3.0 September 27, 2024","text":"
    • (!) feat: delta and absolute construction helpers
    • (!) feat: Conan and CMake options changed as requested at ConanCenter code review
    • (!) feat: unit_can_be_prefixed removed - from now on all named units can be prefixed
    • feat: formatting functions for units and dimensions marked as constexpr to enable compile-time text formatting
    • feat: qp1.quantity_from(qp2) added
    • feat: non-member swap added for fixed_string
    • feat: simplified inplace_vector added
    • feat: text output for angular units improved (space before symbol removed)
    • feat: representation type template parameter added to value conversion functions
    • feat: all tags in the expression templates are now ordered by the type names
    • feat: convertibility of a quantity with a unit one with the raw value added
    • feat: import std; support added
    • feat: compute values for rational magnitude powers (thanks @chiphogg)
    • feat: value_cast<Representation, Unit>() complementary conversion function added
    • feat: hw_voltage example added
    • feat: MP_UNITS_IMPORT_STD and MP_UNITS_MODULES handled properly in test_package.cpp
    • feat: complex quantity character added
    • feat: iec::var unit added
    • feat: type_list_unique added
    • feat: for_each on std::tuple added
    • feat: common_unit support added
    • (!) refactor: quantity_point_like_traits now use numerical value instead of the quantity
    • (!) refactor: iec80000 system renamed to iec
    • (!) refactor: mag_pi is now mag<pi>
    • (!) refactor: common_XXX() functions renamed to get_common_XXX()
    • refactor: error messages-related improvements
    • refactor: [[nodiscard]] and consteval set for some magnitude-related functions
    • refactor: degree Celsius and Fahrenheit symbols text now use Unicode codepoints
    • refactor: dimension_symbol and units_symbol refactored to use inplace_vector
    • refactor: unit_symbol and dimension_symbol refactored for readability and consteval
    • refactor: binary operators of quantity and quantity_point are now hidden friends
    • refactor: tag types-related operators are now hidden friends as well
    • refactor: ValuePreservingTo concept added
    • refactor: perfect forwarding interfaces improved
    • refactor: Representation concepts now requires WeaklyRegular instead of std::regular
    • refactor: quantity_point default-constructibility removed from the quantity_from constraints
    • refactor: has_common_type_v simplified
    • refactor: is_power_of_quantity_spec and is_power_of_dim variable templates converted to concepts
    • refactor: unnecessary custom versions of is_specialization_of removed
    • refactor: is_derived_from_specialization_of_v added and applied to remove custom traits
    • fix: signatures of capacity functions of fixed_string fixed
    • fix: MP_UNITS_API_NO_CRTP handling fixed
    • fix: MP_UNITS_HOSTED branch added to core.h
    • fix: mkdocs dependencies fixed in the gitpod dockerfile
    • fix: MP_UNITS_API_CONTRACTS should have a priority over headers availability
    • fix: si.h and angular.h now properly include hacks.h to define MP_UNITS_HOSTED before its usage
    • fix: quantity scaling between different prefixes improved
    • fix: conversion operator to std::chrono types fixed
    • fix: std::format does not always use Char* as iterators
    • fix: complex_power & co fixed
    • fix: __cpp_deleted_function workaround for clang-19 added
    • test: conversion to chrono unit tests added
    • test: unit tests for creating a quantity from a volatile variable
    • test: increase tolerance for certain math tests to two epsilon by @burnpanck
    • test: std::complex-based quantities tests added
    • build: conanfile bumped to use catch2/3.7.0
    • build: conanfile bumped to use fmt/11.0.1
    • build: minimum required CMake version bumped to 3.25
    • build: duplicated cmake_minimum_required commands removed
    • build: minimum conan version set to 2.0.15
    • build: MSVC compilation enabled for the library part by @czjhoppe
    • build: Dockerfile updated for gitpod
    • build: generate() in test_package now correctly propagates project's options
    • build: target_include_directories is not needed anymore
    • build: target_compile_features now uses CMAKE_CXX_STANDARD
    • build: package_type is dynamically set in conanfile.py depending if we build modules or not
    • build(conan): packaging improved
    • ci: added test for upstream clang on macos-14, as an example for an arm64 platform by @burnpanck
    • style: pre-commit updated to clang-format-18.1.8
    • docs: \"Strong Angular System\" chapter added
    • docs: docs updated to use new spelling for Conan options
    • docs: \"Interface Introduction\" chapter updated
    • docs: St. Louis 2024 report added
    • docs: \"Radians and degrees support\" added
    • docs: Rework of \"Getting Started\" section
    • docs: \"API Reference\" support added (thanks @JohelEGP)
    • docs: \"Generating API reference\" chapter added
    • docs: minor formatting and spelling improvements to the \"CONTRIBUTING.md\"
    • docs: \"Unit symbols\" chapter added
    • docs: examples line numbers fixed
    • docs: \"Text Formatting\" tag removed from avg_speed example
    • docs: line breaks in mermaid graphs fixed
    • docs: 2.3.0 release added
    "},{"location":"release_notes/#2.2.1","title":"2.2.1 July 3, 2024","text":"
    • (!) feat: Conan and CMake options refactored
    "},{"location":"release_notes/#2.2.0","title":"2.2.0 June 14, 2024","text":"
    • (!) feat: C++ modules support added by @JohelEGP
    • (!) feat: New formatting specification implemented
    • (!) feat: has_unit_symbol support removed
    • (!) feat: ABI concerns resolved with introduction of u8 strings for symbols
    • (!) feat: API-related Conan, CMake, and preprocessor options redesigned
    • (!) feat: core.h removed
    • (!) feat: from now on units, dimensions, quantity specifications, and point origins have to be marked as final
    • feat: implicit point origins support added
    • feat: unit default point origin support added
    • feat: fma, isfinite, isinf, and isnan math function added by @NAThompson
    • feat: fma for quantity points added
    • feat: quantity_point support added for quantity_cast and value_cast
    • feat: value_cast<Unit, Representation> added
    • feat: value_cast<Quantity>(q), value_cast<Quantity>(qp) and value_cast<QuantityPoint>(qp) added by @burnpanck
    • feat: interconvertible(QuantitySpec, QuantitySpec) added
    • feat: qp.quantity_from_zero() added
    • feat: value_type type trait added
    • feat: do not print space between a number and percent or per_mille
    • feat: ppm parts per million added by @nebkat
    • feat: atan2 2-argument arctangent added by @nebkat
    • feat: fmod floating-point division remainder added by @nebkat
    • feat: remainder IEEE division remainder added by @nebkat
    • feat: std::format support added
    • feat: unit text output support added
    • feat: formatting error messages improved
    • feat: improve types readability by eliminating extraneous () in references, prefixes, and kind_of
    • feat: dimension and unit text output added
    • feat: some light and radiation ISQ quantities added
    • feat: allow configuring GSL library use
    • feat: freestanding support added
    • (!) refactor: zero_Fahrenheit renamed to zeroth_degree_Fahrenheit
    • (!) refactor: SI-related trigonometric functions moved to the si subnamespace
    • (!) refactor: math.h header file broke up to smaller pieces
    • (!) refactor: fixed_string interface refactored
    • (!) refactor: ReferenceOf does not take a dimension anymore
    • (!) refactor: 'o' replaced with '1' as a modifier for unit_symbol_solidus::one_denominator
    • (!) refactor: get_kind() now returns kind_of
    • (!) refactor: FMT macros moved to compat_macros.h
    • (!) refactor: fixed_string refactored to reflect the latest changes to P3094R2
    • (!) refactor: basic_symbol_text renamed to symbol_text
    • (!) refactor: ratio hidden as an implementation detail behind mag_ratio
    • (!) refactor: framework.h introduced
    • (!) refactor: type list tools made an implementation detail of the library
    • (!) refactor: header files with the entire system definitions moved up in the directory tree
    • (!) refactor: absolute_point_origin does not use CRTP anymore
    • refactor: system's units do not inherit from one another anymore
    • refactor: all units made final
    • refactor: math functions constraints refactored
    • refactor: si_quantities.h added to improve compile-times
    • refactor: validate_ascii_string refactored to is_basic_literal_character_set
    • refactor: underlying_type split to wrapped_type and value_type and used in code
    • refactor: code refactored to comply with clang-tidy
    • refactor: remove dependency on <ranges> header and switch to use an iterator-based copy algorithm
    • refactor: terminate replaced with abort and a header file added
    • refactor: most std::remove_const_t removed and some replaced with the GCC-specific workaround
    • refactor: not needed remove_reference_t and remove_cvref_t removed
    • refactor: binary operators of quantity and quantity_point are now hidden friends
    • fix: QuantityLike conversions required Q::rep instead of using one provided by quantity_like_traits
    • fix: QuantitySpec[Unit] replaced with make_reference in value_cast
    • fix: ice_point is now defined with the integral offset from absolute_zero
    • fix: performance regression in sudo_cast fixed
    • fix: explicit object parameter support fixed
    • fix: missing version header file added to hacks.h
    • fix: quantity_cast to accept lvalue references (thanks @burnpanck)
    • fix: value_cast with lvalue references to quantity_point (thanks @burnpanck)
    • docs: project blog and first posts added
    • docs: project documentation layout refactored
    • docs: \"Interoperability with Other Libraries\" chapter added
    • docs: \"Framework Basics\" chapters updated and cleaned up
    • docs: smoot unit example added to the main page
    • docs: \"Code Example\" chapter renamed to \"Look and Feel\" and reordered in TOC to be after \"Quick Start\"
    • docs: \"Quick Start\" chapter reworked to be simpler and include quantity points
    • docs: \"Quantity points\" chapter extended
    • docs: \"The Affine Space\" chapter updated to reflect the recent design changes
    • docs: \"Working with Legacy interfaces\" chapter added
    • docs: \"Text Output\" chapter updated
    • docs: mkdocs social plugin enabled
    • docs: project logo and custom color scheme added
    • docs: minimum compiler requirements updated
    • docs: unit symbols admonition extended in the \"Quick Start\" chapter
    • docs: Cairo dependency described in the MkDocs section
    • docs: \"hello units\" example updated with dimensions output
    • docs: \"Text Output\" chapter updated with the recent formatting changes
    • docs: formatting grammar language changed to EBNF
    • docs: \"Project structure\" chapter expanded
    • docs: CITATION.cff updated
    • (!) build: Conan and CMake options refactored
    • (!) build: MP_UNITS_AS_SYSTEM_HEADERS renamed to MP_UNITS_BUILD_AS_SYSTEM_HEADERS
    • (!) build: MP_UNITS_BUILD_LA and MP_UNITS_IWYU CMake options now have _DEV_ in the name
    • build: gsl-lite updated to 0.41.0
    • build: catch2 updated to 3.5.1
    • build: fmt updated to 10.2.1
    • build: gitpod environment updated
    • build: check_cxx_feature_supported added
    • build: IWYU path handling fixed
    • build: IWYU enabled on GCC
    • build: CMAKE_EXPORT_COMPILE_COMMANDS flag enabled for the developer's build
    • build(conan): generate() now set cache_variables
    • build(conan): can_run check added before running tests
    • ci: Conan and CMake CI now use different cache names
    • ci: gcc-14 added
    • ci: clang-tidy CI added
    "},{"location":"release_notes/#2.1.1","title":"2.1.1 May 16, 2024","text":"
    • fix: unit tests compilation on gcc-14 fixed
    • fix: explicit this parameter support fixed
    "},{"location":"release_notes/#2.1.0","title":"2.1.0 December 9, 2023","text":"
    • (!) feat: inverse() support added for dimensions, quantity_spec, units, and references (1 / s will now create quantity and not a Unit)
    • (!) feat: quantity_point does not provide zero() anymore
    • (!) feat: quantity_spec and its kind should not compare equal
    • (!) feat: mutating interface removed from fixed_string
    • (!) feat: common_type with a raw value is not needed anymore as for a long time now raw values are not convertible to the dimensionless quantities
    • (!) feat: symbol_text definition simplified
    • (!) feat: users are now allowed to inherit their own types from absolute point origins
    • (!) feat: interoperability with other libraries redesigned
    • feat: basic_fixed_string(const CharT*, std::integral_constant<std::size_t, N>) constructor added
    • feat: isq::activity added and becquerel definition updated to benefit from it
    • feat: gray and sievert now have correct associated quantity kinds
    • feat: UnitCompatibleWith concept added and applied to in(U) and force_in(U) functions
    • feat: quantities can now be multiplied and divided by units (no parenthesis needed anymore)
    • feat: Magnitude / Unit operator added
    • feat: equality for dimensions now will allow derived classes as well (but not from derived_dimension)
    • feat: zero_Fahrenheit point origin added
    • feat: equivalent point origins handling improved
    • feat(example): unit symbols added to the currency example
    • (!) refactor: unit_symbol<fmt>(U) signature refactored and the resulting text can now also be used at runtime
    • (!) refactor: make_xxx factory functions replaced with two-parameter constructors
    • (!) refactor: unit_symbol changed to consteval
    • refactor: in(U) and force_in(U) now return auto to provide better diagnostics on clang
    • refactor: quantity operators constraints refactored
    • refactor: more type members added to fixed_string definition
    • refactor: unit_symbol_formatting enums now use std::int8_t as a representation type
    • fix: symbols of named dimensionless units with the ratio = 1 were not printed
    • fix: iterator is now properly updated for all cases in unit_symbol
    • fix: Fahrenheit conversion ratio was inverted
    • fix: CommonlyInvocableQuantities was overconstrained for the current library design
    • fix: are_ingredients_convertible now mandates explicit conversion for To dimensionless quantities
    • fix: quantity_point::point_for(PO) constraints fixed
    • fix(example): latitude and longitude fixed to include 0 for N and E respectively
    • ci: clang-17 enabled
    • ci: apple-clang-15 enabled
    • ci: Added C++23 builds to the CI matrix
    • docs: \"Getting Started\" chapters updated
    • docs: \"Basic Concepts\" and \"Interface Introduction\" chapters updated
    • docs: \"Design Overview\" chapter added and \"Concepts\" chapter reworked
    • docs: \"Output stream formatting\" chapter updated
    • docs: \"Default formatting\" chapter updated
    • docs: \"Derived unit symbols generation\" chapter added
    • docs: outdated affine space chapter updated
    • docs: CameCase concept identifiers FAQ added
    • docs: gravitational_potential_energy equation fixed on a graph
    • docs: YouTube video link updated to the C++ on Sea 2023
    • docs: ISO papers reference added to docs and README
    • docs: a representation type in a dimensionless quantity FAQ fixed
    • docs: titles added to some important admonitions
    • docs: \"Terms and Definitions\" slightly updated
    • docs: \"canonical unit\" added to glossary and its documentation in code was updated
    • docs: Design overview graph updated
    "},{"location":"release_notes/#2.0.0","title":"2.0.0 September 24, 2023","text":"
    • units namespace renamed to mp_units (#317)
    • header files in the <mp-units/...> rather then in <units/...> (#317)
    • the downcasting facility is removed (#383, #211, #32)
    • unified and simplified quantity creation (#274)
    • determining the best way to create a quantity (#413)
    • V2 quantity_point (#414)
    • introduction of quantity_spec to store not only dimension but also additional information about quantities (#405)
    • quantity now takes reference object, which aggregates quantity_spec and a unit and a representation type
    • units, prefixes, dimensions, quantity specifications, and references are passed as NTTPs to templates and provide arithmetic operations and comparison
    • expression templates consistently used in all derived types to increase the readability (#351, #166)
    • derived dimensions are now factors of only base dimensions (#281)
    • convertibility of derived quantities (#427)
    • dimensions, quantity specifications, units, and references are now composable, significantly reducing the number of definitions and resulting types
    • heavily simplified unit systems definitions (no need to define unnamed derived units, systems-specific dimensions, aliases for quantities, concepts, UDLs, ... anymore)
    • improved definition of all systems
    • support for ISO 80000 Part 3-6 quantities
    • faster than lightspeed constants (#169)
    • extensions to quantity formatting with fmt
    • quantity_kind removed
    • improved casting of unit with .in(Unit), .force_in(Unit) for quantity and quantity_spec
    • numerical value accessor safety improved with .numerical_value_in(Unit) and .force_numerical_value_in(Unit)
    • quantity can no longer be constructed with a raw value (#434)
    • Implicit construction of quantities from a value (#410)
    • quantity_point can no longer be constructed with just a quantity and an explicit PointOrigin is always needed
    • ceil and floor are dangerous (#432)
    • quecto, ronto, ronna, quetta new SI prefixes support
    • comparison against zero added (#487)
    • documentation rewritten from scratch
    • many smaller changes not possible to address with the previous design (#205, #210, #134)
    "},{"location":"release_notes/#0.8.0","title":"0.8.0 June 14, 2023","text":"
    • (!) refactor: common_quantity, common_quantity_for, common_quantity_point, common_quantity_kind, and common_quantity_point_kind removed
    • (!) refactor: named_derived_unit removed as it was not used
    • (!) refactor: derived_unit renamed to derived_scaled_unit
    • (!) refactor: unit renamed to derived_unit
    • (!) refactor: U::is_named removed from the unit types and replaced with NamedUnit concept
    • (!) refactor: PrefixFamily support removed
    • (!) refactor: mi(naut) renamed to nmi
    • (!) refactor: knot unit helper renamed to kn in FPS
    • (!) refactor: knot text symbol changed from \"knot\" to \"kn\"
    • refactor: quantity op+() and op-() reimplemented in terms of reference rather then quantity types
    • refactor(example): glide_computer now use dimensionless quantities with ranged_representation as rep
    • feat: HEP system support added (thanks @RalphSteinhagen)
    • feat: floor(), ceil(), and round() support added (thanks @hofbi)
    • feat: std::format support for compliant compilers added
    • feat: conversion helpers from mp-units to std::chrono types added
    • feat: math functions can now be safely used with user-defined types
    • feat: conversion from quantity_point to std::chrono::time_point added
    • feat: nautical_mile_per_hour and knot added to si::international system
    • (!) fix: add quantity_point::origin, like std::chrono::time_point::clock
    • fix: enable any prefixes for most of the named units (beside those that use prefixes already)
    • fix: hectare definition fixed to be a prefixed version of are + other units
    • fix: account for different dimensions in quantity_point_cast's constraint
    • fix: output stream operator now properly handles state
    • fix: fmt algorithms were overconstrained with forward_iterator
    • fix: CTAD for aliases fixed
    • fix: derived_ratio calculation
    • fix: fill_t assignment operator fixed
    • fix: improve downcast mode off
    • fix: radioactivity header compilation fixed
    • fix: si::hep::dim_momentum duplicated definition fixed
    • fix: fps can now coexist with international system
    • fix: public headers fixed to be standalone
    • test: standalone public headers tests added
    • (!) build: CMake generator in Conan is no longer obtained from an environment variable
    • (!) build: Required Conan version bumped to 1.48
    • (!) build: Conan 1.48 does not set CMAKE_BUILD_TYPE in the conan_toolchain.cmake anymore
    • build: AppleClang 13 support added (thanks @fdischner)
    • build: most of the conanfile.py refactored to be Conan 2.0 ready
    • build: validate() replaced with configure() to raise errors during conan install in Conan 1.X
    • build: minimum Conan version changed to 1.40
    • build: linear-algebra Conan repo is no needed anymore
    • build: Gitpod support added
    • build: clang-format-15 support added
    • build: export config to local build (#322)
    • build: fix export name of mp-units-system
    • build: fmt updated to 8.0.1
    • build: gsl-lite updated to 0.40.0
    • build: catch2 updated to 2.13.9
    • build: doxygen updated to 1.9.4
    • build: linear_algebra/0.7.0 switched to wg21-linear_algebra/0.7.2
    • ci: VS2022, gcc-11, clang-13, clang-14, and AppleClang 13 support added
    • ci: pre-commit support added (thanks @hofbi)
    • docs: Project documentation updated
    • docs: CITATION.cff file added
    • docs: CONTRIBUTING.md updated
    "},{"location":"release_notes/#0.7.0","title":"0.7.0 May 11, 2021","text":"
    • (!) refactor: ScalableNumber renamed to Representation
    • (!) refactor: output stream operators moved to the units/quantity_io.h header file
    • (!) refactor: Refactored the library file tree
    • (!) refactor: quantity::count() renamed to quantity::number()
    • (!) refactor: data system renamed to isq::iec80000 (quantity names renamed too)
    • (!) refactor: *deduced_unit renamed to *derived_unit
    • (!) refactor: got rid of a noble_derived_unit
    • refactor: quantity (kind) point updated to reflect latest changes to quantity
    • refactor: basic concepts, quantity and quantity_cast refactored
    • refactor: abs() definition refactored to be more explicit about the return type
    • feat: quantity (point) kind support added (thanks @johelegp)
    • feat: quantity references support added (thanks @johelegp)
    • feat: quantity aliases support addded
    • feat: interoperability with std::chrono::duration and other units libraries
    • feat: CTAD for dimensionless quantity added
    • feat: modulation_rate support added (thanks @go2sh)
    • feat: SI prefixes for isq::iec80000 support added (thanks @go2sh)
    • feat: a possibility to disable quantity UDLs support with UNITS_NO_LITERALS preprocessor define
    • feat: a support to define ISQ derived dimensions in terms of different number or order of components
    • perf: preconditions check do not influence the runtime performance of a Release build
    • perf: quantity_cast() generates less assembly instructions
    • perf: temporary string creation removed from quantity::op<<()
    • perf: value initialization for quantity value removed (left with a default initialization)
    • perf: limited the equivalent trait usage
    • perf: limited the C++ Standard Library headers usage
    • perf: rvalue references support added for constructors and getters
    • (!) fix: exp() has sense only for dimensionless quantities
    • (!) fix: dim_torque now properly divides by an angle (instead of multiply) + default unit name change
    • fix: quantity's operators fixed to behave like the underlying types do
    • fix: quantity_cast() fixed to work correctly with representation types not convertible from std::intmax_t
    • fix: ambiguous case for empty type list resolved
    • fix: downcasting facility for non-default-constructible types
    • fix: restore user-warnings within the library implementation
    • fix: the text symbol of foot_pound_force and foot_pound_force_per_second
    • fix: quantity modulo arithmetics fixed
    • (!) build: Conan testing version is now hosted on Artifactory
    • (!) build: Linear Algebra is now hosted on its Artifactory
    • (!) build: BUILD_DOCS CMake option renamed to UNITS_BUILD_DOCS
    • build: doxygen updated to 1.8.20
    • build: catch2 updated to 2.13.4
    • build: fmt updated to 7.1.3
    • build: ms-gsl replaced with gsl-lite/0.38.0
    • build: Conan generator switched to cmake_find_package_multi
    • build: Conan CMakeToolchain support added
    • build: CMake scripts cleanup
    • build: ccache support added
    • ci: CI switched from Travis CI to GitHub Actions
    "},{"location":"release_notes/#0.6.0","title":"0.6.0 September 13, 2020","text":"
    • feat: quantity_point support added (thanks @johelegp)
    • feat: Added angle as SI base dimension (thanks @kwikius)
    • feat: si::angular_velocity support added (thanks @mikeford3)
    • feat: FPS system added (thanks @mikeford3)
    • feat: Added support for mathematical function exp(quantity)
    • feat: Localization support for text output added (thanks @rbrugo)
    • feat: Added STL random number distribution wrappers (thanks @yasamoka)
    • (!) refactor: Refactored and cleaned up the library file tree
    • (!) refactor: q_* UDL renamed to _q_*
    • (!) refactor: UDLs with \"per\" in name renamed from *p* to *_per_*
    • (!) refactor: ratio changed to the NTTP kind
    • (!) refactor: exp and Exp renamed to exponent and Exponent
    • (!) refactor: Scalar concept renamed to ScalableNumber
    • (!) refactor: Dimensionless quantities redesigned to be of a quantity type
    • refactor: math.h function signatures refactored to use a Quantity concept (thanks @kwikius)
    • refactor: [[nodiscard]] added to many functions
    • fix: si::day unit symbol fixed to d (thanks @komputerwiz)
    • fix: si::mole unit symbol fixed to mol (thanks @mikeford3)
    • (!) build: gcc-9 is no longer supported (at least gcc-10 is required)
    • build: Visual Studio 16.7 support added
    • build: linear_algebra updated to 0.7.0/stable
    • build: fmt updated to 7.0.3
    • build: range-v3 updated to 0.11.0
    • build: catch2 updated to 2.13.0
    • build: doxygen updated to 1.8.18
    • build: ms-gsl 3.1.0 dependency added
    • build: Removed the dependency on a git submodule with common CMake scripts
    "},{"location":"release_notes/#0.5.0","title":"0.5.0 May 17, 2020","text":"
    • Major refactoring and rewrite of the library
    • Units are now independent from dimensions
    • Dimensions now depend on units (base or coherent units are provided in a class template)
    • Quantity gets a Dimension template parameter again (as unit does not provide information about its dimension anymore)
    • Spaceship operator support added
    • Added official CGS system support
    • Added official data information system support
    • Repository file tree cleanup
    • ratio refactored to contain Exp template parameter (thanks a lot @oschonrock!)
    • SI fundamental constants added
    • q_ prefix applied to all the UDLs (thanks @kwikius)
    • unknown_unit renamed to unknown_coherent_unit
    • Project documentation greatly extended and switched to Sphinx
    • A few more usage examples added
    • ASCII-only output support added (thanks @yasamoka)
    • Representation values formatting extended (thanks @rbrugo)
    • Output streams formatting support added
    • Linear algebra from std::experimental::math support added
    • Named SI units and their dimensions added (thanks @rbrugo
    • libfmt updated to 6.2.0
    • Added absolute functions and epsilon to math.h (thanks @mikeford3)
    • Added a lot of prefixes to named units and introduced alias_unit (thanks @yasamoka)
    • Linking with Conan targets only when they exists (#98)
    • All physical dimensions and units put into physical namespace
    • CMake improvements
    • Velocity renamed to speed

    Many thanks to GitHub users @oschonrock, @kwikius, and @i-ky for their support in drafting a new library design.

    "},{"location":"release_notes/#0.4.0","title":"0.4.0 Nov 17, 2019","text":"
    • Support for derived dimensions in exp added
    • Added pow() and sqrt() operations on quantities
    • units removed from a std::experimental namespace
    • Downcasting facility refactored so the user does not have to write the boilerplate code anymore
    • From now on base dimensions should inherit from base_dimension class template
    • Added unit symbols definitions to base_dimension and derived units
    • Added support for operator<< on quantity
    • fmt support added
    • Derived unit factory helpers refactored
    • Refactored the way prefixed units are defined
    "},{"location":"release_notes/#0.3.1","title":"0.3.1 Sep 18, 2019","text":"
    • cmcstl2 dependency changed to range-v3 0.9.1
    "},{"location":"release_notes/#0.3.0","title":"0.3.0 Sep 16, 2019","text":"
    • The design as described on CppCon 2019 talk (https://youtu.be/0YW6yxkdhlU)
    • Applied the feedback from the Cologne evening session
    • upcasting_traits renamed to downcasting_traits
    • Dimension template parameter removed from quantity
    • units moved to a std::experimental namespace
    • Leading underscore prefix removed from UDLs
    • Added a few more derived dimensions
    • meter renamed to metre
    • Missing operator* added
    • Predefined dimensions moved to a dedicated directory
    • dimension_ prefix removed from names of derived dimensions
    • cmcstl2 library updated to 2019.09.19
    • base_dimension is a value provided as const& to the exp type
    • integrated with Compiler Explorer
    • gsl-lite dependency removed
    • Fractional dimension exponents support added
    • QuantityOf concept introduced
    • quantity_cast<U, Rep>() support added
    "},{"location":"release_notes/#0.2.0","title":"0.2.0 July 18, 2019","text":"
    • The design as described on C++Now 2019 talk (https://youtu.be/wKchCktZPHU)
    • Added C++20 features supported by gcc-9.1 (std::remove_cvref_t, down with typename, std::type_identity)
    • Compile-time performance optimizations (type_list, common_ratio, ratio, conditional_t)
    "},{"location":"release_notes/#0.1.0","title":"0.1.0 May 18, 2019","text":"
    • Initial library release
    • Begin semantic versioning
    • The last version to work with gcc-8
    "},{"location":"appendix/glossary/","title":"Glossary","text":""},{"location":"appendix/glossary/#iso-definitions","title":"ISO definitions","text":"

    Note

    The ISO terms provided below are only a few of many defined in the ISO/IEC Guide 99.

    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 reference.
    • A reference can be a measurement unit, a measurement procedure, a reference material, or a combination of such.
    • A quantity as defined here is a scalar. However, a vector or a tensor, the components of which are quantities, is also considered to be a quantity.
    • The concept \u2019quantity\u2019 may be generically divided into, e.g. \u2018physical quantity\u2019, \u2018chemical quantity\u2019, and \u2018biological quantity\u2019, or \u2018base quantity\u2019 and \u2018derived quantity\u2019.
    • Examples of quantities are: length, radius, wavelength, energy, electric charge, etc.
    kind of quantity, kind
    • Aspect common to mutually comparable quantities.
    • The division of the concept \u2018quantity\u2019 into several kinds is to some extent arbitrary, for example:
      • the quantities diameter, circumference, and wavelength are generally considered to be quantities of the same kind, namely, of the kind of quantity called length,
      • the quantities heat, kinetic energy, and potential energy are generally considered to be quantities of the same kind, namely of the kind of quantity called energy.
    • Quantities of the same kind within a given system of quantities have the same quantity dimension. However, quantities of the same dimension are not necessarily of the same kind.
      • For example, the quantities moment of force and energy are, by convention, not regarded as being of the same kind, although they have the same dimension. Similarly for heat capacity and entropy, as well as for number of entities, relative permeability, and mass fraction.
    system of quantities
    • Set of quantities together with a set of non-contradictory equations relating those quantities.
    • Examples of systems of quantities are: the International System of Quantities, the Imperial System, etc.
    base quantity
    • 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 others.
    • Base quantities are referred to as being mutually independent since a base quantity cannot be expressed as a product of powers of the other base quantities.
    • \u2018Number of entities\u2019 can be regarded as a base quantity in any system of quantities.
    derived quantity
    • Quantity, in a system of quantities, defined in terms of the base quantities of that system.
    International System of Quantities, ISQ
    • System of quantities based on the seven base quantities: length, mass, time, electric current, thermodynamic temperature, amount of substance, and luminous intensity.
    • This system of quantities is published in the ISO 80000 and IEC 80000 series Quantities and units.
    • The International System of Units (SI) is based on the ISQ.
    quantity dimension, dimension of a quantity, dimension
    • Expression of the dependence of a quantity on the base quantities of a system of quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factor.
      • e.g. in the ISQ, the quantity dimension of force is denoted by \\(\\textsf{dim }F = \\mathsf{LMT}^{\u20132}\\).
    • A power of a factor is the factor raised to an exponent. Each factor is the dimension of a base quantity.
    • In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character.
    • In a given system of quantities:
      • quantities of the same kind have the same quantity dimension,
      • quantities of different quantity dimensions are always of different kinds,
      • quantities having the same quantity dimension are not necessarily of the same kind.
    • Symbols representing the dimensions of the base quantities in the ISQ are:

      Base quantity Symbol for dimension length \\(\\mathsf{L}\\) mass \\(\\mathsf{M}\\) time \\(\\mathsf{T}\\) electric current \\(\\mathsf{I}\\) thermodynamic temperature \\(\\mathsf{\u0398}\\) amount of substance \\(\\mathsf{N}\\) luminous intensity \\(\\mathsf{J}\\)

      Thus, the dimension of a quantity \\(Q\\) is denoted by \\(\\textsf{dim }Q = \\mathsf{L}^\u03b1\\mathsf{M}^\u03b2\\mathsf{T}^\u03b3\\mathsf{I}^\u03b4\\mathsf{\u0398}^\u03b5\\mathsf{N}^\u03b6\\mathsf{J}^\u03b7\\) where the exponents, named dimensional exponents, are positive, negative, or zero.

    quantity of dimension one, dimensionless quantity
    • quantity for which all the exponents of the factors corresponding to the base quantities in its quantity dimension are zero.
    • The term \u201cdimensionless quantity\u201d is commonly used and is kept here for historical reasons. It stems from the fact that all exponents are zero in the symbolic representation of the dimension for such quantities. The term \u201cquantity of dimension one\u201d reflects the convention in which the symbolic representation of the dimension for such quantities is the symbol \\(1\\).
    • The measurement units and values of quantities of dimension one are numbers, but such quantities convey more information than a number.
    • Some quantities of dimension one are defined as the ratios of two quantities of the same kind.
    • Numbers of entities are quantities of dimension one.
    measurement unit, unit of measurement, unit
    • Real scalar quantity, defined and adopted by convention, with which any other quantity of the same kind can be compared to express the ratio of the two quantities as a number.
    • Measurement units are designated by conventionally assigned names and symbols.
    • Measurement units of quantities of the same quantity dimension may be designated by the same name and symbol even when the quantities are not of the same kind.
      • For example, joule per kelvin and J/K are respectively the name and symbol of both a measurement unit of heat capacity and a measurement unit of entropy, which are generally not considered to be quantities of the same kind.
    • However, in some cases special measurement unit names are restricted to be used with quantities of specific kind only.
      • For example, the measurement unit \u2018second to the power minus one\u2019 (\\(\\mathsf{1/s}\\)) is called hertz (\\(\\mathsf{Hz}\\)) when used for frequencies and becquerel (\\(\\mathsf{Bq}\\)) when used for activities of radionuclides. As another example, the joule (\\(\\mathsf{J}\\)) is used as a unit of energy, but never as a unit of moment of force, e.g. the newton metre (\\(\\mathsf{N\u00b7m}\\)).
    • Measurement units of quantities of dimension one are numbers. In some cases, these measurement units are given special names, e.g. radian, steradian, and decibel, or are expressed by quotients such as millimole per mole equal to \\(10^{\u22123}\\) and microgram per kilogram equal to \\(10^{\u22129}\\).
    base unit
    • Measurement unit that is adopted by convention for a base quantity.
    • In each coherent system of units, there is only one base unit for each base quantity.
      • e.g. in the SI, the metre is the base unit of length. In the CGS systems, the centimetre is the base unit of length.
    • A base unit may also serve for a derived quantity of the same quantity dimension.
    • For number of entities, the number one, symbol \\(1\\), can be regarded as a base unit in any system of units.
    derived unit
    • Measurement unit for a derived quantity.
    • For example, the metre per second, symbol m/s, and the centimetre per second, symbol cm/s, are derived units of speed in the SI. The kilometre per hour, symbol km/h, is a measurement unit of speed outside the SI but accepted for use with the SI. The knot, equal to one nautical mile per hour, is a measurement unit of speed outside the SI.
    coherent derived unit
    • Derived 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.
    • A power of a base unit is the base unit raised to an exponent.
    • Coherence can be determined only with respect to a particular system of quantities and a given set of base units.
      • For example, if the metre, the second, and the mole are base units, the metre per second is the coherent derived unit of velocity when velocity is defined by the quantity equation \\(v = \\mathsf{d}r/\\mathsf{d}t\\), and the mole per cubic metre is the coherent derived unit of amount-of-substance concentration when amount-of-substance concentration is defined by the quantity equation \\(c = n/V\\). The kilometre per hour and the knot, given as examples of derived units, are not coherent derived units in such a system of quantities.
    • A derived unit can be coherent with respect to one system of quantities but not to another.
      • For example, the centimetre per second is the coherent derived unit of speed in a CGS system of units but is not a coherent derived unit in the SI.
    • The coherent derived unit for every derived quantity of dimension one in a given system of units is the number one, symbol \\(1\\). The name and symbol of the measurement unit one are generally not indicated.
    system of units
    • Set of base units and derived units, together with their multiples and submultiples, defined in accordance with given rules, for a given system of quantities.
    coherent system of units
    • System of units, based on a given system of quantities, in which the measurement unit for each derived quantity is a coherent derived unit.
    • A system of units can be coherent only with respect to a system of quantities and the adopted base units.
    • For a coherent system of units, numerical value equations have the same form, including numerical factors, as the corresponding quantity equations.
    off-system measurement unit, off-system unit
    • Measurement unit that does not belong to a given system of units.
    • For example, the electronvolt (about \\(1.602\\;18 \u00d7 10^{\u201319}\\;\\mathsf{J}\\)) is an off-system measurement unit of energy with respect to the SI. Day, hour, minute are off-system measurement units of time with respect to the SI.
    International System of Units, SI
    • System of units, based on the International System of Quantities, their names and symbols, including a series of prefixes and their names and symbols, together with rules for their use, adopted by the General Conference on Weights and Measures (CGPM).
    quantity value, value of a quantity, value
    • Number and reference together expressing magnitude of a quantity.
      • For example, length of a given rod: \\(5.34\\;\\mathsf{m}\\) or \\(534\\;\\mathsf{cm}\\).
    • The number can be complex.
    • A quantity value can be presented in more than one way.
    • In the case of vector or tensor quantities, each component has a quantity value.
      • For example, force acting on a given particle, e.g. in Cartesian components \\((F_x; F_y; F_z) = (\u221231.5; 43.2; 17.0)\\;\\mathsf{N}\\).
    numerical quantity value, numerical value of a quantity, numerical value
    • Number in the expression of a quantity value, other than any number serving as the reference
      • For example, in an amount-of-substance fraction equal to \\(3\\;\\mathsf{mmol/mol}\\), the numerical quantity value is \\(3\\) and the unit is \\(\\mathsf{mmol/mol}\\). The unit \\(\\mathsf{mmol/mol}\\) is numerically equal to \\(0.001\\), but this number \\(0.001\\) is not part of the numerical quantity value, which remains \\(3\\).
    quantity equation
    • Mathematical relation between quantities in a given system of quantities, independent of measure\u00adment units.
    • For example, \\(T = (1/2) mv^2\\) where \\(T\\) is the kinetic energy and \\(v\\) the speed of a specified particle of mass \\(m\\).
    unit equation
    • Mathematical relation between base units, coher\u00adent derived units or other measurement units.
    • For example, \\(\\mathsf{J} := \\mathsf{kg}\\:\\mathsf{m}^2/\\mathsf{s}^2\\), where, \\(\\mathsf{J}\\), \\(\\mathsf{kg}\\), \\(\\mathsf{m}\\), and \\(\\mathsf{s}\\) are the symbols for the joule, kilogram, metre, and second, respectively. (The symbol \\(:=\\) denotes \u201cis by definition equal to\u201d as given in the ISO 80000 and IEC 80000 series.). \\(1\\;\\mathsf{km/h} = (1/3.6)\\;\\mathsf{m/s}\\).
    numerical value equation, numerical quantity value equation
    • Mathematical relation between numerical quantity values, based on a given quantity equation and specified measurement units.
    • For example, in the quantity equation for kinetic energy of a particle, \\(T = (1/2) mv^2\\), if \\(m = 2\\;\\mathsf{kg}\\) and \\(v = 3\\;\\mathsf{m/s}\\), then \\({T} = (1/2)\\:\u00d7\\:2\\:\u00d7\\:3^2\\) is a numerical value equation giving the numerical value \\(9\\) of \\(T\\) in joules.
    "},{"location":"appendix/glossary/#other-definitions","title":"Other definitions","text":"

    Info

    The below terms extend the official ISO glossary and are commonly referred to by the mp-units library.

    base dimension
    • A dimension of a base quantity.
    derived dimension
    • A dimension of a derived quantity.
    • Implemented as an expression template being the result of the dimension equation on base dimensions.
    dimension equation
    • Mathematical relation between dimensions in a given system of quantities, independent of measure\u00adment units.
    quantity kind hierarchy, quantity hierarchy
    • Quantities of the same kind form a hierarchy that determines their:
      • convertibility (e.g. every width is a length, but width should not be convertible to height)
      • common quantity type (e.g. width + height -> length)
    quantity character, character of a quantity, character
    • Scalars, vectors and tensors are mathematical objects that can be used to denote certain physical quantities and their values. They are as such independent of the particular choice of a coordinate system, whereas each scalar component of a vector or a tensor and each component vector and component tensor depend on that choice.
    • A vector is a tensor of the first order and a scalar is a tensor of order zero.
    • For vectors and tensors, the components are quantities that can be expressed as a product of a number and a unit.
    • Vectors and tensors can also be expressed as a numerical value vector or tensor, respectively, multiplied by a unit.
    • Quantities of different characters support different set of operations.
      • For example, a quantity can be multiplied by another one only if any of them has scalar character. Vectors and tensors can't be multiplied or divided, but they support additional operations like dot and cross products, which are not available for scalars.
    • The term \u2019character\u2019 was borrowed from the below quote:

    ISO 80000-1_2009

    In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character.

    quantity specification, quantity_spec
    • An entity storing all the information about a specific quantity:
      • location in a quantity hierarchy
      • quantity equation
      • dimension of a quantity
      • quantity kind
      • quantity character
      • additional constraints (e.g. non-negative)
    • Dimension of a quantity is not enough to specify all the properties of a quantity.
    unit with an associated quantity, associated unit
    • Unit that is used to measure quantities of a specific kind in a given system of units.
    quantity reference, reference
    • According to its definition, quantity can be expressed by means of a number and a reference
    • In the mp-units library, a reference describes all the required meta-information associated with a specific quantity (quantity specification and unit).
    canonical representation of a unit, canonical unit
    • A canonical representation of a unit consists of:
      • a reference unit being the result of extraction of all the intermediate derived units,
      • a magnitude being a product of all the prefixes and magnitudes of extracted scaled units.
    • All units having the same canonical unit are deemed equal.
    • All units having the same reference unit are convertible (their magnitude may differ and is used during conversion).
    reference unit

    See canonical representation of a unit

    absolute quantity point origin, absolute point origin
    • An explicit point on an axis of values of a specific quantity type that serves as an absolute reference point for all quantity points which definitions are (explicitly or implicitly) based on it.
    • For example, mean sea level is commonly used as an absolute reference point to measure altitudes.
    relative quantity point origin, relative point origin
    • An explicit, known at compile-time, point on an axis of values of a specific quantity type serving as a reference for other quantities.
    • For example, an ice point is a quantity point with a value of \\(273.15\\;\\mathsf{K}\\) that is used as the zero point of a degree Celsius scale.
    quantity point origin, point origin
    • Either an absolute point origin or a relative point origin.
    quantity point, absolute quantity
    • An absolute quantity with respect to an origin.
    • For example, timestamp (as opposed to duration), altitude (as opposed to height), absolute temperature (as opposed to temperature difference).
    "},{"location":"appendix/references/","title":"References","text":"ISO80000

    ISO 80000-1:2009(E) \"Quantities and units \u2014 Part 1: General\", International Organization for Standardization.

    Quincey \"Angles in the SI: a detailed proposal for solving the problem, Quincey, Paul (1 October 2021). SIBrochure

    The International System of Units (SI), International Bureau of Weights and Measures (20 May 2019), ISBN 978-92-822-2272-0.

    "},{"location":"blog/","title":"Blog","text":""},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/","title":"What's new in mp-units 2.0?","text":"

    After a year of hard work, we've just released mp-units 2.0.0. It can be obtained from GitHub and Conan.

    The list of the most significant changes introduced by the new version can be found in our Release Notes. We will also describe some of them in this post.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#why-20-if-10-was-never-released","title":"Why 2.0 if 1.0 was never released?","text":"

    Version 2 of the mp-units project is a huge change and a new quality for the users. We did not want to pretend that 2.0 is an evolutionary upgrade of the previous version of the project. It feels like a different product.

    We could start a new repo named \"mp-units-v2\" similarly to range-v3 but we decided not to go this path. We kept the same repo and made the scope of the changes and potential breakage explicit with a drastic bump in the project version.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#what-has-changed","title":"What has changed?","text":"

    The answer is \"nearly everything\". The whole library and its documentation were rewritten nearly from scratch.

    Here are the significant changes that the users can observe:

    • Repository name

      If you didn't notice, the repository name was changed from \"mpusz/units\" to \"mpusz/mp-units\".

    • Header files content and layout

      Previously, all the header files resided in the include/units directory. Now, they can be found in include/mp-units. The project file tree was significantly changed as well. Many files were moved to different subdirectories or renamed.

    • Namespace

      Previously, all the definitions were provided in the units namespace, and now they are in the mp_units one.

    • Abstractions, interfaces, definitions

      The interfaces of all of the types were refactored. We got unit symbols and a new way to construct a quantity and quantity_point. The readability of the generated types was improved thanks to the introduction of expression templates. Nearly all of the template arguments are now passed by values thanks to class NTTP extensions in C++20. As a result, unit definitions are much easier and terser. Also, the V2 has a powerful ability to model systems of quantities and provides definitions for many ISQ quantities.

    • Conan 2.0

      Also, now we support Conan 2.0, which provides an updated way of handling dependencies.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#what-is-gone","title":"What is gone?","text":"

    Some cornerstones of the initial design did not prove in practice and were removed while we moved to version 2.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#the-downcasting-facility","title":"The downcasting facility","text":"

    The first and the most important of such features was removing the downcasting facility. This feature is still a powerful metaprogramming technique that allows users to map long class template instantiations to nicely named, short, and easy-to-understand user's strong types.

    Such mapping works perfectly fine for 1-to-1 relationships. However, we often deal with N-to-1 connections in the quantities and units domain. Here are only a few such examples:

    • work and torque have the same dimension \\(L^2MT^{-2}\\),
    • becquerel and hertz have the same definition of \\(s^{-1}\\),
    • litre and cubic decimetre have the same factor.

    In the above examples, multiple entities \"wanted\" to register different names for identical class template instantiations, resulting in compile-time errors. We had to invent some hacks and workarounds to make it work, but we were never satisfied with the outcome.

    Additionally, this facility could easily lead to ODR violations or provide different results depending on which header files were included in the translation units. This was too vulnerable to be considered a good practice here.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#no-udls-anymore","title":"No UDLs anymore","text":"

    Over the years, we have learned that UDLs are not a good solution. More information on this subject can be found in the Why don't we use UDLs to create quantities? chapter.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#no-construction-of-a-quantity-from-a-raw-value","title":"No construction of a quantity from a raw value","text":"

    To improve safety, we no longer allow the construction of quantities from raw values. In the new design, we always need to explicitly specify a unit to create a quantity:

    quantity q1 = 42 * m;\nquantity<si::metre> = 2 * km;\nquantity q3(42, si::metre);\n

    The previous approach was reported to be error-prone under maintenance. More on this subject can be found in the Why can't I create a quantity by passing a number to a constructor? chapter.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#new-look-and-feel","title":"New look and feel","text":"

    Here is a concise example showing you the new look and feel of the library:

    #include <mp-units/format.h>\n#include <mp-units/systems/isq/isq.h>\n#include <mp-units/systems/si/si.h>\n#include <format>\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nquantity<isq::speed[m / s]> avg_speed(quantity<si::metre> d,\n                                      quantity<si::second> t)\n{ return d / t; }\n\nint main()\n{\n  auto speed = avg_speed(220 * km, 2 * h);\n  std::println(\"{}\", speed);  // 30.5556 m/s\n}\n

    All of the changes we provided, although breaking ones, resulted in much better, easier, and safer abstractions. These offer a new quantity on the market and hopefully will be appreciated by our users.

    Please check our new documentation to learn about the latest version of the project and find out how to benefit from all the new cool stuff we have here.

    "},{"location":"blog/2023/12/09/mp-units-210-released/","title":"mp-units 2.1.0 released!","text":"

    A new product version can be obtained from GitHub and Conan.

    The list of the most significant changes introduced by the new version can be found in our Release Notes. We will also describe the most important of them in this post.

    "},{"location":"blog/2023/12/09/mp-units-210-released/#no-more-parenthesis-while-creating-quantities-with-derived-units","title":"No more parenthesis while creating quantities with derived units","text":"

    The V2 design introduced a way to create a quantity by multiplying a raw value and a unit:

    quantity q1 = 42 * m;\n

    However, this meant that when we wanted to create a quantity having a derived unit, we had to put parenthesis around the unit equation or create a custom value of the named unit:

    quantity q2 = 60 * (km / h);\n\nconstexpr auto kmph = km / h;\nquantity q3 = 60 * kmph;\n\nquantity q4 = 50 * (1 / s);\n

    With the new version, we removed this restriction, and now we can type:

    quantity q5 = 60 * km / h;\nquantity q6 = 50 / s;\n

    As a side effect, we introduced a breaking change . We can't use the following definition of hertz anymore:

    inline constexpr struct hertz : named_unit<\"Hz\", 1 / second, kind_of<isq::frequency>> {} hertz;\n

    and have to type either:

    inline constexpr struct hertz : named_unit<\"Hz\", one / second, kind_of<isq::frequency>> {} hertz;\n

    or

    inline constexpr struct hertz : named_unit<\"Hz\", inverse(second), kind_of<isq::frequency>> {} hertz;\n

    To be consistent, we applied the same change to the dimensions and quantity specifications definitions. Now, to define a frequency we have to type:

    C++23C++20Portable
    inline constexpr struct frequency : quantity_spec<inverse(period_duration)> {} frequency;\n
    inline constexpr struct frequency : quantity_spec<frequency, inverse(period_duration)> {} frequency;\n
    QUANTITY_SPEC(frequency, inverse(period_duration));\n
    "},{"location":"blog/2023/12/09/mp-units-210-released/#make_xxx-factory-functions-replaced-with-two-parameter-constructors","title":"make_xxx factory functions replaced with two-parameter constructors","text":"

    In the initial version of the V2 framework, if someone did not like the multiply syntax to create a quantity we provided the make_quantity() factory function. A similar approach was used for quantity_point creation.

    This version removes those ( breaking change ) and introduces two parameter constructors:

    quantity q(42, si::metre);\nquantity_point qp(q, mean_sea_level);\n

    The above change encourages a better design and results in a terser code.

    "},{"location":"blog/2023/12/09/mp-units-210-released/#improved-definitions-of-becquerel-gray-and-sievert","title":"Improved definitions of becquerel, gray, and sievert","text":"

    In the initial V2 version, we lacked the definitions of the atomic and nuclear physics quantities, which resulted in simplified and unsafe definitions of becquerel, gray, and sievert units. We still do not model most of the quantities from this domain, but we've added the ones that are necessary for the definition of those units.

    Thanks to the above, the following expressions will not compile:

    quantity q1 = 1 * Hz + 1 * Bq;\nquantity<si::sievert> q2 = 42 * Gy;\n
    "},{"location":"blog/2023/12/09/mp-units-210-released/#compatibility-with-other-libraries-redesigned","title":"Compatibility with other libraries redesigned","text":"

    Another significant improvement in this version was redesigning the way we provide compatibility with other similar libraries. The interfaces of quantity_like_traits and quantity_point_like_traits were changed and extended to provide conversion not only from but also to entities from other libraries ( breaking change ).

    We've also introduced an innovative approach that allows us to specify if such conversions should happen implicitly or if they need to be forced explicitly.

    More on this subject can be found in the Interoperability with Other Libraries chapter.

    "},{"location":"blog/2023/12/09/mp-units-210-released/#point-origins-can-now-be-derived-from-each-other","title":"Point origins can now be derived from each other","text":"

    Previously, each class derived from absolute_point_origin was considered a unique independent point origin. On the other hand, it was OK to derive multiple classes from the same relative_point_origin, and those were specifying the same point in the domain. We found this confusing and limiting. This is why, in this version, the absolute_point_origin uses a CRTP idiom to be able to detect between points that should be considered different from the ones that should be equivalent.

    If we derive from the same instantiation of absolute_point_origin we end up with an equivalent point origin. This change allows us to provide different names for the same temperature points:

    inline constexpr struct absolute_zero : absolute_point_origin<absolute_zero, isq::thermodynamic_temperature> {} absolute_zero;\ninline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin;\n\ninline constexpr struct ice_point : relative_point_origin<absolute_zero + 273.15 * kelvin> {} ice_point;\ninline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius;\n

    Please note that this is a breaking change as well.

    "},{"location":"blog/2023/12/09/mp-units-210-released/#unit-symbol-text-can-now-be-properly-used-at-runtime","title":"Unit symbol text can now be properly used at runtime","text":"

    The interface of the previous definition of unit_symbol function allowed the use of the returned buffer only at compile-time. This was too limiting as users often want to use unit symbols at runtime (e.g., print them to the console). The new version redesigned the interface of this function ( breaking change ) to return a buffer that can be properly used at both compilation and runtime:

    std::string_view unit1 = unit_symbol(m / s);\nstd::cout << unit1 << \"\\n\";     // m/s\nstd::string_view unit2 = unit_symbol<{.solidus = unit_symbol_solidus::never}>(m / s);\nstd::cout << unit2 << \"\\n\";     // m s\u207b\u00b9\n
    "},{"location":"blog/2024/06/14/mp-units-220-released/","title":"mp-units 2.2.0 released!","text":"

    A new product version can be obtained from GitHub and Conan.

    Among other features, this release provides long-awaited support for C++20 modules, redesigns and enhances text output formatting, and greatly simplifies quantity point usage. This post describes those and a few other smaller interesting improvements, while a much longer list of the most significant changes introduced by the new version can be found in our Release Notes.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#c20-modules-and-project-structure-cleanup","title":"C++20 modules and project structure cleanup","text":"

    GitHub Issue #7 was our oldest open issue before this release. Not anymore. After 4.5 years, we finally closed it, even though the C++ modules' support is still really limited.

    Info

    To benefit from C++ modules, we need at least:

    • CMake 3.29.3
    • Ninja 1.11
    • clang-17

    In the upcoming months, hopefully, the situation will improve with the bug fixes in CMake, gcc-14, and MSVC.

    Note

    More requirements for C++ modules support can be found in the CMake's documentation.

    To enable the compilation and distribution of C++ modules, a cxx_modules Conan or MP_UNITS_BUILD_CXX_MODULES CMake option has to be enabled.

    With the above, the following C++ modules will be provided:

    flowchart TD\n    mp_units --- mp_units.systems --- mp_units.core
    C++ Module CMake Target Contents mp_units.core mp-units::core Core library framework and systems-independent utilities mp_units.systems mp-units::systems All the systems of quantities and units mp_units mp-units::mp-units Core + Systems

    The easiest way to use them is just to import mp_units; at the beginning of your translation unit (see the Quick Start chapter for some usage examples).

    Note

    C++20 modules support still have some issues when imported from the installed CMake target. See the following GitLab issue for more details.

    In this release, we also highly limited the number of CMake targets ( breaking change ). Now, they correspond exactly to the C++ modules they provide. This means that many smaller partial targets were removed. We also merged text output targets with the core library's definition.

    The table below specifies where we can now find the contents of previously available CMake targets:

    Before Now mp-units::utility mp-units::core mp-units::core-io mp-units::core mp-units::core-fmt mp-units::core mp-units::{system_name} mp-units::systems

    While we were enabling C++ modules, we also had to refactor our header files slightly ( breaking change ). Some had to be split into smaller pieces (e.g., math.h), while others had to be moved to a different subdirectory (e.g., chrono.h).

    In version 2.2, the following headers have a new location or contents:

    Header File C++ Module Contents mp-units/math.h mp_units.core System-independent functions only mp-units/systems/si/math.h mp_units.systems Trigonometric functions using si::radian mp-units/systems/angular/math.h mp_units.systems Trigonometric functions using angular::radian mp-units/systems/si/chrono.h mp_units.systems std::chrono compatibility traits

    Benefiting from this opportunity, we also cleaned up core and systems definitions ( breaking change ).

    Regarding the library's core, we removed core.h and exposed only one header framework.h that provides all of the library's framework so the user does not have to enumerate files like unit.h, quantity.h, and quantity_point.h anymore. Those headers are not gone, they were put to the mp-units/framework subheader. So they are still there if you really need them.

    Regarding the changes in systems definitions, we moved the wrapper header files with the entire system definition to the mp-units/systems subdirectory. Additionally, they now also include framework.h, so a system include is enough to use the library. Thanks to that our users don't have to write tedious code like the below anymore:

    NowBefore
    #include <mp-units/systems/cgs.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n\n// ...\n
    #include <mp-units/quantity_point.h>\n#include <mp-units/systems/cgs/cgs.h>\n#include <mp-units/systems/international/international.h>\n#include <mp-units/systems/isq/isq.h>\n#include <mp-units/systems/si/si.h>\n\n// ...\n

    Additionally, we merged all of the compatibility-related macros into one header file mp-units/compat_macros.h. This header file should be explicitly included before importing C++ modules if we want to benefit from the Wide Compatibility tools.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#better-control-over-the-librarys-api","title":"Better control over the library's API","text":"

    With this release, nearly all of the Conan and CMake build options were refactored with the intent of providing better control over the library's API.

    Previously, the library used the latest available feature set supported by a specific compiler. For example, quantity_spec definitions would use CRTP on an older compiler or provide a simpler API on a newer one thanks to the C++23 this deduction feature. This could lead to surprising results where the same code written by the user would compile fine on one compiler but not the other.

    From this release, all API extensions have their corresponding configuration options in Conan and CMake. With this, a user has full control over the API exposed by the library. Those options expose three values:

    • True - The feature is always enabled (the configuration error will happen if the compiler does not support this feature)
    • False - The feature is disabled, and an older alternative is always used.
    • Auto - The feature is automatically enabled if the compiler supports it (old behavior).

    Additionally, some CMake options were renamed to better express the impact on our users ( breaking change ). For example, now CMake options include:

    • MP_UNITS_API_* - options affecting the library's API,
    • MP_UNITS_BUILD_* - options affecting the build process,
    • MP_UNITS_DEV_* - options primarily useful for the project developers or people who want to compile our unit tests and examples.
    "},{"location":"blog/2024/06/14/mp-units-220-released/#configurable-contracts-checking","title":"Configurable contracts checking","text":"

    Before this release, the library always depended on gsl-lite to perform runtime contract and asserts checking. In this release we introduced new options to control if contract checking should be based on gsl-lite, ms-gsl, or may be completely disabled.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#freestanding-support","title":"Freestanding support","text":"

    From this release it is possible to configure the library in the freestanding mode. This limits the functionality and interface of the library to be compatible with the freestanding implementations.

    Info

    To learn more, please refer to the Build options chapter.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#simplified-quantity-point-support","title":"Simplified quantity point support","text":"

    This release significantly simplifies the usage of quantity points and affine space abstractions in general.

    Previously, the user always had to define an explicit point origin even if the domain being modeled does not have such an explicit origin. Now, in such cases, we can benefit from the implicit point origins. For example:

    NowBefore
    quantity_point price_usd{100 * USD};\n
    constexpr struct zero final : absolute_point_origin<currency> {} zero;\n\nquantity_point price_usd = zero + 100 * USD;\n

    As we can see above, the new design allows direct-initializing quantity_point class template from a quantity, but only if the former one is defined in terms of the implicit point origin. Otherwise, an explicit origin still always has to be provided during initialization.

    Also, we introduced the possibility of specifying a default point origin in the unit definition. With that, we could provide proper temperature scales without forcing the user to always use the origins explicitly. Also, a new member function, .quantity_from_zero(), was introduced that always returns the quantity from the unit's specific point origin or from the implicit point origin otherwise.

    NowBefore
    quantity_point temp{20 * deg_C};\nstd::cout << \"Temperature: \" << temp << \" (\"\n          << temp.in(deg_F).quantity_from_zero() << \", \"\n          << temp.in(K).quantity_from_zero() << \")\\n\";\n
    quantity_point temp = si::zeroth_degree_Celsius + 20 * deg_C;\nstd::cout << \"Temperature: \" << temp << \" (\"\n          << temp.in(deg_F).quantity_from(usc::zeroth_degree_Fahrenheit) << \", \"\n          << temp.in(K).quantity_from(si::zeroth_kelvin) << \")\\n\";\n

    More information about the new design can be found in The Affine Space chapter.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#unified-temperature-point-origins-names","title":"Unified temperature point origins names","text":"

    By omission, we had the following temperature point origins in the library:

    • si::zero_kelvin (for si::kelvin),
    • si::zeroth_degree_Celsius (for si::degree_Celsius),
    • usc::zero_Fahrenheit (for usc::degree_Fahrenheit).

    With this release, the last one was renamed to usc::zeroth_degree_Fahrenheit to be consistently named with its corresponding unit and with the si::zeroth_degree_Celsius ( breaking change ).

    "},{"location":"blog/2024/06/14/mp-units-220-released/#changes-to-units-definitions","title":"Changes to units definitions","text":"

    There were several known issues when units were deriving from each other (e.g., #512 and #537). We could either highly complicate the framework to allow these which could result in much longer compilation times or disallow inheriting from units at all. We chose the second option.

    With this release all of of the units must be marked as final. To enforce this we have changed the definition of the Unit<T> concept, which now requires type T to be final ( breaking change ).

    WG21 Study Group 16 (Unicode) raised concerns about potential ABI issues when different translation units are compiled with different ordinary literal encodings. Those issues were resolved with a change to units definitions ( breaking change ). It affects only units that specify both Unicode and ASCII symbols. The new design requires the Unicode symbol to be provided as a UTF-8 literal.

    This also means that the basic_symbol_text has fixed character types for both symbols. This is why it was renamed to symbol_text ( breaking change ).

    NowBefore
    inline constexpr struct ohm final : named_unit<symbol_text{u8\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n
    inline constexpr struct ohm : named_unit<basic_symbol_text{\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n

    Note

    On C++20-compliant compilers it should be enough to type the following in the unit's definition:

    inline constexpr struct ohm final : named_unit<{u8\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n
    "},{"location":"blog/2024/06/14/mp-units-220-released/#changes-to-dimension-quantity-specification-and-point-origins-definitions","title":"Changes to dimension, quantity specification, and point origins definitions","text":"

    Similarly to units, now also all dimensions, quantity specifications, and point origins have to be marked final ( breaking change ).

    NowBefore
    inline constexpr struct dim_length final : base_dimension<\"L\"> {} dim_length;\ninline constexpr struct length final : quantity_spec<dim_length> {} length;\n\ninline constexpr struct absolute_zero final : absolute_point_origin<isq::thermodynamic_temperature> {} absolute_zero;\ninline constexpr auto zeroth_kelvin  = absolute_zero;\ninline constexpr struct kelvin final : named_unit<\"K\", kind_of<isq::thermodynamic_temperature>, zeroth_kelvin> {} kelvin;\n\ninline constexpr struct ice_point final : relative_point_origin<quantity_point{273'150 * milli<kelvin>}> {} ice_point;\ninline constexpr auto zeroth_degree_Celsius = ice_point;\ninline constexpr struct degree_Celsius final : named_unit<symbol_text{u8\"\u2103\", \"`C\"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;\n
    inline constexpr struct dim_length : base_dimension<\"L\"> {} dim_length;\ninline constexpr struct length : quantity_spec<dim_length> {} length;\n\ninline constexpr struct absolute_zero : absolute_point_origin<absolute_zero, isq::thermodynamic_temperature> {} absolute_zero;\ninline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin;\ninline constexpr struct kelvin : named_unit<\"K\", kind_of<isq::thermodynamic_temperature>, zeroth_kelvin> {} kelvin;\n\ninline constexpr struct ice_point : relative_point_origin<quantity_point{273'150 * milli<kelvin>}> {} ice_point;\ninline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius;\ninline constexpr struct degree_Celsius : named_unit<symbol_text{u8\"\u2103\", \"`C\"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;\n

    Please also note, that the absolute_point_origin does not use CRTP idiom anymore ( breaking change ).

    "},{"location":"blog/2024/06/14/mp-units-220-released/#improved-text-output","title":"Improved text output","text":"

    With this release, we can print not only whole quantities but also just their units or dimensions. Also, we fixed the std::format support so users can now enjoy full C++20 compatibility and don't have to use fmtlib anymore.

    We have also changed the grammar for quantities formatting ( breaking change ). It introduces the composition of underlying formatters that finally allows us to properly format user-defined representation types (assuming they have std::format support). Additionally, thanks to a new %? token, we can provide a custom format string that will properly print quantity of any unit.

    Here is a small preview of what is now available:

    using namespace mp_units::si::unit_symbols;\nusing namespace mp_units::international::unit_symbols;\nquantity q = (90. * km / h).in(mph);\n\nstd::cout << \"Number: \" << q.numerical_value_in(mph) << \"\\n\";\nstd::cout << \"Unit: \" << q.unit << \"\\n\";\nstd::cout << \"Dimension: \" << q.dimension << \"\\n\";\nstd::println(\"{::N[.2f]}\", q);\nstd::println(\"{:.4f} in {} of {}\", q.numerical_value_in(mph), q.unit, q.dimension);\nstd::println(\"{:%N in %U of %D:N[.4f]}\", q);\n
    Number: 55.9234\nUnit: mi/h\nDimension: LT\u207b\u00b9\n55.92 mi/h\n55.9234 in mi/h of LT\u207b\u00b9\n55.9234 in mi/h of LT\u207b\u00b9\n

    More on this subject can be found in the updated Text Output chapter.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#improved-casts","title":"Improved casts","text":"

    We added a new conversion function. value_cast<Unit, Representation> forces the conversion of both a unit and representation type in one step and always ensures that the best precision is provided.

    Also, we have finally added proper implementations of value_cast and quantity_cast for quantity points.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#even-better-error-messages","title":"Even better error messages","text":"

    This release made a few small refactorings that, without changing the user-facing API, allowed us to improve the readability of the generated types that can be observed in the compilation errors.

    Example 1 (clang):

    NowBefore
    error: no matching function for call to 'time_to_goal'\n   26 |   const quantity ttg = time_to_goal(half_marathon_distance, pace);\n      |                        ^~~~~~~~~~~~\nnote: candidate template ignored: constraints not satisfied [with distance:auto = quantity<kilo_<metre>{}, double>,\n                                                                  speed:auto = quantity<derived_unit<second, per<kilo_<metre>>>{}, double>]\n   13 | QuantityOf<isq::time> auto time_to_goal(QuantityOf<isq::length> auto distance,\n      |                            ^\nnote: because 'QuantityOf<quantity<derived_unit<si::second, per<si::kilo_<si::metre> > >{{{}}}>, isq::speed>' evaluated to false\n   14 |                                         QuantityOf<isq::speed> auto speed)\n      |                                         ^\nnote: because 'QuantitySpecOf<std::remove_const_t<decltype(quantity<derived_unit<second, per<kilo_<metre> > >{{{}}}, double>::quantity_spec)>, struct speed{{{}}}>' evaluated to false\n   61 | concept QuantityOf = Quantity<Q> && QuantitySpecOf<std::remove_const_t<decltype(Q::quantity_spec)>, QS>;\n      |                                     ^\nnote: because 'implicitly_convertible(kind_of_<derived_quantity_spec<isq::time, per<isq::length> > >{}, struct speed{{{}}})' evaluated to false\n  147 |   QuantitySpec<T> && QuantitySpec<decltype(QS)> && implicitly_convertible(T{}, QS) &&\n      |                                                                         ^\n1 error generated.\nCompiler returned: 1\n
    error: no matching function for call to 'time_to_goal'\n   26 |   const quantity ttg = time_to_goal(half_marathon_distance, pace);\n      |                        ^~~~~~~~~~~~\nnote: candidate template ignored: constraints not satisfied [with distance:auto = quantity<kilo_<metre{{}}>{}, double>,\n                                                                  speed:auto = quantity<derived_unit<second, per<kilo_<metre{{}}>>>{}, double>]\n   13 | QuantityOf<isq::time> auto time_to_goal(QuantityOf<isq::length> auto distance,\n      |                            ^\nnote: because 'QuantityOf<quantity<derived_unit<si::second, per<si::kilo_<si::metre{{}}> > >{{{}}}>, isq::speed>' evaluated to false\n   14 |                                         QuantityOf<isq::speed> auto speed)\n      |                                         ^\nnote: because 'QuantitySpecOf<std::remove_const_t<decltype(quantity<derived_unit<second, per<kilo_<metre{{}}> > >{{{}}}, double>::quantity_spec)>, struct speed{{{}}}>' evaluated to false\n   61 | concept QuantityOf = Quantity<Q> && QuantitySpecOf<std::remove_const_t<decltype(Q::quantity_spec)>, QS>;\n      |                                     ^\nnote: because 'implicitly_convertible(kind_of_<derived_quantity_spec<isq::time, per<isq::length> >{{}, {{}}}>{}, struct speed{{{}}})' evaluated to false\n  147 |   QuantitySpec<T> && QuantitySpec<decltype(QS)> && implicitly_convertible(T{}, QS) &&\n      |                                                                         ^\n1 error generated.\nCompiler returned: 1\n

    Example 2 (gcc):

    NowBefore
    error: no matching function for call to 'Box::Box(quantity<reference<isq::height, si::metre>(), int>, quantity<reference<horizontal_length, si::metre>(), int>,\n                                                  quantity<reference<isq::width, si::metre>(), int>)'\n   27 | Box my_box(isq::height(1 * m), horizontal_length(2 * m), isq::width(3 * m));\n      |                                                                           ^\nnote: candidate: 'Box::Box(quantity<reference<horizontal_length, si::metre>()>, quantity<reference<isq::width, si::metre>()>,\n                                          quantity<reference<isq::height, si::metre>()>)'\n   19 |   Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n      |   ^~~\nnote:   no known conversion for argument 1 from 'quantity<reference<isq::height, si::metre>(),int>'\n        to 'quantity<reference<horizontal_length, si::metre>(),double>'\n   19 |   Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n
    error: no matching function for call to 'Box::Box(quantity<reference<isq::height(), si::metre()>(), int>, quantity<reference<horizontal_length(), si::metre()>(), int>,\n                                                  quantity<reference<isq::width(), si::metre()>(), int>)'\n   27 | Box my_box(isq::height(1 * m), horizontal_length(2 * m), isq::width(3 * m));\n      |                                                                           ^\nnote: candidate: 'Box::Box(quantity<reference<horizontal_length(), si::metre()>()>, quantity<reference<isq::width(), si::metre()>()>,\n                                          quantity<reference<isq::height(), si::metre()>()>)'\n   19 |   Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n      |   ^~~\nnote:   no known conversion for argument 1 from 'quantity<reference<isq::height(), si::metre()>(),int>'\n        to 'quantity<reference<horizontal_length(), si::metre()>(),double>'\n   19 |   Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n
    "},{"location":"blog/2024/06/14/mp-units-220-released/#mathh-header-changes","title":"math.h header changes","text":"

    This release provided lots of changes to the mp_units/math.h header file.

    First, we got several outstanding contributions:

    • fma, isfinite, isinf, and isnan math functions were added by @NAThompson,
    • ppm, atan2, fmod, and remainder were added by @nebkat.

    Thanks!

    Additionally, we changed the namespace for trigonometric functions using SI units. Now they are inside of the mp_units::si subnamespace and not in mp_units::isq like it was the case before ( breaking change ).

    Also, the header itself was split into smaller pieces that improve C++20 modules definitions.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#ratio-made-an-implementation-detail-of-the-library","title":"ratio made an implementation detail of the library","text":"

    We decided not to expose ratio and associated interfaces in the public part of the library ( breaking change ). Standardization of it could be problematic as we have std::ratio already.

    Alternatively, as in the public interface it was always only used with mag, we introduced a new helper called mag_ratio to provide the magnitude of the unit defined in terms of a rational conversion factor. Here is a comparison of the code with previous and current definitions:

    NowBefore
    inline constexpr struct yard final : named_unit<\"yd\", mag_ratio<9'144, 10'000> * si::metre> {} yard;\ninline constexpr struct foot final : named_unit<\"ft\", mag_ratio<1, 3> * yard> {} foot;\n
    inline constexpr struct yard : named_unit<\"yd\", mag<ratio{9'144, 10'000}> * si::metre> {} yard;\ninline constexpr struct foot : named_unit<\"ft\", mag<ratio{1, 3}> * yard> {} foot;\n
    "},{"location":"blog/2024/09/27/mp-units-230-released/","title":"mp-units 2.3.0 released!","text":"

    A new product version can be obtained from GitHub and Conan.

    This release fine-tunes many key features of the library. This post describes the most interesting improvements, while a much longer list of the changes introduced by the new version can be found in our Release Notes.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#cmake-and-conan-options-changed","title":"CMake and Conan options changed","text":"

    During the review on the ConanCenter, we got feedback that we should improve the handling of options for which value is automatically determined based on the current configuration. Instead of explicitly setting the auto value, we defer the choice between True/False until the configuration stage and set it there once all the settings are known. auto value for such option was removed ( breaking change ).

    If you didn't set any value at the command line for such options, everything stays the same for you. However, some changes are needed if you explicitly used auto like below:

    conan install . -o 'mp-units:std_format=auto' -s compiler.cppstd=23 -b missing\n

    Now you have to either skip such an option to keep automatic deduction:

    conan install . -s compiler.cppstd=23 -b missing\n

    or set it explicitly to True or False to force a specific configuration:

    conan install . -o 'mp-units:std_format=True' -s compiler.cppstd=23 -b missing\n
    "},{"location":"blog/2024/09/27/mp-units-230-released/#msvc-compiler-support","title":"MSVC compiler support","text":"

    The MSVC compiler has the most bugs in the C++20 support from all our compilers. However, with this release, we could apply many workarounds to the library's code to make it work. \ud83c\udf89

    Please note those workarounds were only applied to the library's code and not to our unit tests and examples. Trying to build the entire project on MSVC will inevitably fail to compile unless the MSVC bugs are resolved. MSVC developers still have some work to do.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#representation-type-template-parameter-added-to-value-conversion-functions","title":"Representation type template parameter added to value conversion functions","text":"

    Previously, changing a representation type was only possible with a value_cast<NewRep>(q) non-member function while a change of unit was supported by all value_cast<NewU>(q), q.in(NewU), and q.force_in(NewU). The rationale for it was that passing an explicit type to a member function template requires a template disambiguator when we are dealing with a dependent name (e.g., quantity type is determined based on a template parameter).

    During a discussion in LEWGI at the St. Louis WG21 Meeting, we decided to provide such additional overloads despite possible issues when a dependent name is used. In such case, a user needs to provide a template disambiguator or switch back to using value_cast:

    // non-dependent name\nauto f(quantity<m, int> q)             { return q.in<double>(km); }\nauto g(quantity<m, int> q)             { return value_cast<double, km>(q); }\n\n// dependent name\nauto h(QuantityOf<isq::length> auto q) { return q.template in<double>(km); }\nauto i(QuantityOf<isq::length> auto q) { return value_cast<double, km>(q); }\n

    The table below provides all the value conversion functions in mp-units that may be run on x being the instance of either quantity or quantity_point:

    Forcing Representation Unit Member function Non-member function No Same u x.in(u) No T Same x.in<T>() No T u x.in<T>(u) Yes Same u x.force_in(u) value_cast<u>(x) Yes T Same x.force_in<T>() value_cast<T>(x) Yes T u x.force_in<T>(u) value_cast<u, T>(x) or value_cast<T, u>(x)"},{"location":"blog/2024/09/27/mp-units-230-released/#quantity-reference-specifiers","title":"Quantity reference specifiers","text":"

    The features described in this chapter directly solve an issue raised on std-proposals reflector. As it was reported, the code below may look correct, but it provides an invalid result:

    quantity Volume = 1.0 * m3;\nquantity Temperature = 28.0 * deg_C;\nquantity n_ = 0.04401 * kg / mol;\nquantity R_boltzman = 8.314 * N * m / (K * mol);\nquantity mass = 40.0 * kg;\nquantity Pressure = R_boltzman * Temperature.in(K) * mass / n_ / Volume;\nstd::cout << Pressure << \"\\n\";\n

    The problem is related to the accidental usage of a quantity rather than quantity_point for Temperature. This means that after conversion to kelvins, we will get 28 K instead of the expected 301.15 K, corrupting all further calculations.

    A correct code should use a quantity_point:

    quantity_point Temperature(28.0 * deg_C);\n

    This might be an obvious thing for domain experts, but new users of the library may not be aware of the affine space abstractions and how they influence temperature handling.

    After a lengthy discussion on handling such scenarios, we decided to:

    • make the above code deprecated ( breaking change ),
    • provide an alternative way to create a quantity with the delta quantity construction helper.

    Here are the main points of this new design:

    1. All references/units that specify point origin in their definition (i.e., si::kelvin, \u00a0 \u00a0si::degree_Celsius, and usc::degree_Fahrenheit) are excluded from the multiply syntax ( breaking change ).
    2. A new delta quantity construction helper is introduced:

      • delta<m>(42) results with a quantity<si::metre, int>,
      • delta<deg_C>(5) results with a quantity<si::deg_C, int>.
    3. A new absolute quantity point construction helper is introduced:

      • absolute<m>(42) results with a quantity_point<si::metre, zeroth_point_origin<kind_of<isq::length>>{}, int>,
      • absolute<deg_C>(5) results with a quantity<si::metre, si::ice_point, int>.

    Info

    Please note that si::kelvin is also excluded from the multiply syntax to prevent the following surprising issues:

    NowBefore
    quantity q = delta<K>(300);\nquantity_point qp = absolute<K>(300);\nstatic_assert(q.in(deg_C) != qp.in(deg_C).quantity_from_zero());\n
    quantity q(300 * K);\nquantity_point qp(300 * K);\nstatic_assert(q.in(deg_C) != qp.in(deg_C).quantity_from_zero());\n

    We believe that the code enforced with new utilities makes it much easier to understand what happens here.

    With such changes to the interface design, the offending code will not compile as initially written. Users will be forced to think more about what they write. To enable the compilation, the users have to create explicitly:

    • a quantity_point (the intended abstraction in this example) with any of the below syntaxes:

      quantity_point Temperature = absolute<deg_C>(28.0);\nauto Temperature = absolute<deg_C>(28.0);\nquantity_point Temperature(delta<deg_C>(28.0));\n
    • a quantity (an incorrect abstraction in this example) with:

      quantity Temperature = delta<deg_C>(28.0);\nauto Temperature = delta<deg_C>(28.0);\n

    Thanks to the new design, we can immediately see what happens here and why the result might be incorrect in the second case.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#quantity_point_like_traits-are-based-on-numerical-value-instead-of-a-quantity","title":"quantity_point_like_traits are based on numerical value instead of a quantity","text":"

    In this release, we decided to fine-tune the traits that customize the conversion between custom quantity point types and the ones provided with mp-units ( breaking change ).

    Previously, such type traits were based on the quantity type. This was inconsistent with quantity_like_traits, that is working on raw values. Also, there are cases where a custom quantity point abstraction is not modelled with a quantity type. In such cases, the previous approach required additional types to be introduced for no good reason.

    NowBefore
    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  using rep = decltype(Timestamp::seconds);\n\n  static constexpr convert_implicitly<rep> to_numerical_value(Timestamp ts)\n  {\n    return ts.seconds;\n  }\n\n  static constexpr convert_explicitly<Timestamp> from_numerical_value(rep v)\n  {\n    return Timestamp(v);\n  }\n};\n
    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  using rep = decltype(Timestamp::seconds);\n\n  static constexpr convert_implicitly<quantity<reference, rep>> to_quantity(Timestamp ts)\n  {\n    return ts.seconds * si::second;\n  }\n\n  static constexpr convert_explicitly<Timestamp> from_quantity(quantity<reference, rep> q)\n  {\n    return Timestamp(q.numerical_value_ref_in(si::second));\n  }\n};\n

    Note

    The old behavior is deprecated and will be removed in future releases.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#magpi","title":"mag<pi>","text":"

    With this release, we introduced a new strongly-typed constant to create a magnitude involving scaling by pi. The solution used before was not consistent with magnitudes of integral values and also was leaking a floating-point value of std::numbers::pi_v<long double> to the resulting magnitude type. With the new approach, this is no longer the case, and the user-facing interface is more consistent:

    NowBefore
    inline constexpr struct degree final : named_unit<{u8\"\u00b0\", \"deg\"}, mag<pi> / mag<180> * si::radian> {} degree;\n
    inline constexpr struct degree final : named_unit<{u8\"\u00b0\", \"deg\"}, mag_pi / mag<180> * si::radian> {} degree;\n

    Note

    The old mag_pi helper is marked as deprecated and will be removed in future releases.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#common-units","title":"Common units","text":"

    Adding or subtracting two quantities of different units will force the library to find a common unit for those. This is to prevent data truncation. For the cases when one of the units is an integral multiple of the another, the resulting quantity will use a \"smaller\" one in its result. For example:

    static_assert((1 * kg + 1 * g).unit == g);\nstatic_assert((1 * km + 1 * mm).unit == mm);\nstatic_assert((1 * yd + 1 * mi).unit == yd);\n

    However, in many cases an arithmetic on quantities of different units will result in a yet another unit. This happens when none of the source units is an integral multiple of another. In such cases, the library returns a special type that denotes that we are dealing with a common unit of such an equation.

    Previously we returned a scaled unit calculated against our arbitrarily appointed reference unit. This resulted often in a long and messy type exposing the prime-factorized magnitude of the unit (implementation detail). In this release, we introduced a new common_unit wrapper for such cases:

    NowBefore
    quantity q = 1 * km + 1 * mi;  // quantity<common_unit<international::mile, si::kilo_<si::metre>>{}, int>\n
    quantity q = 1 * km + 1 * mi;  // quantity<scaled_unit<magnitude<power_v<2, 3>{}, power_v<5, -3>{}>{}, si::metre>{}, int>\n

    Note

    A user should never explicitly instantiate a common_unit class template. The library's framework will do it based on the provided quantity equation.

    Such units need special printing rules for their symbols. As they represent a minimum set of common units resulting from the addition or subtraction of multiple quantities, from this release, we print all of them as a scaled version of the source unit. Previously we were printing them relative to some arbitrary reference unit (implementation detail) that often was not spelled by the user at all in the source code. For example the following:

    std::cout << 1 * km + 1 * mi << \"\\n\";\nstd::cout << 1 * nmi + 1 * mi << \"\\n\";\nstd::cout << 1 * km / h + 1 * m / s << \"\\n\";\n

    will print:

    NowBefore
    40771 ([1/25146] mi = [1/15625] km)\n108167 ([1/50292] mi = [1/57875] nmi)\n23 ([1/5] km/h = [1/18] m/s)\n
    40771 [8/125] m\n108167 [4/125] m\n23 [1/18] m/s\n

    Thanks to the above, it might be easier for the user to reason about the magnitude of the resulting unit and its impact on the value stored in the quantity.

    Info

    In order to provide common_unit strong type unit wrapper we had to rename all the common_XXX() functions to get_common_XXX() ( breaking change ).

    "},{"location":"blog/2024/09/27/mp-units-230-released/#superpowers-of-the-unit-one","title":"Superpowers of the unit one","text":"

    In this release, we also added a long-awaited change. From now on a quantity of a unit one can be:

    • implicitly constructed from the raw value,
    • explicitly converted to a raw value,
    • compared to a raw value.
    NowBefore
    quantity<one> inc(quantity<one> q) { return q + 1; }\nvoid legacy(double) { /* ... */ }\n\nif (auto q = inc(42); q != 0)\n  legacy(static_cast<int>(q));\n
    quantity<one> inc(quantity<one> q) { return q + 1 * one; }\nvoid legacy(double) { /* ... */ }\n\nif (auto q = inc(42 * one); q != 0 * one)\n  legacy(q.numerical_value_in(one));\n

    This property also expands to usual arithmetic operators.

    With the above change, we can now achieve the same results in a terser way:

    NowBefore
    static_assert(10 * km / (5 * km) == 2);\nconst quantity gain = 1. / index;\n
    static_assert(10 * km / (5 * km) == 2 * one);\nconst quantity gain = 1. / index * one;\n

    Note

    Those rules do not apply to all the dimensionless quantities. It would be unsafe and misleading to allow such operations on units with a magnitude different than 1 (e.g., percent or radian).

    "},{"location":"blog/2024/09/27/mp-units-230-released/#import-std-support","title":"import std; support","text":"

    This release brings experimental support for import std;. The only compiler that supports it for now is clang-18+. Until all the compilers start to support it and CMake removes the experimental tag from this feature, we will also keep it experimental.

    As all of the C++ compilers are buggy for now, it is not allowed to bring the same definitions through the import std; and regular header files. This applies not only to the current project but also to all its dependencies. This is why, in order to use it with mp-units, we need to disable all the dependencies as well (enforced with conanfile.py). It means that we have to use std::format (instead of fmtlib) and remove functions contract checking.

    With the above assumptions, we can refactor our smoot example to:

    import mp_units;\nimport std;\n\nusing namespace mp_units;\n\ninline constexpr struct smoot final : named_unit<\"smoot\", mag<67> * usc::inch> {} smoot;\n\nint main()\n{\n  constexpr quantity dist = 364.4 * smoot;\n  std::println(\"Harvard Bridge length = {::N[.1f]} ({::N[.1f]}, {::N[.2f]}) \u00b1 1 \u03b5ar\",\n               dist, dist.in(usc::foot), dist.in(si::metre));\n}\n
    "},{"location":"blog/2024/09/27/mp-units-230-released/#unit_can_be_prefixed-removed","title":"unit_can_be_prefixed removed","text":"

    Previously, the unit_can_be_prefixed type trait was used to limit the possibility to prefix some units that are officially known as non-prefixable (e.g., hour, minute).

    It turned out that it is not easy to determine whether some units can be prefixed. For example, for degree Celsius, the ISO 80000-5 standard explicitly states:

    Prefixes are not allowed in combination with the unit \u00b0C.

    On the other hand this NIST page says:

    Prefix symbols may be used with the unit symbol \u00baC and prefix names may be used with the unit name \u201cdegree Celsius.\u201d For example, 12 m\u00baC (12 millidegrees Celsius) is acceptable.

    It seems that it is also a common engineering practice.

    To prevent such issues, we decided to simplify the library's design and remove the unit_can_be_prefixed type trait ( breaking change ).

    From now on, every named unit in the library can be prefixed with the SI or IEC prefix.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#iec80000-system-renamed-to-iec","title":"iec80000 system renamed to iec","text":"

    As we mentioned IEC already, in this release, we decided to rename the name of the system and its corresponding namespace from iec80000 to iec ( breaking change ). This involves renaming of a defining header file and of the namespace it provides.

    With this change it should be easier to type the namespace name. This name is also more correct for some quantities and units that are introduced by IEC but not necessarily in the ISO/IEC 80000 series of documents (e.g., iec::var).

    Note

    The old iec80000 namespace in iec8000.h is marked as deprecated and will be removed in future releases.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#error-messages-related-improvements","title":"Error messages-related improvements","text":"

    The readability of compile-time error messages is always a challenge for generic C++ libraries. However, for quantities and units library, generating readable errors is the most important requirement. If you do not make errors, you do not need such a library in your project .

    This is why we put lots of effort into improving here. Besides submitting compiler bugs to improve on their part, we also try to do our best here.

    Some compilers do not present the type resulting from calling a function within a template argument. This ends up with statements like get_quantity_spec(si::second{}) in the error message. Some less experienced users of the library may not know what this mean, and then why the conversion error happens.

    To improve this, we injected additional helper concepts into the definitions. It results with a bit longer but a more readable error in the end.

    For example:

    NowBefore
    error: no matching member function for call to 'in'\n   15 | const quantity time_to_goal = (distance * speed).in(s);\n      |                               ~~~~~~~~~~~~~~~~~~~^~\nnote: candidate template ignored: constraints not satisfied [with ToU = struct second]\n  221 |   [[nodiscard]] constexpr QuantityOf<quantity_spec> auto in(ToU) const\n      |                                                          ^\nnote: because 'detail::UnitCompatibleWith<si::second, unit, quantity_spec>' evaluated to false\n  219 |   template<detail::UnitCompatibleWith<unit, quantity_spec> ToU>\n      |            ^\nnote: because '!AssociatedUnit<si::second>' evaluated to false\n  164 |   (!AssociatedUnit<U> || UnitOf<U, QS>) && detail::UnitConvertibleTo<FromU, U{}>;\n      |    ^\nnote: and 'UnitOf<si::second, kind_of_<derived_quantity_spec<power<isq::length, 2>, per<isq::time> > >{}>' evaluated to false\n  164 |   (!AssociatedUnit<U> || UnitOf<U, QS>) && detail::UnitConvertibleTo<FromU, U{}>;\n      |                          ^\nnote: because 'detail::QuantitySpecConvertibleTo<get_quantity_spec(si::second{}), kind_of_<derived_quantity_spec<power<isq::length, 2>, per<isq::time> > >{}>' evaluated to false\n  141 |                  detail::QuantitySpecConvertibleTo<get_quantity_spec(U{}), QS> &&\n      |                  ^\nnote: because 'implicitly_convertible(kind_of_<struct time>{}, kind_of_<derived_quantity_spec<power<isq::length, 2>, per<isq::time> > >{})' evaluated to false\n  151 |   implicitly_convertible(From, To);\n      |   ^\n1 error generated.\nCompiler returned: 1\n
    error: no matching member function for call to 'in'\n   15 | const quantity time_to_goal = (distance * speed).in(s);\n      |                               ~~~~~~~~~~~~~~~~~~~^~\nnote: candidate template ignored: constraints not satisfied [with U = struct second]\n  185 |   [[nodiscard]] constexpr QuantityOf<quantity_spec> auto in(U) const\n      |                                                          ^\nnote: because 'detail::UnitCompatibleWith<si::second, unit, quantity_spec>' evaluated to false\n  183 |   template<detail::UnitCompatibleWith<unit, quantity_spec> U>\n      |            ^\nnote: because '!AssociatedUnit<si::second>' evaluated to false\n  207 |   (!AssociatedUnit<U> || UnitOf<U, QS>)&&(detail::have_same_canonical_reference_unit(U{}, U2));\n      |    ^\nnote: and 'UnitOf<si::second, kind_of_<derived_quantity_spec<power<isq::length, 2>, per<isq::time> > >{}>' evaluated to false\n  207 |   (!AssociatedUnit<U> || UnitOf<U, QS>)&&(detail::have_same_canonical_reference_unit(U{}, U2));\n      |                          ^\nnote: because 'implicitly_convertible(get_quantity_spec(si::second{}), kind_of_<derived_quantity_spec<power<length, 2>, per<time> > >{})' evaluated to false\n  187 |   implicitly_convertible(get_quantity_spec(U{}), QS) &&\n      |   ^\n1 error generated.\nCompiler returned: 1\n

    Note

    The above error messages were stripped a bit of the additional information (file name, namespace name, nested curlies) to provide better readability.

    "},{"location":"blog/2024/11/05/mp-units-240-released/","title":"mp-units 2.4.0 released!","text":"

    A new product version can be obtained from GitHub and Conan.

    This release was unexpected. We planned a significant new feature to happen next, but while preparing for it, and also while writing API Reference documentation, we made so many vital fixes and improvements that we decided that they deserve a dedicated release first.

    This post describes the most significant improvements while a much longer list of the changes introduced by the new version can be found in our Release Notes.

    "},{"location":"blog/2024/11/05/mp-units-240-released/#isq-quantities-cleanup","title":"ISQ quantities cleanup","text":"

    Initially, we kept quantities defined in \"IEC 80000-13: Information science and technology\" in a standalone iec80000 namespace, which was renamed to iec in the previous release. It turned out that this was incorrect. Those quantities are also a part of the ISQ. This is why, in this release, we moved all of them to the isq namespace ( breaking change ).

    From now on, iec namespace does not provide any quantities and serves purely as a system of units definition. It contains binary prefixes (based on the powers of two) and some units introduced by IEC (e.g., var, erlang, bit, or `baud).

    Note

    The quantities in iec namespace are now deprecated and will be removed in future releases.

    Also, it turns out that the latest ISO 80000-3 revision makes a small cleanup to the phase_speed and group_speed quantities. Those were always defined as scalar quantities but also had alternative names phase_velocity and group_velocity. This is misleading as velocity is typically considered a vector quantity. It is why those XXX_velocity aliases were removed from the ISO standard and from mp-units library ( breaking change ).

    "},{"location":"blog/2024/11/05/mp-units-240-released/#units-equality","title":"Units equality","text":"

    Previously we assumed that units like J, N m, and kg m\u00b2/s\u00b2 are equal. In some cases, this might not be entirely correct. Some quantities require a specific derived unit instead of a unit with a special name. For example:

    • N m should be used for moment of force (instead of J),
    • V A should be used for apparent power (instead of W).

    This is why, starting from this release units like J, N m, and kg m\u00b2/s\u00b2 will not compare equal ( breaking change ). However, they are deemed equivalent:

    static_assert(equivalent(J, N * m));\nstatic_assert(equivalent(W, V * A));\n
    "},{"location":"blog/2024/11/05/mp-units-240-released/#portable-text-output","title":"Portable text output","text":"

    From the very beginning, the text output of symbols could be formatted in two different ways:

    • Unicode,
    • portable using so-called ASCII alternatives.

    mp-units used the terms \"Unicode\" or \"ASCII\" and 'U' or 'A' formatting options for them. Even though those terms are widely understood in the C++ community, they are technically incorrect.

    During the recent SG16 meeting, we looked for proper alternatives and ended up with the \"portable\" and \"UTF-8\" terms ( breaking change ).

    From now on, we will provide the following:

    • text_encoding::utf8, symbol_text<N, M>::utf8(), and U formatting option,
    • text_encoding::portable, symbol_text<N, M>::portable(), and P formatting option.

    Note

    The old identifiers and formatting options are now deprecated and will be removed in future releases.

    "},{"location":"blog/2024/11/05/mp-units-240-released/#char_traits-removed-from-fixed_string","title":"char_traits removed from fixed_string","text":"

    During the same SG16 meeting, the room was strongly against providing char_traits for fixed_string. This is why char_traits support was removed in this release ( breaking change ).

    "},{"location":"blog/2024/11/05/mp-units-240-released/#improved-units-text-output","title":"Improved units' text output","text":"

    In the previous release, we introduced common unit abstraction. Initially, all its components were printed in parenthesis which contained a list of all the scaled units separated with =. After some feedback, we decided to change it to a new syntax.

    For example, the following:

    std::cout << 1 * km + 1 * mi << \"\\n\";\nstd::cout << 1 * nmi + 1 * mi << \"\\n\";\nstd::cout << 1 * km / h + 1 * m / s << \"\\n\";\n

    will print:

    NowBefore
    40771 EQUIV{[1/25146 mi], [1/15625 km]}\n108167 EQUIV{[1/50292 mi], [1/57875 nmi]}\n23 EQUIV{[1/5 km/h], [1/18 m/s]}\n
    40771 ([1/25146] mi = [1/15625] km)\n108167 ([1/50292] mi = [1/57875] nmi)\n23 ([1/5] km/h = [1/18] m/s)\n

    As we can see above, the scaled units output changed as well. Now, the entire scaled unit is encapsulated within [...] brackets to better denote its scope.

    Additionally, small magnitudes of scaled units do not use the power of 10, and also scaled units do not have a composition priority over the derived units anymore.

    As a result of those changes, the following:

    constexpr Unit auto L_per_100km = L / (mag<100> * km);\nstd::cout << 6.7 * L_per_100km << \"\\n\";\n

    prints:

    NowBefore
    6.7 L/[100 km]\n
    6.7 \u00d7 10\u207b\u00b2 l/km\n

    One more change that we can see above is that litre now use 'L' instead of 'l' for its symbol. The latter one too often is confused with the number 1.

    The next improvement adds proper formatting support for magnitudes. All of the formatting options that were working before for the unit symbols now also work for magnitudes.

    For example:

    using enum text_encoding;\nusing enum unit_symbol_solidus;\nusing usf = unit_symbol_formatting;\n\nstatic_assert(unit_symbol(mag<1> / (mag<2> * mag<pi>)*metre) == \"[2\u207b\u00b9 \ud835\udf0b\u207b\u00b9 m]\");\nstatic_assert(unit_symbol<usf{.solidus = always}>(mag<1> / (mag<2> * mag<pi>)*metre) == \"[1/(2 \ud835\udf0b) m]\");\nstatic_assert(unit_symbol<usf{.encoding = portable, .solidus = always}>(mag<1> / (mag<2> * mag<pi>)*metre) ==\n              \"[1/(2 pi) m]\");\n

    As we can see above, the library also learned how to print magnitude symbols. This required a change in the mag_constant definition. Now, it takes a magnitude symbol and has to be final like for other similar types in the library ( breaking change ):

    inline constexpr struct pi final : mag_constant<symbol_text{u8\"\u03c0\", \"pi\"}, std::numbers::pi_v<long double>> {} pi;\ninline constexpr auto \u03c0 = pi;\n
    "},{"location":"blog/2024/11/05/mp-units-240-released/#unicode-identifiers","title":"Unicode identifiers","text":"

    The example above introduced something interesting: a \u03c0 identifier for a variable. With the latest changes to the C++ language, we can officially use Unicode symbols as identifiers in the C++ code.

    In this release, we've added Unicode identifiers support not only for \u03c0 magnitude constant but also for unit symbols.

    Now we can type the following:

    With UTF-8 glyphsPortable
    quantity resistance = 60 * k\u03a9;\nquantity capacitance = 100 * \u00b5F;\n
    quantity resistance = 60 * kohm;\nquantity capacitance = 100 * uF;\n

    This might make the source code easier to understand, but typing those identifiers can be tricky. Sometimes, the best solution to type it might be a copy-paste approach. If we do not like this idea, we can still use old portable identifiers for those as well.

    "},{"location":"blog/2024/11/05/mp-units-240-released/#convertibility-with-quantitylike-and-quantitypointlike-entities","title":"Convertibility with QuantityLike and QuantityPointLike entities","text":"

    In this release, we decided to fine-tune further the traits that customize the conversion between custom quantity and quantity point types and the ones provided with mp-units ( breaking change ).

    Previously, to_numerical_value and from_numerical_value returned a type wrapped in a special tag type describing the conversion type (explicit or implicit).

    This was a novel and experimental approach. Finally, we decided not to do it and used a bit more verbose but a more standard solution. From now on, we need to provide two additional static data members of type bool:

    • explicit_import - true means that the conversion to the mp-units abstraction is explicit,
    • explicit_export - true means that the conversion from the mp-units abstraction is explicit.
    NowBefore
    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = true;\n  using rep = decltype(Timestamp::seconds);\n\n  static constexpr rep to_numerical_value(Timestamp ts)\n  {\n    return ts.seconds;\n  }\n\n  static constexpr Timestamp from_numerical_value(rep v)\n  {\n    return Timestamp(v);\n  }\n};\n
    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  using rep = decltype(Timestamp::seconds);\n\n  static constexpr convert_implicitly<rep> to_numerical_value(Timestamp ts)\n  {\n    return ts.seconds;\n  }\n\n  static constexpr convert_explicitly<Timestamp> from_numerical_value(rep v)\n  {\n    return Timestamp(v);\n  }\n};\n
    "},{"location":"blog/2024/11/05/mp-units-240-released/#symbolic-constants-implementation-should-be-implementation-defined","title":"Symbolic constants implementation should be implementation-defined","text":"

    In the process of writing API Reference, we decided to hide all the metadata associated with symbolic constants - tag types used to define units, dimensions, quantity specification, etc. ( breaking change ).

    All the types and values exposed by such types are needed only in the implementation details of the library. Users should not need them. Hiding those and making them implementation-defined gives other vendors the freedom to choose different ways to implement features of this library in their codebases.

    Important

    Based on Hyrum's Law some users may depend on this information already, and this release will break their code.

    If that is the case for you, we would love to hear about your use case and its rationale. It may mean that we should either:

    • extend the library's functionality to support your use case out of the box and keep those members hidden,
    • restore public visibility of such members and enforce this in the API Reference so that all the users of various library implementations may use them in the same way as you.
    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/","title":"International System of Quantities (ISQ): Part 1 - Introduction","text":"

    This post starts a series of articles about the International System of Quantities (ISQ). In this series, we will describe:

    • What is ISQ?
    • Which engineering problems does ISQ help to solve and how?
    • How to model and implement it in the programming language?
    • What is missing in the ISQ, and why is that a problem?
    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#terms-and-definitions","title":"Terms and Definitions","text":"

    From our experience, many people, including experts in the domain, often tend to name things differently, or sometimes they use the same term while having a different meaning in mind. This is why it is essential to stick to one well-defined glossary of terms for metrology.

    The mp-units project consistently uses the official metrology vocabulary defined by the ISO and BIPM:

    • International Organization for Standardization (ISO),
    • International Bureau of Weights and Measures (BIPM).

    The above are identical and contain the same set of definitions. We provide both to point out that the biggest institutions in standardizing metrology agree on the same vocabulary.

    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#systems-of-quantities-vs-systems-of-units","title":"Systems of Quantities vs Systems of Units","text":"

    Here are the official definitions from our vocabulary:

    System of quantities

    A system of quantities is a set of quantities together with a set of noncontradictory equations relating those quantities.

    System of units

    A system of units is a set of base units and derived units, together with their multiples and submultiples, defined in accordance with given rules, for a given system of quantities.

    From the definition above, we can find out that the systems of quantities and units form a hierarchy:

    flowchart TD\n    system_of_quantities[\"System of Quantities\"]\n    system_of_quantities --- system_of_units1[System of Units #1]\n    system_of_quantities --- system_of_units2[System of Units #2]\n    system_of_quantities --- system_of_units3[System of Units #3]

    System of quantities defines quantities commonly used in engineering (e.g., length, time, mass, speed, energy, power, etc.) and relations between them. It does not assign any specific units to those quantities, though.

    Systems of units are the ones that assign units of measurement to quantities from a specific system of quantities they chose to model. Different systems of units are free to chose whatever they find suitable for specific quantities and do not have to be consistent/compatible with other such systems. For example:

    • SI decided to measure length in meters, mass in kilograms, and time in seconds,
    • CGS decided to measure length in centimeters, mass in grams, and time in seconds.

    Both systems of units above agree on the unit of time, but chose different units for other quantities. In the above example, SI chose a non-prefixed unit of metre for a base quantity of length while CGS chose a scaled centimetre. On the other hand, SI chose a scaled kilogram over the gram used in the CGS. Those decisions also result in a need for different coherent units for derived quantities. For example:

    Quantity SI CGS length metre (m) centimetre (cm) mass kilogram (kg) gram (g) time second (s) second (s) force newton (N) dyne energy joule (J) erg pressure pascal (Pa) barye

    Often, there is no way to state which one is correct or which one is wrong. Each system of units has the freedom to choose whichever unit suits its engineering requirements and constraints the best for a specific quantity.

    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#isq-vs-si","title":"ISQ vs SI","text":"

    Some of the systems of quantities and units have been used more over the years and have become more popular than others. Here are the official descriptions of the most popular systems used in engineering today:

    International System of Quantities (ISQ)

    The International System of Quantities (ISQ) is a system of quantities based on the seven base quantities: length, mass, time, electric current, thermodynamic temperature, amount of substance, and luminous intensity.

    International System of Units (SI)

    The International System of Units (SI) is a system of units, based on the International System of Quantities, their names and symbols, including a series of prefixes and their names and symbols, together with rules for their use, adopted by the General Conference on Weights and Measures (CGPM).

    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#the-international-system-of-quantities-isq-standardization","title":"The International System of Quantities (ISQ) standardization","text":"

    The set of quantities constituting the ISQ is defined in the series of ISO 80000 and IEC 80000 standards under the general title \"Quantities and units\".

    ISO 80000:

    • Part 1: General
    • Part 2: Mathematical signs and symbols to be used in the natural sciences and technology
    • Part 3: Space and time
    • Part 4: Mechanics
    • Part 5: Thermodynamics
    • Part 7: Light
    • Part 8: Acoustics
    • Part 9: Physical chemistry and molecular physics
    • Part 10: Atomic and nuclear physics
    • Part 11: Characteristic numbers
    • Part 12: Condensed matter physics

    IEC 80000:

    • Part 6: Electromagnetism
    • Part 13: Information science and technology
    • Part 15: Logarithmic and related quantities, and their units
    • Part 16: Printing and writing rules
    • Part 17: Time dependency
    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will describe typical issues with libraries that do not model systems of quantities.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/","title":"International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used","text":"

    This article is the next one in our series about the ISQ. After introducing the basic terms and systems, this article will talk about the issues we face when we base the quantities and units library on just units or dimensions.

    Note

    The issues described in this article do not apply to the mp-units library. Its interfaces, even if when we decide only to use simple quantities that only use units, those are still backed up by quantity kinds under the framework's hood.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#limitations-of-units-only-solutions","title":"Limitations of units-only solutions","text":"

    Units-only is not a good design for a quantities and units library. It works to some extent, but plenty of use cases can't be addressed, and for those that somehow work, we miss important safety improvements provided by additional abstractions in this article series.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#no-way-to-specify-a-quantity-type-in-generic-interfaces","title":"No way to specify a quantity type in generic interfaces","text":"

    A common requirement in the domain is to write unit-agnostic generic interfaces. For example, let's try to implement a generic avg_speed function template that takes a quantity of any unit and produces the result. If we call it with distance in km and time in h, we will get km/h as a result, but if we call it with mi and h, we expect mi/h to be returned.

    template<Unit auto U1, typename Rep1, Unit auto U2, typename Rep2>\nauto avg_speed(quantity<U1, Rep1> distance, quantity<U2, Rep2> time)\n{\n  return distance / time;\n}\n\nquantity speed = avg_speed(120 * km, 2 * h);\n

    This function works but does not provide any type safety to the users. The function arguments can be easily reordered on the call site. Also, we do not get any information about the return type of the function or any safety measures to ensure that the function logic actually returns a quantity of speed.

    To improve safety, with a units-only library, we have to write the function in the following way:

    template<typename Rep1, typename Rep2>\nquantity<si::metre / si::second, decltype(Rep1{} / Rep2{})> avg_speed(quantity<si::metre, Rep1> distance,\n                                                                      quantity<si::second, Rep2> time)\n{\n  return distance / time;\n}\n\navg_speed(120 * km, 2 * h).in(km / h);\n

    Despite being safer, the above code decreased the performance because we always pay for the conversion at the function's input and output.

    Moreover, in a good library, the above code should not compile. The reason for this is that even though the conversion from km to m and from h to s is considered value-preserving, it is not true in the opposite direction. When we try to convert the result stored in an integral type from the unit of m/s to km/h, we will inevitably lose some data.

    We could try to provide concepts like ScaledUnitOf<si::metre> that would take a set of units while trying to constrain them somehow, but it leads to even more problems with the unit definitions. For example, are Hz and Bq just scaled versions of 1/s? If we constrain the interface to just prefixed units, then litre and a cubic metre or kilometre and mile will be incompatible. What about radian and steradian or a litre per 100 kilometre (popular unit of a fuel consumption) and a squared metre? Should those be compatible?

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#disjoint-units-of-the-same-quantity-type-do-not-work","title":"Disjoint units of the same quantity type do not work","text":"

    Sometimes, we need to define several units describing the same quantity but which should not convert to each other in the library's framework. A typical example here is currency. A user may want to define EURO and USD as units of currency, so both of them can be used for such quantities. However, it is impossible to predefine one fixed conversion factor for those, as a currency exchange rate varies over time, and the library's framework can't provide such an information as an input to the built-in conversion function. User's application may have more information in this domain and handle such a conversion at runtime with custom logic (e.g., using an additional time point function argument). If we would like to model that in a unit-only solution, how can we specify that EURO and USD are units of quantities of currency, but are not convertible to each other?

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#dimensions-to-the-rescue","title":"Dimensions to the rescue?","text":"

    To resolve the above issues, most of the libraries on the market introduce dimension abstraction. Thanks to that, we could solve the first issue of the previous chapter with:

    QuantityOf<dim_speed> auto avg_speed(QuantityOf<dim_length> auto distance,\n                                     QuantityOf<dim_time> auto time)\n{\n  return distance / time;\n}\n

    and the second one by specifying that both EURO and USD are units of dim_currency. This is a significant improvement but still has some issues.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#limitations-of-dimensions","title":"Limitations of dimensions","text":"

    Let's first look at the above solution again. A domain expert seeing this code will immediately say there is no such thing as a speed dimension. The ISQ specifies only 7 dimensions with unique symbols assigned, and the dimensions of all the ISQ quantities are created as a vector product of those. For example, a quantity of speed has a dimension of \\(L^1T^{-1}\\). So, to be physically correct, the above code should be rewritten as:

    QuantityOf<dim_length / dim_time> auto avg_speed(QuantityOf<dim_length> auto distance,\n                                                 QuantityOf<dim_time> auto time)\n{\n  return distance / time;\n}\n

    Most of the libraries on the market ignore this fact and try to model distinct quantities through their dimensions, giving a false sense of safety. A dimension is not enough to describe a quantity. This has been known for a long time now. The \"Measurement Data (Archive Report)\" report from 1996 says explicitly:

    Measurement Data (Archive Report)

    Dimensional analysis does not adequately model the semantics of measurement data.

    In the following chapters, we will see a few use cases that can't be solved with an approach that only relies on units or dimensions.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#si-units-of-quantities-of-the-same-dimension-but-different-kinds","title":"SI units of quantities of the same dimension but different kinds","text":"

    The SI provides several units for distinct quantities of the same dimension but different kinds. For example:

    • hertz (Hz) is a unit of frequency and becquerel (Bq) is a unit of activity. Both are defined as \\(s^{-1}\\), and have the same dimension of \\(T^{-1}\\).
    • gray (Gy) is a unit of absorbed dose and sievert (Sv) is a unit of dose equivalent. Both are defined as \\(m^2 s^{-2}\\), and have the same dimension of \\(L^2T^{-2}\\)
    • radian (rad) is a unit of plane angle defined as \\(m/m\\), and steradian (sr) is a unit of solid angle defined as \\(m^2/m^2\\). Both are quantities of dimension one, which also has its own units like one (1) and percent (%).

    There are many more similar examples in the ISO/IEC 80000 series. For example, storage capacity quantity can be measured in units of one, bit, octet, and byte.

    The above conflicts can't be solved with dimensions, and they yield many safety issues. For example, we can ask ourselves what should be the result of the following:

    1. quantity q = 1 * Hz + 1 * Bq;
    2. quantity<Gy> q = 42 * Sv;
    3. bool b = (1 * rad + 1 * bit) == 2 * sr;

    None of the above code should compile, but most of the libraries on the market happily accept it and provide meaningless results. Some of them decide not to define one or more of the above units at all to avoid potential safety issues. For example, the Au library does not define Sv to avoid mixing it up with Gy.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#derived-quantities-of-the-same-dimension-but-different-kinds","title":"Derived quantities of the same dimension but different kinds","text":"

    Even if some quantities do not have a specially assigned unit, they may still have a totally different physical meaning even if they share the same dimension:

    • work vs. moment of force both of the same dimension \\(L^2MT^{-2}\\),
    • fuel consumption expressed in \\(\\frac{l}{100\\;km}\\) vs. area expressed in \\(m^2\\) both of the same dimension \\(L^2\\).

    Again, we don't want to accidentally mix those.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#various-quantities-of-the-same-dimension-and-kinds","title":"Various quantities of the same dimension and kinds","text":"

    Even if we somehow address all the above, there are plenty of use cases that still can't be safely implemented with such abstractions.

    Let's consider that we want to implement a freight transport application to position cargo in the container. In majority of the products on the market we will end up with something like:

    class Box {\n  length length_;\n  length width_;\n  length height_;\npublic:\n  Box(length l, length w, length h): length_(l), width_(w), height_(h) {}\n  area floor() const { return length_ * width_; }\n  // ...\n};\n
    Box my_box(2 * m, 3 * m, 1 * m);\n

    Such interfaces are not much safer than just using plain fundamental types (e.g., double). One of the main reasons of using a quantities and units library was to introduce strong-type interfaces to prevent such issues. In this scenario, we need to be able to discriminate between length, width, and height of the package.

    A similar but also really important use case is in aviation. The current altitude is a totally different quantity than the distance to the destination. The same is true for forward speed and sink rate. We do not want to accidentally mix those.

    When we deal with energy, we should be able to implicitly construct it from a proper product of any mass, length, and time. However, when we want to calculate gravitational potential energy, we may not want it to be implicitly initialized from any expression of matching dimensions. Such an implicit construction should be allowed only if we multiply a mass with acceleration of free fall and height. All other conversions should have an explicit annotation to make it clear that something potentially unsafe is being done in the code. Also, we should not be able to assign a potential energy to a quantity of kinetic energy. However, both of them (possibly accumulated with each other) should be convertible to a mechanical energy quantity.

    mass m = 1 * kg;\nlength l = 1 * m;\ntime t = 1 * s;\nacceleration_of_free_fall g = 9.81 * m / s2;\nheight h = 1 * m;\nspeed v = 1 * m / s;\nenergy e = m * pow<2>(l) / pow<2>(t);                     // OK\npotential_energy ep1 = e;                                 // should not compile\npotential_energy ep2 = static_cast<potential_energy>(e);  // OK\npotential_energy ep3 = m * g * h;                         // OK\nkinetic_energy ek1 = m * pow<2>(v) / 2;                   // OK\nkinetic_energy ek2 = ep3 + ek1;                           // should not compile\nmechanical_energy me = ep3 + ek1;                         // OK\n

    Yet another example comes from the audio industry. In the audio software, we want to treat specific counts (e.g., beats, samples) as separate quantities. We could assign dedicated base dimensions to them. However, if we divide them by duration, we should obtain a quantity convertible to frequency and even be able to express the result in a unit of Hz. With the dedicated dimensions approach, this wouldn't work as the dimension of frequency is just \\(T^{-1}\\), which would not match the results of our dimensional equations. This is why we can't assign dedicated dimensions to such counts.

    The last example that we want to mention here comes from finance. This time, we need to model currency volume as a special quantity of currency. currency volume can be obtained by multiplying currency by the dimensionless market quantity. Of course, both currency and currency volume should be expressed in the same units (e.g., USD).

    None of the above scenarios can be addressed with just units and dimensions. We need a better abstraction to safely implement them.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will introduce the main ideas behind the International System of Quantities and describe how we can model it in the programming language.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/","title":"International System of Quantities (ISQ): Part 3 - Modeling ISQ","text":"

    The physical units libraries on the market typically only focus on modeling one or more systems of units. However, as we have learned, this is not the only system kind to model. Another, and maybe even more important, is a system of quantities. The most important example here is the International System of Quantities (ISQ) defined by ISO/IEC 80000.

    This article continues our series about the International System of Quantities. This time, we will learn about the main ideas behind the ISQ and describe how it can be modelled in a programming language.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#dimension-is-not-enough-to-describe-a-quantity","title":"Dimension is not enough to describe a quantity","text":"

    Most of the products on the market are aware of physical dimensions. However, a dimension is not enough to describe a quantity. Let's repeat briefly some of the problems described in more detail in the previous article. For example, let's see the following implementation:

    class Box {\n  area base_;\n  length height_;\npublic:\n  Box(length l, length w, length h) : base_(l * w), height_(h) {}\n  // ...\n};\n\nBox my_box(2 * m, 3 * m, 1 * m);\n

    How do you like such an interface? It turns out that in most existing strongly-typed libraries this is often the best we can do.

    Another typical question many users ask is how to deal with work and torque. Both of those have the same dimension but are distinct quantities.

    A similar issue is related to figuring out what should be the result of:

    auto res = 1 * Hz + 1 * Bq + 1 * Bd;\n

    where:

    • Hz (hertz) - unit of frequency,
    • Bq (becquerel) - unit of activity,
    • Bd (baud) - unit of modulation rate.

    All of those quantities have the same dimension, namely \\(\\mathsf{T}^{-1}\\), but probably it is not wise to allow adding, subtracting, or comparing them, as they describe vastly different physical properties.

    If the above example seems too abstract, let's consider Gy (gray - unit of absorbed dose) and Sv (sievert - unit of dose equivalent), or radian and steradian. All of those quantities have the same dimensions.

    Another example here is fuel consumption (fuel volume divided by distance, e.g., 6.7 l/100km) and an area. Again, both have the same dimension \\(\\mathsf{L}^{2}\\), but probably it wouldn't be wise to allow adding, subtracting, or comparing a fuel consumption of a car and the area of a football field. Such an operation does not have any physical sense and should fail to compile.

    It turns out that the above issues can't be solved correctly without proper modeling of a system of quantities.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#quantities-of-the-same-kind","title":"Quantities of the same kind","text":"

    As it was described in the previous article, dimension is not enough to describe a quantity. We need a better abstraction to ensure the safety of our calculations. It turns out that ISO/IEC 80000 comes with the answer:

    ISO 80000-1:2009

    • Quantities may be grouped together into categories of quantities that are mutually comparable.
    • Mutually comparable quantities are called quantities of the same kind.
    • Two or more quantities cannot be added or subtracted unless they belong to the same category of mutually comparable quantities.
    • Quantities of the same kind within a given system of quantities have the same quantity dimension.
    • Quantities of the same dimension are not necessarily of the same kind.

    ISO Guide also explicitly states:

    ISO Guide

    Measurement units of quantities of the same quantity dimension may be designated by the same name and symbol even when the quantities are not of the same kind. For example, joule per kelvin and J/K are respectively the name and symbol of both a measurement unit of heat capacity and a measurement unit of entropy, which are generally not considered to be quantities of the same kind. However, in some cases special measurement unit names are restricted to be used with quantities of specific kind only. For example, the measurement unit \u2018second to the power minus one\u2019 (1/s) is called hertz (Hz) when used for frequencies and becquerel (Bq) when used for activities of radionuclides. As another example, the joule (J) is used as a unit of energy, but never as a unit of moment of force, i.e. the newton metre (N \u00b7 m).

    The above quotes from ISO provide answers to all the issues mentioned above and in the previous article.

    More than one quantity may be defined for the same dimension:

    • quantities of different kinds (e.g., frequency, modulation rate, activity).
    • quantities of the same kind (e.g., length, width, altitude, distance, radius, wavelength, position vector).

    Two quantities can't be added, subtracted, or compared unless they belong to the same kind. As frequency, activity, and modulation rate are of different kinds, the expression provided above should not compile.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#system-of-quantities-is-not-only-about-kinds","title":"System of quantities is not only about kinds","text":"

    ISO/IEC 80000 specifies hundreds of different quantities. Plenty of various kinds are provided, and often, each kind contains more than one quantity. It turns out that such quantities form a hierarchy of quantities of the same kind.

    For example, here are all quantities of the kind length provided in the ISO 80000-3:

    flowchart TD\n    length[\"<b>length</b><br>[m]\"]\n    length --- width[\"<b>width</b> / <b>breadth</b>\"]\n    length --- height[\"<b>height</b> / <b>depth</b> / <b>altitude</b>\"]\n    width --- thickness[\"<b>thickness</b>\"]\n    width --- diameter[\"<b>diameter</b>\"]\n    width --- radius[\"<b>radius</b>\"]\n    length --- path_length[\"<b>path_length</b>\"]\n    path_length --- distance[\"<b>distance</b>\"]\n    distance --- radial_distance[\"<b>radial_distance</b>\"]\n    length --- wavelength[\"<b>wavelength</b>\"]\n    length --- displacement[\"<b>displacement</b><br>{vector}\"]\n    displacement --- position_vector[\"<b>position_vector</b>\"]\n    radius --- radius_of_curvature[\"<b>radius_of_curvature</b>\"]

    Each of the above quantities expresses some kind of length, and each can be measured with meters, which is the unit defined by the SI for quantities of length. However, each has different properties, usage, and sometimes even a different character (position vector and displacement are vector quantities).

    Forming such a hierarchy helps us define arithmetics and conversion rules for various quantities of the same kind.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#converting-between-quantities-of-the-same-kind","title":"Converting between quantities of the same kind","text":"

    Based on the hierarchy above, we can define the following quantity conversion rules:

    1. Implicit conversions

      • Every width is a length.
      • Every radius is a width.
      static_assert(implicitly_convertible(isq::width, isq::length));\nstatic_assert(implicitly_convertible(isq::radius, isq::length));\nstatic_assert(implicitly_convertible(isq::radius, isq::width));\n

      Implicit conversions are allowed on copy-initialization:

      void foo(quantity<isq::length[m]> q);\n
      quantity<isq::width[m]> q1 = 42 * m;\nquantity<isq::length[m]> q2 = q1;  // implicit quantity conversion\nfoo(q1);                           // implicit quantity conversion\n
    2. Explicit conversions

      • Not every length is a width.
      • Not every width is a radius.
      static_assert(!implicitly_convertible(isq::length, isq::width));\nstatic_assert(!implicitly_convertible(isq::length, isq::radius));\nstatic_assert(!implicitly_convertible(isq::width, isq::radius));\nstatic_assert(explicitly_convertible(isq::length, isq::width));\nstatic_assert(explicitly_convertible(isq::length, isq::radius));\nstatic_assert(explicitly_convertible(isq::width, isq::radius));\n

      Explicit conversions are forced by passing the quantity to a call operator of a quantity_spec type:

      void foo(quantity<isq::height[m]> q);\n
      quantity<isq::length[m]> q1 = 42 * m;\nquantity<isq::height[m]> q2 = isq::height(q1);  // explicit quantity conversion\nfoo(isq::height(q1));                           // explicit quantity conversion\n
    3. Explicit casts

      • height is never a width, and vice versa.
      • Both height and width are quantities of kind length.
      static_assert(!implicitly_convertible(isq::height, isq::width));\nstatic_assert(!explicitly_convertible(isq::height, isq::width));\nstatic_assert(castable(isq::height, isq::width));\n

      Explicit casts are forced with a dedicated quantity_cast function:

      void foo(quantity<isq::height[m]> q);\n
      quantity<isq::width[m]> q1 = 42 * m;\nquantity<isq::height[m]> q2 = quantity_cast<isq::height>(q1);  // explicit quantity cast\nfoo(quantity_cast<isq::height>(q1));                           // explicit quantity cast\n
    4. No conversion

      • time has nothing in common with length.
      static_assert(!implicitly_convertible(isq::time, isq::length));\nstatic_assert(!explicitly_convertible(isq::time, isq::length));\nstatic_assert(!castable(isq::time, isq::length));\n

      Even the explicit casts will not force such a conversion:

      void foo(quantity<isq::length[m]>);\n
      quantity<isq::length[m]> q1 = 42 * s;    // Compile-time error\nfoo(quantity_cast<isq::length>(42 * s)); // Compile-time error\n
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#comparing-adding-and-subtracting-quantities-of-the-same-kind","title":"Comparing, adding, and subtracting quantities of the same kind","text":"

    ISO/IEC 80000 explicitly states that width and height are quantities of the same kind, and as such they:

    • are mutually comparable,
    • can be added and subtracted.

    This means that we should be allowed to compare any quantities from the same tree (as long as their underlying representation types are comparable):

    static_assert(isq::radius(1 * m) == isq::height(1 * m));\n

    Also, based on our hierarchy above, the only reasonable result of 1 * width + 1 * height is 2 * length, where the result of length is known as a common quantity type. A result of such an equation is always the first common node in a hierarchy tree of the same kind. For example:

    static_assert((isq::width(1 * m) + isq::height(1 * m)).quantity_spec == isq::length);\nstatic_assert((isq::thickness(1 * m) + isq::radius(1 * m)).quantity_spec == isq::width);\nstatic_assert((isq::distance(1 * m) + isq::path_length(1 * m)).quantity_spec == isq::path_length);\n
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#modeling-a-quantity-kind","title":"Modeling a quantity kind","text":"

    In the quantities and units library, we also need an abstraction describing an entire family of quantities of the same kind. Such quantities have not only the same dimension but also can be expressed in the same units.

    To annotate a quantity to represent its kind (and not just a hierarchy tree's root quantity) we introduced a kind_of<> specifier. For example, to express any quantity of length, we need to type kind_of<isq::length>.

    Important

    isq::length and kind_of<isq::length> are two different things.

    Such an entity behaves as any quantity of its kind. This means that it is implicitly convertible to any quantity in a tree.

    static_assert(!implicitly_convertible(isq::length, isq::height));\nstatic_assert(implicitly_convertible(kind_of<isq::length>, isq::height));\n

    Additionally, the result of operations on quantity kinds is also a quantity kind:

    static_assert(same_type<kind_of<isq::length> / kind_of<isq::time>, kind_of<isq::length / isq::time>>);\n

    However, if at least one equation's operand is not a quantity kind, the result becomes a \"strong\" quantity where all the kinds are converted to the hierarchy tree's root quantities:

    static_assert(!same_type<kind_of<isq::length> / isq::time, kind_of<isq::length / isq::time>>);\nstatic_assert(same_type<kind_of<isq::length> / isq::time, isq::length / isq::time>);\n

    Info

    Only a root quantity from the hierarchy tree or the one marked with is_kind specifier in the quantity_spec definition can be put as a template parameter to the kind_of specifier. For example, kind_of<isq::width> will fail to compile. However, we can call get_kind(q) to obtain a kind of any quantity:

    static_assert(get_kind(isq::width) == kind_of<isq::length>);\n
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#how-do-systems-of-units-benefit-from-the-isq-and-quantity-kinds","title":"How do systems of units benefit from the ISQ and quantity kinds?","text":"

    Modeling a system of units is the most essential feature and a selling point of every physical units library. Thanks to that, the library can protect users from assigning, adding, subtracting, or comparing incompatible units and provide automated conversion factors between various compatible units.

    Probably all the libraries in the wild model the SI (or at least most of it), and many of them provide support for additional units belonging to various other systems (e.g., imperial).

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#systems-of-units-are-based-on-systems-of-quantities","title":"Systems of units are based on systems of quantities","text":"

    Systems of quantities specify a set of quantities and equations relating to those quantities. Those equations do not take any unit or a numerical representation into account at all. In order to create a quantity, we need to add those missing pieces of information. This is where a system of units kicks in.

    The SI is explicitly stated to be based on the ISQ. Among others, it defines seven base units, one for each base quantity of ISQ. In the library, this is expressed by associating a quantity kind to a unit being defined:

    inline constexpr struct metre final : named_unit<\"m\", kind_of<isq::length>> {} metre;\n

    The kind_of<isq::length> above states explicitly that this unit has an associated quantity kind. In other words, si::metre (and scaled units based on it) can be used to express the amount of any quantity of kind length.

    Note

    For some systems of units (e.g., natural units), a unit may not have an associated quantity type. For example, if we define the speed of light constant as c = 1, we can define a system where both length and time will be measured in seconds, and speed will be a quantity measured with the unit one. In such case, the definition will look as follows:

    inline constexpr struct second final : named_unit<\"s\"> {} second;\n
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#constraining-a-derived-unit-to-work-only-with-a-specific-derived-quantity","title":"Constraining a derived unit to work only with a specific derived quantity","text":"

    Some derived units are valid only for specific derived quantities. For example, SI specifies both hertz and becquerel derived units with the same unit equation \\(s^{-1}\\). However, it also explicitly states:

    SI

    The hertz shall only be used for periodic phenomena and the becquerel shall only be used for stochastic processes in activity referred to a radionuclide.

    This is why it is important for the library to allow constraining such units to be used only with a specific quantity kind:

    inline constexpr struct hertz final : named_unit<\"Hz\", one / second, kind_of<isq::frequency>> {} hertz;\ninline constexpr struct becquerel final : named_unit<\"Bq\", one / second, kind_of<isq::activity>> {} becquerel;\n

    With the above, hertz can only be used for frequencies, while becquerel should only be used for quantities of activity:

    quantity<isq::frequency[Hz]> q1 = 60 * Bq;   // Compile-time error\nquantity<isq::activity[Hz]> q2;              // Compile-time error\nquantity<isq::frequency[Hz]> q3 = 60 * Hz;   // OK\nstd::cout << q3.in(Bq) << \"\\n\";              // Compile-time error\n

    We know already that quantities of different kinds can't be compared, added, and subtracted. The following equation will not compile thanks to constraining derived units to be valid for specific kinds only:

    auto q = 1 * Hz + 1 * Bq;   // Fails to compile\n

    All of the above features improve the safety of our library and the products that are using it.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will present how we can implement our ISQ model in a C++ programming language and we will point out some of the first issues that stand in our way.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/","title":"International System of Quantities (ISQ): Part 4 - Implementing ISQ","text":"

    Up until now, we have introduced the International System of Quantities and described how we can model its main aspects. This article will present how to implement those models in a programming language, and we will point out some of the first issues that stand in our way.

    In the previous article, we have already introduced a notion of quantity kind, provided kind_of<> specifier, and described how it helps in the modeling of the system of units (e.g., SI).

    Now, it is time to see how we can implement hierarchies of quantities of the same kind.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#modeling-a-hierarchy-of-kind-length","title":"Modeling a hierarchy of kind length","text":"

    First, let's start with something easy - hierarchy of kind length. ISO 80000-3 does a good job of describing all relations between quantities in this case.

    We've seen this tree already:

    flowchart TD\n    length[\"<b>length</b><br>[m]\"]\n    length --- width[\"<b>width</b> / <b>breadth</b>\"]\n    length --- height[\"<b>height</b> / <b>depth</b> / <b>altitude</b>\"]\n    width --- thickness[\"<b>thickness</b>\"]\n    width --- diameter[\"<b>diameter</b>\"]\n    width --- radius[\"<b>radius</b>\"]\n    length --- path_length[\"<b>path_length</b>\"]\n    path_length --- distance[\"<b>distance</b>\"]\n    distance --- radial_distance[\"<b>radial_distance</b>\"]\n    length --- wavelength[\"<b>wavelength</b>\"]\n    length --- displacement[\"<b>displacement</b><br>{vector}\"]\n    displacement --- position_vector[\"<b>position_vector</b>\"]\n    radius --- radius_of_curvature[\"<b>radius_of_curvature</b>\"]

    This is how we can model it in C++:

    inline constexpr struct dim_length final          : base_dimension<\"L\"> {} dim_length;\n\ninline constexpr struct length final              : quantity_spec<dim_length> {} length;\ninline constexpr struct width final               : quantity_spec<length> {} width;\ninline constexpr auto breadth = width;\ninline constexpr struct height final              : quantity_spec<length> {} height;\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\ninline constexpr struct thickness final           : quantity_spec<width> {} thickness;\ninline constexpr struct diameter final            : quantity_spec<width> {} diameter;\ninline constexpr struct radius final              : quantity_spec<width> {} radius;\ninline constexpr struct radius_of_curvature final : quantity_spec<radius> {} radius_of_curvature;\ninline constexpr struct path_length final         : quantity_spec<length> {} path_length;\ninline constexpr auto arc_length = path_length;\ninline constexpr struct distance final            : quantity_spec<path_length> {} distance;\ninline constexpr struct radial_distance final     : quantity_spec<distance> {} radial_distance;\ninline constexpr struct wavelength final          : quantity_spec<length> {} wavelength;\ninline constexpr struct displacement final        : quantity_spec<length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final     : quantity_spec<displacement> {} position_vector;\n

    Thanks to the expressivity and power of C++ templates, we can specify all quantity properties in one line of code. In the above code:

    • length takes the base dimension to indicate that we are creating a base quantity that will serve as a root for a tree of quantities of the same kind,
    • width and following quantities are branches and leaves of this tree with the parent always provided as the first argument to quantity_spec class template,
    • breadth is an alias name for the same quantity as width.

    Note

    Some quantities may be specified to have complex, vector, or tensor character (e.g., displacement). The quantity character can be set with the last parameter of quantity_spec.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#modeling-a-hierarchy-of-kind-energy","title":"Modeling a hierarchy of kind energy","text":"

    Base quantities are simple. It is more complicated when we start modeling derived quantities. Let's try to model the hierarchy for energy.

    When we look into the ISO/IEC 80000 standards, this task immediately stops being as easy as the previous one. Derived quantity equations often do not automatically form a hierarchy tree, and ISO/IEC standards do not provide a clear answer to inter-quantity dependencies. This is why it is often not obvious what such a tree should look like.

    Even more, ISO explicitly states:

    ISO/IEC Guide 99

    The division of \u2018quantity\u2019 according to \u2018kind of quantity\u2019 is, to some extent, arbitrary.

    Let's try anyway. The below presents some arbitrary hierarchy of derived quantities of kind energy:

    flowchart TD\n    energy[\"<b>energy</b><br><i>(mass * length<sup>2</sup> / time<sup>2</sup>)</i><br>[J]\"]\n    energy --- mechanical_energy[\"<b>mechanical_energy</b>\"]\n    mechanical_energy --- potential_energy[\"<b>potential_energy</b>\"]\n    potential_energy --- gravitational_potential_energy[\"<b>gravitational_potential_energy</b><br><i>(mass * acceleration_of_free_fall * height)</i>\"]\n    potential_energy --- elastic_potential_energy[\"<b>elastic_potential_energy</b><br><i>(spring_constant * amount_of_compression<sup>2</sup>)</i>\"]\n    mechanical_energy --- kinetic_energy[\"<b>kinetic_energy</b><br><i>(mass * speed<sup>2</sup>)</i>\"]\n    energy --- enthalpy[\"<b>enthalpy</b>\"]\n    enthalpy --- internal_energy[\"<b>internal_energy</b> / <b>thermodynamic_energy</b>\"]\n    internal_energy --- Helmholtz_energy[\"<b>Helmholtz_energy</b> / <b>Helmholtz_function</b>\"]\n    enthalpy --- Gibbs_energy[\"<b>Gibbs_energy</b> / <b>Gibbs_function</b>\"]\n    energy --- active_energy[\"<b>active_energy</b>\"]

    As we can see above, besides what we've already seen for length hierarchy, derived quantities may provide specific recipes that can be used to create them implicitly:

    • energy is the most generic one and thus can be created from base quantities of mass, length, and time. As those are also the roots of quantities of their kinds and all other quantities from their trees are implicitly convertible to them, it means that an energy can be implicitly constructed from any quantity of mass, length, and time:

      static_assert(implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time), isq::energy));\nstatic_assert(implicitly_convertible(isq::mass * pow<2>(isq::height) / pow<2>(isq::time), isq::energy));\n
    • mechanical energy is a more \"specialized\" quantity than energy (not every energy is a mechanical energy). It is why an explicit cast is needed to convert from either energy or the results of its quantity equation:

      static_assert(!implicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                      isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                     isq::mechanical_energy));\n
    • gravitational potential energy is not only even more specialized one but additionally, it is special in a way that it provides its own \"constrained\" quantity equation. Maybe not every mass * pow<2>(length) / pow<2>(time) is a gravitational potential energy, but every mass * acceleration_of_free_fall * height is.

      static_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                      gravitational_potential_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                     gravitational_potential_energy));\nstatic_assert(implicitly_convertible(isq::mass * isq::acceleration_of_free_fall * isq::height,\n                                     gravitational_potential_energy));\n

    And here is the C++ code for it:

    inline constexpr struct energy final                         : quantity_spec<mass* pow<2>(length) / pow<2>(time)> {} energy;\ninline constexpr struct mechanical_energy final              : quantity_spec<energy> {} mechanical_energy;                                                            // differs from ISO 80000\ninline constexpr struct potential_energy final               : quantity_spec<mechanical_energy> {} potential_energy;                                                  // differs from ISO 80000\ninline constexpr struct gravitational_potential_energy final : quantity_spec<potential_energy, mass * acceleration_of_free_fall * height> {} potential_energy;        // not in ISO 80000\ninline constexpr struct elastic_potential_energy final       : quantity_spec<potential_energy, spring_constant * pow<2>(amount_of_compression)> {} potential_energy;  // not in ISO 80000\ninline constexpr struct kinetic_energy final                 : quantity_spec<mechanical_energy, mass* pow<2>(speed)> {} kinetic_energy;                               // differs from ISO 80000\ninline constexpr struct enthalpy final                       : quantity_spec<energy> {} enthalpy;                                                                     // differs from ISO 80000\ninline constexpr struct internal_energy final                : quantity_spec<enthalpy> {} internal_energy;                                                            // differs from ISO 80000\ninline constexpr auto thermodynamic_energy = internal_energy;\ninline constexpr struct Helmholtz_energy final               : quantity_spec<internal_energy> {} Helmholtz_energy;\ninline constexpr auto Helmholtz_function = Helmholtz_energy;\ninline constexpr struct Gibbs_energy final                   : quantity_spec<enthalpy> {} Gibbs_energy;\ninline constexpr auto Gibbs_function = Gibbs_energy;\n

    Again, the first parameter of quantity_spec determines the position in the tree. If a second argument is provided, it denotes a recipe for this quantity.

    With the above simple definitions we've automatically addressed our energy-related issues from the Various quantities of the same dimension and kinds chapter of the \"Part 2\" article.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#modeling-a-hierarchy-of-kind-dimensionless","title":"Modeling a hierarchy of kind dimensionless","text":"

    As the last example for this article, let's try to model and implement quantities of dimension one, often also called dimensionless quantities. This quantity hierarchy contains more than one quantity kind and more than one unit in its tree:

    flowchart TD\n    dimensionless[\"<b>dimensionless</b><br>[one]\"]\n    dimensionless --- rotation[\"<b>rotation</b>\"]\n    dimensionless --- thermodynamic_efficiency[\"<b>thermodynamic_efficiency</b><br><i>(work / heat)</i>\"]\n    dimensionless --- angular_measure[\"<b>angular_measure</b><br><i>(arc_length / radius)</i><br>[rad]\"]\n    angular_measure --- rotational_displacement[\"<b>rotational_displacement</b><br><i>(path_length / radius)</i>\"]\n    angular_measure --- phase_angle[\"<b>phase_angle</b>\"]\n    dimensionless --- solid_angular_measure[\"<b>solid_angular_measure</b><br><i>(area / pow<2>(radius))</i><br>[sr]\"]\n    dimensionless --- drag_factor[\"<b>drag_factor</b><br><i>(drag_force / (mass_density * pow<2>(speed) * area))</i>\"]\n    dimensionless --- storage_capacity[\"<b>storage_capacity</b><br>[bit]\"] --- equivalent_binary_storage_capacity[\"<b>equivalent_binary_storage_capacity</b>\"]\n    dimensionless --- ...

    To enable such support in the library, we provided an is_kind specifier that can be appended to the quantity specification:

    inline constexpr struct dimensionless final            : quantity_spec<detail::derived_quantity_spec<>{}> {} dimensionless;\ninline constexpr struct rotation final                 : quantity_spec<dimensionless> {} rotation;\ninline constexpr struct thermodynamic_efficiency final : quantity_spec<dimensionless, work / heat> {} efficiency;\ninline constexpr struct angular_measure final          : quantity_spec<dimensionless, arc_length / radius, is_kind> {} angular_measure;\ninline constexpr struct rotational_displacement final  : quantity_spec<angular_measure, path_length / radius> {} rotational_displacement;\ninline constexpr struct phase_angle final              : quantity_spec<angular_measure> {} phase_angle;\ninline constexpr struct solid_angular_measure final    : quantity_spec<dimensionless, area / pow<2>(radius), is_kind> {} solid_angular_measure;\ninline constexpr struct drag_factor final              : quantity_spec<dimensionless, drag_force / (mass_density * pow<2>(speed) * area)> {} drag_factor;\ninline constexpr struct storage_capacity final         : quantity_spec<dimensionless, is_kind> {} storage_capacity;\n

    With the above, we can constrain radian, steradian, and bit to be allowed for usage with specific quantity kinds only:

    inline constexpr struct radian final    : named_unit<\"rad\", metre / metre, kind_of<isq::angular_measure>> {} radian;\ninline constexpr struct steradian final : named_unit<\"sr\", square(metre) / square(metre), kind_of<isq::solid_angular_measure>> {} steradian;\ninline constexpr struct bit final       : named_unit<\"bit\", one, kind_of<storage_capacity>> {} bit;\n

    but still allow the usage of one and its scaled versions for such quantities.

    Note

    dimensionless is a special quantity which serves as an identity element in quantity equations. It is predefined in the library's framework and there is no way for the user to define it or something similar to it.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will present how our ISQ model helps to address the remaining issues described in the Part 2 of our series.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/","title":"International System of Quantities (ISQ): Part 5 - Benefits","text":"

    In the previous articles, we introduced the International System of Quantities, described how we can model and implement it in a programming language, and presented the issues of software that does not use such abstraction to implement a units library.

    Some of the issues raised in Part 2 of our series were addressed in Part 3 already. This article will present how our ISQ model elegantly addresses the remaining problems.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#generic-but-safe-interfaces","title":"Generic but safe interfaces","text":"

    Let's start with the implementation of a generic utility function that would calculate the average speed based on provided arguments. The resulting quantity should use a derived unit of the provided arguments (e.g., km/h for km and h, m/s for m and s, ...).

    With C++ concepts backed up with ISQ quantities, we can simply type it as:

    constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d,\n                                                QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n

    The above constrains the algorithm to proper quantity types and ensures that a quantity of speed is returned. The latter is essential not only for the users to better understand what the function does but also serves as a unit test for our implementation. It ensures that our quantity equations are correct in the implementation part of the function, and we indeed return a quantity of speed.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#non-convertible-units-of-currency","title":"Non-convertible units of currency","text":"

    Our second example was about disjoint units of currency. We want to use various units of currency but we can't provide compile-time known conversion factors between those as such ratios are only known at runtime.

    First, we define:

    • a new dimension for currency and a quantity type based on it,
    • set of disjoint units of currency for its quantity kind.
    inline constexpr struct dim_currency final : base_dimension<\"$\"> {} dim_currency;\ninline constexpr struct currency final : quantity_spec<dim_currency> {} currency;\n\ninline constexpr struct euro final : named_unit<\"EUR\", kind_of<currency>> {} euro;\ninline constexpr struct us_dollar final : named_unit<\"USD\", kind_of<currency>> {} us_dollar;\n\nnamespace unit_symbols {\n\ninline constexpr auto EUR = euro;\ninline constexpr auto USD = us_dollar;\n\n}\n\nstatic_assert(!std::equality_comparable_with<quantity<euro, int>, quantity<us_dollar, int>>);\n

    Next, we can provide a custom currency exchange facility that accounts for a specific point in time:

    template<Unit auto From, Unit auto To>\n[[nodiscard]] double exchange_rate(std::chrono::sys_seconds timestamp)\n{\n  // user-provided logic...\n}\n\ntemplate<UnitOf<currency> auto To, QuantityOf<currency> From>\nQuantityOf<currency> auto exchange_to(From q, std::chrono::sys_seconds timestamp)\n{\n  const auto rate =\n    static_cast<From::rep>(exchange_rate<From::unit, To>(timestamp) * q.numerical_value_in(q.unit));\n  return rate * From::quantity_spec[To];\n}\n

    Finally, we can use our simple model in the following way:

    using namespace unit_symbols;\nusing namespace std::chrono;\n\nconst auto yesterday = time_point_cast<seconds>(system_clock::now() - hours{24});\nconst quantity price_usd = 100 * USD;\nconst quantity price_euro = exchange_to<euro>(price_usd, yesterday);\n\nstd::cout << price_usd << \" -> \" << price_euro << \"\\n\";\n// std::cout << price_usd + price_euro << \"\\n\";  // does not compile\n

    Note

    It would be better to model the above prices as quantity points, but this is a subject for a different article .

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#derived-quantities-of-the-same-dimension-but-different-kinds","title":"Derived quantities of the same dimension but different kinds","text":"

    Until now, the issues discussed have not actually required modeling of the ISQ. The introduction of physical dimensions would be enough, and indeed, this is what most of the libraries on the market do. However, we have more exciting challenges to solve as well.

    The next issue was related to different quantities having the same dimension. In many cases, we want to prevent conversions and any other compatibility between such distinct quantities.

    Let's try to implement our fuel consumption example. First, we define the quantity type and a handy identifier for a derived unit that we want to use:

    inline constexpr struct fuel_consumption final : quantity_spec<isq::volume / isq::length> {} fuel_consumption;\ninline constexpr auto L_per_100km = si::litre / (mag<100> * si::kilo<si::metre>);\n\nstatic_assert(fuel_consumption != isq::area);\nstatic_assert(fuel_consumption.dimension == isq::area.dimension);\n

    Next, we define two quantities. The first one is based only on a derived unit of L/[100 km], while the second uses a strongly typed quantity type:

    quantity q1 = 5.8 * L_per_100km;\nquantity q2 = fuel_consumption(6.7 * L_per_100km);\nstd::println(\"Fuel consumptions: {}, {}\", q1, q2);\n\nstatic_assert(implicitly_convertible(q1.quantity_spec, isq::area));\nstatic_assert(!implicitly_convertible(q2.quantity_spec, isq::area));\nstatic_assert(!explicitly_convertible(q2.quantity_spec, isq::area));\nstatic_assert(!castable(q2.quantity_spec, isq::area));\n

    As we can see, with just units (especially derived ones) and dimensions, we often can't achieve the same level of safety as with adequately modeled hierarchies of quantities. Only in case of q2 we can prevent incorrect conversions to a different quantity of the same dimension.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#various-quantities-of-the-same-dimension-and-kinds","title":"Various quantities of the same dimension and kinds","text":"

    In the previous example, area and fuel consumption were quantities of the same dimension but of different kinds. In engineering, there are also many cases where we need to model distinct quantities of the same kind.

    Let's try to improve the safety of our Box example.

    First, we need to extend our ISQ definitions by the horizontal length quantity and a horizontal area derived from it:

    inline constexpr struct horizontal_length final : quantity_spec<isq::length> {} horizontal_length;\ninline constexpr struct horizontal_area final : quantity_spec<isq::area, horizontal_length * isq::width> {} horizontal_area;\n

    Note

    isq::length denotes any quantity of length (not only the horizontal one).

    static_assert(implicitly_convertible(horizontal_length, isq::length));\nstatic_assert(!implicitly_convertible(isq::length, horizontal_length));\n\nstatic_assert(implicitly_convertible(horizontal_area, isq::area));\nstatic_assert(!implicitly_convertible(isq::area, horizontal_area));\n\nstatic_assert(implicitly_convertible(isq::length * isq::length, isq::area));\nstatic_assert(!implicitly_convertible(isq::length * isq::length, horizontal_area));\n\nstatic_assert(implicitly_convertible(horizontal_length * isq::width, isq::area));\nstatic_assert(implicitly_convertible(horizontal_length * isq::width, horizontal_area));\n

    With simple two lines of definition, we made the above logic automatically work without needing additional customization for special cases. Based on hierarchies of derived quantities and their recipes, the proposed model automatically inherits the properties of base quantities involved in the recipe. This makes the composition of derived quantities very easy, which is not true for alternative solutions based on tag types that do not compose their properties.

    Now we can refactor our Box to benefit from the introduced safe abstractions:

    class Box {\n  quantity<horizontal_length[m]> length_;\n  quantity<isq::width[m]> width_;\n  quantity<isq::height[m]> height_;\npublic:\n  Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n    length_(l), width_(w), height_(h)\n  {}\n\n  quantity<horizontal_area[m2]> floor() const { return length_ * width_; }\n  // ...\n};\n

    It is important to note that the safety can be enforced only when a user provides typed quantities as arguments to the functions:

    Box my_box1(2 * m, 3 * m, 1 * m);\nBox my_box2(2 * horizontal_length[m], 3 * isq::width[m], 1 * isq::height[m]);\nBox my_box3(horizontal_length(2 * m), isq::width(3 * m), isq::height(1 * m));\n

    Important

    It is up to the user to decide when and where to care about explicit quantity types and when to prefer simple unit-only mode.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#various-kinds-of-dimensionless-quantities","title":"Various kinds of dimensionless quantities","text":"

    Most of the quantities hierarchies describe only one kind. There are some exceptions, though. One of them is a hierarchy of dimensionless quantities. This tree defines quantities that denote:

    • counts (e.g., storage capacity),
    • ratios (e.g., efficiency),
    • angles (e.g., angular measure, solid angular measure),
    • scaled numbers.

    Each of the above could form a separate tree of mutually comparable quantities. However, all of them have a common property. Every quantity from this tree, despite often being measured in a dedicated unit (e.g., bit, rad, sr), should also be able to be measured in a unit one.

    We've seen how to model such a hierarchy in a previous article in our series. This time, we will see a simplified part of a concrete, real-life example for this use case.

    We often need to provide strong types for different counts in the digital signal processing domain. Abstractions like samples, beats, MIDI clock, and others should not be possible to be intermixed with each other:

    namespace ni {\n\ninline constexpr struct SampleCount final : quantity_spec<dimensionless, is_kind> {} SampleCount;\ninline constexpr struct UnitSampleAmount final : quantity_spec<dimensionless, is_kind> {} UnitSampleAmount;\ninline constexpr struct MIDIClock final : quantity_spec<dimensionless, is_kind> {} MIDIClock;\ninline constexpr struct BeatCount final : quantity_spec<dimensionless, is_kind> {} BeatCount;\n

    We should also be able to create derived quantities from those. For example, when we divide such a quantity by time we should get a new strong quantity that can be measured in both a dedicated unit (e.g., Smpl/s for sample rate) and hertz:

    inline constexpr struct SampleDuration final : quantity_spec<isq::period_duration> {} SampleDuration;\ninline constexpr struct SamplingRate final : quantity_spec<isq::frequency, SampleCount / SampleDuration> {} SamplingRate;\n\ninline constexpr auto Amplitude = UnitSampleAmount;\ninline constexpr auto Level = UnitSampleAmount;\ninline constexpr struct Power final : quantity_spec<Level * Level> {} Power;\n\ninline constexpr struct BeatDuration final : quantity_spec<isq::period_duration> {} BeatDuration;\ninline constexpr struct Tempo final : quantity_spec<isq::frequency, BeatCount / BeatDuration> {} Tempo;\n

    We can also define a collection of units associated with specific quantity kinds and their symbols:

    inline constexpr struct Sample final : named_unit<\"Smpl\", one, kind_of<SampleCount>> {} Sample;\ninline constexpr struct SampleValue final : named_unit<\"PCM\", one, kind_of<UnitSampleAmount>> {} SampleValue;\ninline constexpr struct MIDIPulse final : named_unit<\"p\", one, kind_of<MIDIClock>> {} MIDIPulse;\n\ninline constexpr struct QuarterNote final : named_unit<\"q\", one, kind_of<BeatCount>> {} QuarterNote;\ninline constexpr struct HalfNote final : named_unit<\"h\", mag<2> * QuarterNote> {} HalfNote;\ninline constexpr struct DottedHalfNote final : named_unit<\"h.\", mag<3> * QuarterNote> {} DottedHalfNote;\ninline constexpr struct WholeNote final : named_unit<\"w\", mag<4> * QuarterNote> {} WholeNote;\ninline constexpr struct EightNote final : named_unit<\"8th\", mag_ratio<1, 2> * QuarterNote> {} EightNote;\ninline constexpr struct DottedQuarterNote final : named_unit<\"q.\", mag<3> * EightNote> {} DottedQuarterNote;\ninline constexpr struct QuarterNoteTriplet final : named_unit<\"qt\", mag_ratio<1, 3> * HalfNote> {} QuarterNoteTriplet;\ninline constexpr struct SixteenthNote final : named_unit<\"16th\", mag_ratio<1, 2> * EightNote> {} SixteenthNote;\ninline constexpr struct DottedEightNote final : named_unit<\"q.\", mag<3> * SixteenthNote> {} DottedEightNote;\n\ninline constexpr auto Beat = QuarterNote;\n\ninline constexpr struct BeatsPerMinute final : named_unit<\"bpm\", Beat / si::minute> {} BeatsPerMinute;\ninline constexpr struct MIDIPulsePerQuarter final : named_unit<\"ppqn\", MIDIPulse / QuarterNote> {} MIDIPulsePerQuarter;\n\nnamespace unit_symbols {\n\ninline constexpr auto Smpl = Sample;\ninline constexpr auto pcm = SampleValue;\ninline constexpr auto p = MIDIPulse;\n\ninline constexpr auto n_wd = 3 * HalfNote;\ninline constexpr auto n_w = WholeNote;\ninline constexpr auto n_hd = DottedHalfNote;\ninline constexpr auto n_h = HalfNote;\ninline constexpr auto n_qd = DottedQuarterNote;\ninline constexpr auto n_q = QuarterNote;\ninline constexpr auto n_qt = QuarterNoteTriplet;\ninline constexpr auto n_8thd = DottedEightNote;\ninline constexpr auto n_8th = EightNote;\ninline constexpr auto n_16th = SixteenthNote;\n\n}\n\n}  // namespace ni\n

    With the above, we can safely work with each quantity and use SI or domain-specific units as needed:

    using namespace ni::unit_symbols;\nusing namespace mp_units::si::unit_symbols;\n\nconst auto sr1 = ni::GetSampleRate();\nconst auto sr2 = 48'000.f * Smpl / s;\n\nconst auto samples = 512 * Smpl;\n\nconst auto sampleTime1 = (samples / sr1).in(s);\nconst auto sampleTime2 = (samples / sr2).in(ms);\n\nconst auto sampleDuration1 = (1 / sr1).in(ms);\nconst auto sampleDuration2 = (1 / sr2).in(ms);\n\nconst auto rampTime = 35.f * ms;\nconst auto rampSamples1 = (rampTime * sr1).force_in<int>(Smpl);\nconst auto rampSamples2 = (rampTime * sr2).force_in<int>(Smpl);\n\nstd::println(\"Sample rate 1 is: {}\", sr1);\nstd::println(\"Sample rate 2 is: {}\", sr2);\n\nstd::println(\"{} @ {} is {::N[.5f]}\", samples, sr1, sampleTime1);\nstd::println(\"{} @ {} is {::N[.5f]}\", samples, sr2, sampleTime2);\n\nstd::println(\"One sample @ {} is {::N[.5f]}\", sr1, sampleDuration1);\nstd::println(\"One sample @ {} is {::N[.5f]}\", sr2, sampleDuration2);\n\nstd::println(\"{} is {} @ {}\", rampTime, rampSamples1, sr1);\nstd::println(\"{} is {} @ {}\", rampTime, rampSamples2, sr2);\n

    The above prints:

    Sample rate 1 is: 44100 Hz\nSample rate 2 is: 48000 Smpl/s\n512 Smpl @ 44100 Hz is 0.01161 s\n512 Smpl @ 48000 Smpl/s is 10.66667 ms\nOne sample @ 44100 Hz is 0.02268 ms\nOne sample @ 48000 Smpl/s is 0.02083 ms\n35 ms is 1543 Smpl @ 44100 Hz\n35 ms is 1680 Smpl @ 48000 Smpl/s\n

    We can also do a bit more advanced computations to get the following:

    auto sampleValue = -0.4f * pcm;\nauto power1 = sampleValue * sampleValue;\nauto power2 = -0.2 * pow<2>(pcm);\n\nauto tempo = ni::GetTempo();\nauto reverbBeats = 1 * n_qd;\nauto reverbTime = reverbBeats / tempo;\n\nauto pulsePerQuarter = value_cast<float>(ni::GetPPQN());\nauto transportPosition = ni::GetTransportPos();\nauto transportBeats = (transportPosition / pulsePerQuarter).in(n_q);\nauto transportTime = (transportBeats / tempo).in(s);\n\nstd::println(\"SampleValue is: {}\", sampleValue);\nstd::println(\"Power 1 is: {}\", power1);\nstd::println(\"Power 2 is: {}\", power2);\n\nstd::println(\"Tempo is: {}\", tempo);\nstd::println(\"Reverb Beats is: {}\", reverbBeats);\nstd::println(\"Reverb Time is: {}\", reverbTime.in(s));\nstd::println(\"Pulse Per Quarter is: {}\", pulsePerQuarter);\nstd::println(\"Transport Position is: {}\", transportPosition);\nstd::println(\"Transport Beats is: {}\", transportBeats);\nstd::println(\"Transport Time is: {}\", transportTime);\n

    which prints:

    SampleValue is: -0.4 PCM\nPower 1 is: 0.16000001 PCM\u00b2\nPower 2 is: -0.2 PCM\u00b2\nTempo is: 110 bpm\nReverb Beats is: 1 q.\nReverb Time is: 0.8181818 s\nPulse Per Quarter is: 960 ppqn\nTransport Position is: 15836 p\nTransport Beats is: 16.495832 q\nTransport Time is: 8.997726 s\n

    Info

    More about this example can be found in \"Exploration of Strongly-typed Units in C++: A Case Study from Digital Audio\" CppCon 2023 talk by Roth Michaels.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will discuss the challenges and issues related to the modeling of the ISQ with a programming language.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/","title":"International System of Quantities (ISQ): Part 6 - Challenges","text":"

    This article might be the last one from our series. This time, we will discuss the challenges and issues with modeling of the ISQ in software.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#ambiguity","title":"Ambiguity","text":"

    Some quantity names are ambiguous. It is not a problem of ISQ but of the English language and the way we communicate things. When I say: \"Every width is a length, but not every length is a width\" most people understand this right away. However, the same people trying to model our 3D box problem try to do it as follows:

    class Box {\n  quantity<isq::length[m]> length_;\n  quantity<isq::width[m]> width_;\n  quantity<isq::height[m]> height_;\npublic:\n  // ...\n};\n

    This looks correct at first sight. Only when we think about the sentence mentioned above will we realize that this implementation has a problem. We intended to specify three orthogonal dimensions of the box, each of which will be a strong quantity that is not convertible to others. But we've failed.

    When we look at the tree of quantities of length we immediately see that both width and height are special lengths so they are convertible to it.

    To implement our task correctly, we had to define and use a new quantity of kind length:

    inline constexpr struct horizontal_length final : quantity_spec<isq::length> {} horizontal_length;\n

    We do not propose adding horizontal length to ISO 80000-3. There are probably other similar cases as well, but so far, this was the most common and obvious one we've encountered.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#no-common-quantities","title":"No common quantities","text":"

    ISO 80000-1:2009 explicitly states:

    Quote

    Two or more quantities cannot be added or subtracted unless they belong to the same category of mutually comparable quantities.

    This means that we should be able to add and subtract any quantities as long as they belong to the same kind. However, ISO/IEC documents do not provide any rules or even hints about what should be the result of such operations.

    If it is possible to add radius and distance, then what quantity should be provided in return? Undoubtedly, the resulting quantity type can't be the same as any of the arguments. It is not a radius or distance. It is some closer unspecified length, though.

    Info

    Finding the correct solution took us many months of experimentation and implementation. Based on the hierarchy tree of quantities, we can define conversion rules and what a common quantity should be.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#lack-of-consistency","title":"Lack of consistency","text":"

    The documents of ISO/IEC 80000 are not 100% consistent, and programming languages do not like inconsistencies.

    For example:

    • time is mentioned as a base quantity of ISQ in ISO 80000-1 chapter 4.5.
    • ISO 80000-3 \"Space and time\", does not define a quantity of time. It provides a duration quantity (item 3-9) with symbol t, and states in the Remarks section:

      Quote

      Duration is often just called time.

    • Other parts (e.g., IEC 80000-6 \"Electromagnetism\") often say:

      Quote

      ... t is time (ISO 80000-3)

    To be consistent, ISO/IEC should either:

    • change ISO 80000-1 chapter 4.5 and all references in other parts to use duration (unlikely),
    • or add time as an alias name to duration in the definition 3-9 of ISO 80000-3.
    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#lack-of-definitions","title":"Lack of definitions","text":"

    ISQ defines derived quantities in terms of other quantities provided in the series. However, some definitions mention quantities that are not defined in the ISQ at all.

    For example, weight is defined as \\(F_\\textsf{g} = m\\;g\\), where \\(m\\) is the mass of the body (item 4-1 of ISO 80000-4 \"Mechanics\"), and \\(g\\) is the local acceleration of free fall (ISO 80000-3).

    The problem here is that ISO 80000-3 never defines a quantity with a symbol \\(g\\) or named as a local acceleration of free fall. The closest one we have is acceleration (item 3-11) with a symbol \\(a\\).

    Info

    To have a proper definition of weight in mp-units that is not defined in terms of just any kind of acceleration, we have added isq::acceleration_of_free_fall in our definitions as an extension to the original ISQ set of quantities.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#not-engineering-friendly","title":"Not engineering-friendly","text":"

    Many quantities have proper physical definitions, but they are sometimes not engineering-friendly.

    For example, velocity is defined as a rate of change of position vector \\(v = \\frac{\\textsf{d}r}{\\textsf{d}t}\\), where \\(r\\) denotes the position vector (item 3\u20111.10) and \\(t\\) the duration (item 3\u20119).

    Next, a speed quantity is defined as the magnitude of velocity. Despite being physically correct, requiring every speed to be derived from the vector quantity of velocity in software would be inconvenient. If this was the only case, people would always need to use vector representations of position vectors to talk about speeds, which differs from what we do in practice. In practice, we divide any kind of length by time to get some kind of speed.

    ISO 80000-3 provides length, height, distance and other quantities of kind length that when divided by duration can serve really well to calculate speed.

    Info

    This is why in mp-units, we decided to divert from the official definition of speed and define it as:

    inline constexpr struct speed : quantity_spec<speed, length / time> {} speed;\n

    This allows us to create a quantity of kind speed from any quantity of length divided by time.

    Additionally, it is essential to note that for the needs of our library, defining velocity as position_vector / duration would be wrong. We miss the delta part here. Even though it is not mentioned in ISO 80000-3, the delta of position vectors is actually a displacement. This is why our velocity is defined as:

    inline constexpr struct velocity : quantity_spec<speed, displacement / duration> {} velocity;\n

    Please also note that velocity is defined as a more specialized quantity of speed.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#affine-space-agnostic","title":"Affine space agnostic","text":"

    The affine space is a powerful abstraction, allowing us to model some problems safer or more accurately. It has two types of entities:

    • point - a position specified with coordinate values (e.g., location, address, etc.),
    • displacement vector - the difference between two points (e.g., shift, offset, displacement, duration, etc.).

    Vectors support all the arithmetics operations, but points have some limitations. It is not possible to:

    • add two points,
    • subtract a point from a vector,
    • multiply nor divide points with anything else.

    ISO/IEC series does not acknowledge this abstraction even though it would be really useful in some cases. Let's discuss the following two examples.

    What does it mean to add two altitudes? It is not meaningful. On the other hand, subtracting those should not result in an altitude, but in a quantity of height. Adding or subtracting height to/from altitude results in altitude. Subtracting altitude from height is meaningless again. Those quantities clearly model affine space. Maybe this is why ISQ defines them as one quantity type height/depth/altitude?

    What does it mean to add two position vectors? It is not meaningful again. However, subtracting those results in a displacement as we noted in the previous chapter. Adding or subtracting displacement to/from position vector results in another position vector, and subtracting position vector from displacement does not have physical sense. Again, those quantities perfectly model affine space. However, this time, those are defined as separate and independent quantities (i.e., displacement is not modeled as delta position vector or position vector is not modeled as a displacement from the origin of a coordinate system).

    Info

    Currently, mp-units does not enforce the affine space behavior for such quantities. Today, subtracting two altitudes result in an altitude and subtracting two position vectors result in a position vector. However, we plan to support automatic conversion to a proper quantity type on subtraction and addition shortly.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#non-negative-quantities","title":"Non-negative quantities","text":"

    Some quantities in the ISQ are defined as non-negative. This is a really interesting property that may be checked at runtime to increase safety. However, the number of such quantities is minimal. From a few hundred quantities provided by the ISO/IEC series, only the following have this property mentioned explicitly:

    • width/breadth,
    • thickness,
    • diameter,
    • radius.

    If height was defined separately from altitude, it could probably also join this group.

    Let's think a bit more about this. What does it mean that a quantity is non-negative? Indeed, it is hard to imagine something of a negative width or radius. However, if we subtract two widths, the second one may be larger. This will result in a negative quantity of width, violating our precondition. So, is it non-negative or not?

    Again, we have to talk about the affine space abstractions. Every empirical measurement can be expressed as a point. Such points for some quantities may be non-negative indeed.

    Non-negative quantities do not end on the ones provided above. For example, speed is a good example here as well. In general, all magnitudes of vector quantities will also have this property.

    When subtracting two points, we end up with a delta/displacement type, which may be negative even for quantities listed as non-negative in the ISQ. As stated in the previous chapter, having affine space abstractions acknowledged in ISQ would greatly help here.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#lack-of-quantity-recipes","title":"Lack of quantity recipes","text":"

    Definition of many derived quantities provides their recipes in the form of quantity equations (e.g., weight equation in the previous chapter). However, some of them do not. Instead, they often provide a very generic description.

    For example, force is defined as:

    Quote

    vector (ISO 80000-2) quantity describing interaction between bodies or particles.

    This is not helpful for programming languages that like explicit definitions. Different vendors may interpret the above differently, which will result in different implementations that will not be compatible with each other.

    As the derived quantity of force has to be a vector quantity, it has to be defined in terms of at least one other vector quantity. We have a few to choose from:

    • displacement (\\(\\Delta{r}\\)),
    • velocity (\\(v\\)),
    • acceleration (\\(a\\)).

    It is not stated explicitly in ISQ which one of those should be used and how.

    Info

    In mp-units we decided to define force as \\(F = m\\;a\\).

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#lack-of-generic-quantities-and-name-conflicts","title":"Lack of generic quantities and name conflicts","text":"

    In the previous chapter, we complained about some definitions needing to be more complex or generic. On the other hand, we also lack some generic quantities in ISQ that could serve as a root for a quantity hierarchy tree.

    For example:

    • ISO 80000-4 \"Mechanics\" defines power <mechanics> as \\(P = F\\;v\\) (scalar product of force \\(F\\) (item 4-9.1) acting to a body and its velocity \\(v\\) (ISO 80000-3)),
    • ISO 80000-6 \"Electromagnetism\" defines power as \\(p = u\\;i\\) (scalar quantity given by the product of instantaneous voltage \\(u\\) (item 6-11.3) and instantaneous electric current \\(i\\) (item 6-1)).

    First, the above definitions have somehow conflicting names which makes it hard for the programming languages to name them consistently by different vendors.

    Info

    In mp-units, we chose mechanical_power and electromagnetism_power for those.

    Second, we do not have any other more generic definition of power to put above those in the tree. Not having it makes it hard to answer what should be the result of:

    quantity q = isq::mechanical_power(42 * W) + isq::electromagnetism_power(60 * W);\n

    Info

    To solve the above problem, we have added isq::power in mp-units, that has a really generic definition of:

    inline constexpr struct power : quantity_spec<mass* pow<2>(length) / pow<3>(time)> {} power;\n
    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#invalid-definitions-order","title":"Invalid definitions order","text":"

    Energy is defined a bit better than power, but still not without issues.

    The first time ISQ mentions energy is in the ISO 80000-4 \"Mechanics\". It defines potential energy, kinetic energy, and a mechanical energy as the sum of the first two. Right after that a mechanical work/work is defined.

    Then ISO 80000-5 \"Thermodynamics\" defines energy <thermodynamics> as:

    Quote

    ability of a system to do work (ISO 80000-4).

    Next, internal energy/thermodynamic energy is defined in terms of the change of heat.

    From the above, it seems that what is called energy <thermodynamics> should actually be the root of our tree and probably be provided in Part 4 before the mechanical energy is defined.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#hierarchies-of-derived-quantities","title":"Hierarchies of derived quantities","text":"

    Derived quantities of the same kind are often independently defined in the ISQ. The ISO/IEC 80000 series often does not suggest any hierarchy between those. Even more, it states:

    ISO/IEC Guide 99

    The division of \u2018quantity\u2019 according to \u2018kind of quantity\u2019 is, to some extent, arbitrary.

    Because of this, it is unknown or ambiguous how to form a hierarchy tree for such quantities.

    To get some sense of the complexity here, let's look again at our tree of quantities of a kind energy:

    flowchart TD\n    energy[\"<b>energy</b><br><i>(mass * length<sup>2</sup> / time<sup>2</sup>)</i><br>[J]\"]\n    energy --- mechanical_energy[\"<b>mechanical_energy</b>\"]\n    mechanical_energy --- potential_energy[\"<b>potential_energy</b>\"]\n    mechanical_energy --- kinetic_energy[\"<b>kinetic_energy</b>\"]\n    energy --- enthalpy[\"<b>enthalpy</b>\"]\n    enthalpy --- internal_energy[\"<b>internal_energy</b> / <b>thermodynamic_energy</b>\"]\n    internal_energy --- Helmholtz_energy[\"<b>Helmholtz_energy</b> / <b>Helmholtz_function</b>\"]\n    enthalpy --- Gibbs_energy[\"<b>Gibbs_energy</b> / <b>Gibbs_function</b>\"]\n    energy --- active_energy[\"<b>active_energy</b>\"]

    Not being exact means that every vendor may implement it differently. This will result in:

    • different convertibility rules among quantities:

      static_assert(implicitly_convertible(isq::potential_energy, isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::mechanical_energy, isq::potential_energy));\n
    • different common quantities resulting from the arithmetics on various quantities of the same kind:

      static_assert((isq::potential_energy(1 * J) + isq::kinetic_energy(1 * J)).quantity_spec == isq::mechanical_energy);\n

    It would be great if ISQ could provide specific division of quantities into kinds and more information about the position of each quantity within the hierarchy of quantities of the same kind.

    Important

    We can try to do this by ourselves, but it is tough. Probably no one, for sure we are not, is an expert in all the fields of ISO/IEC 80000 applicability.

    We need the help of subject matter experts who will help us build those trees for their domains and then verify that everything works as expected.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#the-same-or-a-different-kind","title":"The same or a different kind?","text":"

    Some quantities are more complicated than others. For example, power has:

    • scalar quantities expressed in:
      • W (watts) (e.g., mechanical power, active power),
      • VA (volt-ampere) (e.g., apparent power),
      • var (e.g., reactive power),
    • complex quantities expressed in VA (volt-ampere) (e.g., complex power).

    How should we model this? Maybe those should be two or three independent trees of quantities, each having its own unit?

    flowchart TD\n    power[\"<b>power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[W]\"]\n    power --- mechanical_power[\"<b>mechanical_power</b><br><i>(scalar_product(force, velocity))</i>\"]\n    power --- electromagnetism_power[\"<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>\"]\n    power --- active_power[\"<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>\"]\n\n    nonactive_power[\"<b>nonactive_power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[VA]\"]\n    nonactive_power --- reactive_power[\"<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]\"]\n\n    complex_power[\"<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i><br>[VA]\"]\n    complex_power --- apparent_power[\"<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i>\"]

    This will mean that we will not be able to add or compare active power, reactive power, and apparent power, which probably makes a lot of sense. However, it also means that the following will fail to compile:

    quantity apparent = isq::apparent_power(100 * VA);\nquantity active = isq::active_power(60 * W);\nquantity<isq::nonactive_power[VA]> q = sqrt(pow<2>(apparent) - pow<2>(active)); // Compile-time error\n

    Also the following will not work:

    quantity active = isq::active_power(60 * W);\nquantity reactive = isq::reactive_power(40 * var);\nquantity<isq::apparent_power[VA]> q = sqrt(pow<2>(active) + pow<2>(reactive)); // Compile-time error\n

    If we want the above to work maybe we need to implement the tree as follows?

    flowchart TD\n    power[\"<b>power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[W]\"]\n    power --- mechanical_power[\"<b>mechanical_power</b><br><i>(scalar_product(force, velocity))</i>\"]\n    power --- electromagnetism_power[\"<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>\"]\n    power --- apparent_power[\"<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i><br>[VA]\"]\n    apparent_power --- active_power[\"<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>\"]\n    apparent_power --- nonactive_power[\"<b>nonactive_power</b><br><i>(sqrt(apparent_power<sup>2</sup> - active_power<sup>2</sup>))</i><br>\"]\n    nonactive_power --- reactive_power[\"<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]\"]\n    apparent_power --- complex_power[\"<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i>\"]

    However, the above allows direct addition and comparison of active power and nonactive power, and also will not complain if someone will try to use watt (W) as a unit of apparent power or reactive power.

    Again, ISQ does not provide a direct answer here.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#more-base-quantities","title":"More base quantities?","text":"

    Is ISQ really based on only seven base quantities? Let's look at the definition of traffic intensity in IEC 80000-13 \"Information science and technology\":

    Quote

    number of simultaneously busy resources in a particular pool of resources.

    It looks like a definition of a specialized dimensionless quantity or, more correctly, a quantity of dimension one. This would not be the only such case. Even in the same Part 13, we can find quantities like storage capacity with a similar property.

    Only when we look closer do we start to see differences. All dimensionless quantities, even if they have their own dedicated units, can also be measured in a unit of one (1). This is true for storage capacity (also measured in bits), angular measure (also measured in radians), _solid angular measure (also measured in steradians), and more.

    However, traffic intensity can only be measured in erlangs (E), not in a unit one (1). Does it mean that it is a \"hidden\" 8-th base quantity in ISQ? If so, should it have its own dimension as well?

    Angular quantities are another interesting case here. Scientists have written petitions and papers for years to make them an additional dimension in ISQ and SI. More about this can be found in our documentation's Strong Angular System chapter.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#summary","title":"Summary","text":"

    ISQ is tremendous and solves many problems we had in modeling various subjects for years in software. As a result, we have more powerful tools in our hands that allow us to deliver safer products.

    Unfortunately, ISQ, contrarily to SI, is not widely recognized, and no libraries besides mp-units model it in any programming language. Keeping it behind a paywall does not help either. We hope that posts from this series will spread in the community, raise awareness of ISQ and its benefits, and encourage authors of other libraries to implement it in their products.

    Despite all the benefits, it is essential to realize that ISQ has many problems. International standards should be specified in such a way that there is no room for ambiguity in their interpretation by different parties trying to use them. As described above, this is not the case here.

    ISQ is not ready to be unambiguously modeled in software by various vendors. Here are the most important problems to solve to allow this:

    1. ISQ needs to define basic operations on quantities:

    - what the result of addition and subtraction should be when arguments differ, \u00a0 \u00a0 - convertibility rules.

    1. The exact quantity equation recipe needs to be included for many derived quantities.
    2. Many ISQ quantities do not provide their exact relation versus other quantities of the same kind (no strict hierarchy).
    3. Some missing quantities need to be included. Others would benefit from corrected names.

    Additionally:

    • extending ISQ with affine space abstractions,
    • specifying more quantities as non-negative,
    • adding more base quantities (i.e., angle)

    could improve the safety of our programs and products that people depend on with their lives on a daily basis.

    I hope you enjoyed following this series and learned more about the International System of Quantities. Please try it out in your domain and share feedback with us. We always love to hear about the projects in which our library is being used and about use cases it helps address.

    "},{"location":"blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/","title":"Report from the Kona 2023 ISO C++ Committee meeting","text":"

    Several groups in the ISO C++ Committee reviewed the P1935: A C++ Approach to Physical Units proposal in Belfast 2019 and Prague 2020. All those groups expressed interest in the potential standardization of such a library and encouraged further work. The authors also got valuable initial feedback that highly influenced the design of the V2 version of the mp-units library.

    In the following years, we scoped on getting more feedback from the production and design. This resulted in version 2 of the mp-units library that resolved many issues the users and Committee members raised. The features and interfaces of this version are close to being the best we can get with the current version of the C++ language standard.

    We submitted three new proposals related to the standardization of the quantities and units library for the last ISO C++ Committee meeting:

    • P2980: A motivation, scope, and plan for a physical quantities and units library,
    • P2981: Improving our safety with a physical quantities and units library,
    • P2982: std::quantity as a numeric type.

    Those were reviewed and briefly discussed in several groups: Numerics (SG6), Safety & Security (SG23), and Library Evolution Working Group (LEWG). Most of the feedback was positive, and the Committee is interested in spending more time on such proposals.

    The following poll was taken by the LEWG:

    LEWG POLL: Given that our time is limited, more time should be promised for a quantities and units library

    Strongly in Favor In favor Neutral Against Strongly Against 10 13 4 0 0

    Attendance: 22 + 6

    Number of Authors: 4

    Authors\u2019 position: 4x SF

    Outcome: Strong consensus in favor

    Additionally, some concerns were raised about the large scope of the proposal. We were encouraged to return with more details and design rationale in a unified paper. This is what we are working on now for the next Committee meeting that will happen in mid-March 2024 in Tokyo.

    "},{"location":"blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/","title":"Report from the St. Louis 2024 ISO C++ Committee meeting","text":"

    We made significant progress in the standardization of this library during the ISO C++ Committee meeting in St. Louis.

    "},{"location":"blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/#p30942r3-stdbasic_fixed_string","title":"P30942R3: std::basic_fixed_string","text":"

    First, the fixed_string was unanimously forwarded from the SG18 LEWG Incubator to the Library Evolution Working Group (LEWG). The group suggested a few minor changes to the paper, which resulted in the R3 version of the proposal.

    The paper is in excellent shape, and the entire wording is ready as well. Hopefully it will progress quickly through the remaining groups in the Committee.

    "},{"location":"blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/#p3045r1-quantities-and-units-library","title":"P3045R1: Quantities and units library","text":"

    In the SG6 (Numerics), we had a really efficient discussion about the recently raised usability issues with temperatures and the Minimal Viable Product (MVP) scope.

    The following polls were taken:

    POLL: If WG21 adds anything to the standard to provide units or quantities, then such a solution must at least include the necessary abstractions for units, dimensions, and quantity kinds. (It does not have to provide system definitions, e.g. ISQ/SI definitions.)

    Strongly in Favor In favor Neutral Against Strongly Against 7 4 7 0 0

    POLL: If WG21 adds anything to the standard to provide units or quantities, then such a solution must at least include the necessary abstractions for units, dimensions, quantity kinds, and quantities of the same kind. (It does not have to provide system definitions, e.g. ISQ/SI definitions.)

    Strongly in Favor In favor Neutral Against Strongly Against 5 2 5 2 0

    POLL: If WG21 adds anything to the standard to provide units or quantities, then such a solution must at least include the necessary abstractions for units, dimensions, quantity kinds, and affine spaces. (It does not have to provide system definitions, e.g. ISQ/SI definitions.)

    Strongly in Favor In favor Neutral Against Strongly Against 5 0 8 1 0

    As we can see, there are no controversies about the first poll that states that the MVP should include at least:

    • units,
    • dimensions,
    • quantity kinds (e.g., frequency vs activity, plane angle vs solid angle, fuel_consumption vs area).

    The next polls add either:

    • quantities of the same kind (e.g., length vs width vs wavelength, potential energy vs kinetic energy),
    • the affine space abstractions (i.e., point origins and quantity_point).

    SG6 considered those less important, but no one was strongly against including those in the MVP. We were asked to return with better motivation and usage examples for those features.

    If you are depending on quantities of the same kind or quantity points in your project and you would like to see them in the std library, please let us know about your use cases.

    Besides SG6, we spent six hours in the SG18 LEWG Incubator discussing the details of the library design. The proposal was very well received, and we got a few valuable comments and suggestions that we will apply to the next version of the paper.

    "},{"location":"blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/","title":"Report from the Tokyo 2024 ISO C++ Committee meeting","text":"

    The Tokyo 2024 meeting was a very important step in the standardization of this library. Several WG21 groups reviewed proposals, and the feedback was really good.

    "},{"location":"blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/#p3045r0-quantities-and-units-library","title":"P3045R0: Quantities and units library","text":"

    The Study Group 6 (Numerics) discussed the proposal for several hours. The initial feedback was positive. There were some concerns related to the description and design of the affine space abstractions in the library. Besides that, the people in the room liked what they saw.

    We run a few polls in SG6 as well:

    POLL: The syntax number * unit is the right solution for constructing quantities. Not allowing reordering the operands is correct.

    Strongly in Favor In favor Neutral Against Strongly Against 5 4 1 0 1

    POLL: Not defining any UDLs is the right solution.

    No objection to unanimous consent.

    The paper was also briefly discussed in SG18 LEWG Incubator, and the initial feedback was also positive. No polls were taken.

    SG16 Unicode does not meet during ISO C++ Committee F2F meetings. Still, the text output chapter paper was also reviewed by it during an online meeting before Tokyo. We got good feedback and are expected to return with the updated version. No polls were taken.

    "},{"location":"blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/#p30942r1-stdbasic_fixed_string","title":"P30942R1: std::basic_fixed_string","text":"

    In the SG18 LEWG Incubator, before we started to talk about P3045R0, we spent a few hours discussing the design of the std::basic_fixed_string, which is proposed for C++26. The group gave excellent feedback, and if the R2 version addresses it properly, the paper is expected to progress to LEWG (Library Evolution Working Group) in St. Louis.

    Plenty of polls were taken:

    POLL: We should promise more committee time to pursuing std::basic_fixed_string, knowing that our time is scarce and this will leave less time for other work.

    Strongly in Favor In favor Neutral Against Strongly Against 11 0 0 0 0

    POLL: Should the constructor from a string literal be consteval?

    Strongly in Favor In favor Neutral Against Strongly Against 6 3 2 0 0

    POLL: Do we want to add .view()?

    Strongly in Favor In favor Neutral Against Strongly Against 3 5 3 0 0

    POLL: Do we want the .size member to be an integral_constant<size_t, N> (and .empty to be bool_constant<N==0>)?

    Strongly in Favor In favor Neutral Against Strongly Against 5 2 2 2 0

    POLL: Should the index operator[] return a reference to const?

    Strongly in Favor In favor Neutral Against Strongly Against 2 2 3 3 0

    POLL: Should the constructor from a string literal have a precondition that txt[N] == 0?

    Strongly in Favor In favor Neutral Against Strongly Against 6 1 2 0 2"},{"location":"getting_started/contributing/","title":"Contributing","text":"

    \ud83d\udc4d\ud83c\udf89 First off, thanks for taking the time to contribute! \ud83c\udf89\ud83d\udc4d

    "},{"location":"getting_started/contributing/#mp-units-documentation","title":"mp-units documentation","text":"

    Before contributing, it is highly recommended to familiarize yourself with our official documentation.

    This file is also a part of it, and this is why it has non-standard Markdown formatting (which can be seen when reading in a regular Markdown renderer). To benefit from full mkdocs rendering, please switch to the Contributing chapter of our documentation.

    "},{"location":"getting_started/contributing/#where-to-start","title":"Where to start?","text":"

    If you are looking for a good issue to start with, please check the following:

    • good first issue - issues that should be pretty simple to implement,
    • help wanted - issues that typically are a bit more involved than beginner issues,
    • high priority - things to fix ASAP but often of higher complexity.
    "},{"location":"getting_started/contributing/#gitpod","title":"Gitpod","text":"

    The easiest way to start coding is to jump straight into Gitpod environment. You can either click the button below

    or prefix any mp-units URL (main branch, other branches, issues, PRs, ...) in your web browser with gitpod.io/# (e.g., https://gitpod.io/#https://github.com/mpusz/mp-units).

    The above environment provides you with:

    • all supported compilers for Linux development and the latest version of build tools like cmake and conan,
    • all Conan dependencies preinstalled on the machine,
    • all documentation generation tools ready to use,
    • completed prebuilds for all targets (Debug and Release builds for each compiler),
    • VSCode preconfigured to benefit from all the above.
    "},{"location":"getting_started/contributing/#building-testing-and-packaging","title":"Building, testing, and packaging","text":"

    Alternatively, please refer to our official docs for download, build, and install instructions with the below changes if you want to set up a development environment on your local machine.

    "},{"location":"getting_started/contributing/#conan-configuration-properties","title":"Conan configuration properties","text":"user.mp-units.build:all

    Enables compilation of all the source code, including tests and examples. To support this, it requires some additional Conan build dependencies described in Repository directory tree and dependencies. It also runs unit tests during the Conan build (unless tools.build:skip_test configuration property is set to True).

    user.mp-units.build:skip_la

    If user.mp-units.build:all is enabled, among others, Conan installs the external wg21-linear_algebra dependency and enables the compilation of linear algebra-based tests and usage examples. Such behavior can be disabled with this option.

    user.mp-units.analyze:clang-tidy

    Enables clang-tidy analysis.

    "},{"location":"getting_started/contributing/#cmake-options-for-mp-units-project-developers","title":"CMake options for mp-units project developers","text":"MP_UNITS_DEV_BUILD_LA

    2.2.0 \u00b7 ON/OFF (Default: ON)

    Enables building code depending on the linear algebra library.

    MP_UNITS_DEV_IWYU

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Enables include-what-you-use analysis.

    MP_UNITS_DEV_CLANG_TIDY

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Enables clang-tidy analysis.

    MP_UNITS_DEV_TIME_TRACE

    2.5.0 \u00b7 NONE/ALL/MODULES/HEADERS (Default: NONE)

    Enables compilation performance data collection with -ftime-trace for clang compilers.

    All our unit tests compile only for headers and never for modules. To allow fair comparison, MODULES and HEADERS do not enable the data collection for unit tests. This means that they affect only the core, systems, and examples.

    Please use ALL to profile unit tests as well.

    "},{"location":"getting_started/contributing/#building-the-entire-repository","title":"Building the entire repository","text":"

    To build all the mp-units source code (with unit tests and examples), you should:

    1. Use the CMakeLists.txt from the top-level directory.
    2. Run Conan with user.mp-units.build:all = True.
    git clone https://github.com/mpusz/mp-units.git && cd mp-units\nconan build . -pr <your_conan_profile> -s compiler.cppstd=23 -c user.mp-units.build:all=True -b missing\n

    The above will download and install all of the dependencies needed for the development of the library, build all of the source code, and run unit tests.

    If you prefer to build the project via CMake rather than Conan, then you should replace the conan build with conan install command and then follow with a regular CMake build and testing:

    conan install . -pr <your_conan_profile> -s compiler.cppstd=23 -c user.mp-units.build:all=True -b missing\ncmake --preset conan-default\ncmake --build --preset conan-release\ncmake --build --preset conan-release --target all_verify_interface_header_sets\ncmake --build --preset conan-release --target test\n

    Hint

    To ensure that we always build all the targets and to save some typing of the Conan commands, we can set the following in the ~/.conan2/global.conf:

    user.mp-units.build:all=True\n
    "},{"location":"getting_started/contributing/#packaging","title":"Packaging","text":"

    To test CMake installation and Conan packaging run:

    conan create . --user <username> --channel <channel> -pr <your_conan_profile> -s compiler.cppstd=23 \\\n               -c user.mp-units.build:all=True -b missing\n

    The above will create a Conan package and run tests provided in ./test_package directory.

    In case you would like to upload mp-units package to the Conan server, do the following:

    conan upload -r <remote-name> --all mp-units/2.2.0@<user>/<channel>\n
    "},{"location":"getting_started/contributing/#building-documentation","title":"Building documentation","text":"

    We are building our documentation using Material for MkDocs. The easiest way to install all the required dependencies is with pip:

    pip install -U mkdocs-material mkdocs-rss-plugin\n

    Additionally, a Cairo Graphics library is required by Material for MkDocs. Please follow the official MkDocs documentation to install it.

    After that, you can either:

    • easily start a live server to preview the documentation as you write

      mkdocs serve\n
    • build the documentation

      mkdocs build\n
    "},{"location":"getting_started/contributing/#generating-api-reference","title":"Generating API reference","text":"

    We need to take a few steps to set up our environment so that we are ready to generate API reference documents.

    First, we need to satisfy the requirements described in https://github.com/cplusplus/draft and https://github.com/Eelis/cxxdraft-htmlgen. On the Ubuntu platform, this is equivalent to the following instructions run from the user's home directory:

    sudo apt install latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended lmodern\nsudo apt install haskell-stack graphviz nodejs npm ghc cabal-install\nnpm install split mathjax-full mathjax-node-sre mathjax-node-cli\ncabal update\n

    On some platforms, installing mathjax-node-cli through npm does update the system's PATH environment variable resulting in tex2html not found errors. In such cases we need to add the .bin folder to the PATH environment variable manually:

    echo \"export PATH=\\\"~/node_modules/.bin:\\$PATH\\\"\" >> ~/.bashrc && source ~/.bashrc\n

    Next, we need to clone the following git repositories:

    • https://github.com/JohelEGP/jegp.cmake_modules
    • standardese_sources_base branch of https://github.com/JohelEGP/draft
    • standardese_sources_base branch of https://github.com/JohelEGP/cxxdraft-htmlgen

    For example:

    git clone https://github.com/JohelEGP/jegp.cmake_modules.git --depth=1\ngit clone https://github.com/JohelEGP/draft.git --branch=standardese_sources_base --depth=1\ngit clone https://github.com/JohelEGP/cxxdraft-htmlgen.git --branch=standardese_sources_base --depth=1\n

    Now, we are ready to start building our API reference. First, we need to configure CMake with the following:

    cmake -S docs/api_reference/src -B build/docs/api_reference \\\n      -DCMAKE_MODULE_PATH=\"<path to gh:JohelEGP/jegp.cmake_modules>/modules\" \\\n\u00a0 \u00a0 \u00a0 -DJEGP_STANDARDESE_SOURCES_GIT_REPOSITORY=\"<path to gh:JohelEGP/draft>\" \\\n      -DJEGP_CXXDRAFT_HTMLGEN_GIT_REPOSITORY=\"<path to gh:JohelEGP/cxxdraft-htmlgen>\"\n

    Then we need to build the docs with CMake:

    cmake --build build/docs/api_reference\n

    In the end, we need to move the generated documentation to the docs/api_reference/gen subdirectory:

    mv build/docs/api_reference/mp-units.html docs/api_reference/gen\n

    or just link the entire directory:

    ln -sf ../../build/docs/api_reference/mp-units.html docs/api_reference/gen\n
    "},{"location":"getting_started/contributing/#before-committing-git-changes","title":"Before committing git changes","text":"

    There are a few steps recommended to check before committing and pushing your changes to the git repository.

    "},{"location":"getting_started/contributing/#naming-conventions","title":"Naming conventions","text":"

    Here are the main rules for naming things in this repo:

    • types, functions, variables use standard_case,
    • template parameters use PascalCase,
    • C++ concept names, for now, use PascalCase, but we plan to change it (see GitHub Issue #93 for more details).
    "},{"location":"getting_started/contributing/#unified-code-formatting","title":"Unified code formatting","text":"

    A formatting standard is enforced with the pre-commit script. Before committing your changes, please do the following:

    pip install -U pre-commit\npre-commit run --all-files\n

    This will run:

    • clang-format for code formatting with the .clang-format file provided in the repo,
    • cmake-format to format the CMake files,
    • some other checks (e.g., python script checkers, whitespaces, etc.).

    The script will run on all the files in the repo and will apply the changes in place when needed. After the script is done, please make sure to review and stage all those changes for the git commit.

    "},{"location":"getting_started/contributing/#backward-compatibility","title":"Backward compatibility","text":"

    Before submission, please remember to check if the code compiles fine on the supported compilers. The CI will check it anyway, but it is good to check at least some of the configurations before pushing changes. Especially older compilers can be tricky as those do not have full C++20 conformance. The official list of supported compilers can always be found in the C++ compiler support (API/ABI) chapter of our documentation.

    "},{"location":"getting_started/cpp_compiler_support/","title":"C++ compiler support (API/ABI)","text":"

    Info

    mp-units library tries to provide the best user experience possible with the C++ language. To achieve that, it extensively uses the latest C++ language features.

    Even though the library benefits from the latest C++ versions (if available), C++20 is enough to compile and use all of the library's functionality. Newer features can be hidden behind some preprocessor macros providing a backward-compatible way to use them.

    The table below provides the minimum compiler version required to compile the code using a specific C++ feature:

    C++ Feature C++ version gcc clang apple-clang MSVC Minimum support 20 12 16 15 194 std::format 20 13 17 None 194 C++ modules 20 None 17 None None import std; 23 None 18 None None Explicit this parameter 23 14 18 None None MSVC bugs

    MSVC still has a poor C++20 conformance. We had to make many workarounds to our codebase to make it compile on this compiler. Usage of such nasty preprocessor macros degrade the readability and maintainability of our code. This is why we've applied those patches to the main library code but not to unit tests and examples. Those still do not compile on MSVC.

    Here is a list of the most important MSVC bugs:

    • Discrepancy in Behavior of operator= and operator for Multiplying int and double at compile time
    • Syntax error when using non-type template parameters in templated class member function
    • Type always preferred over value when using qualified identifiers

    Please upvote them so they get a higher fixing priority at Microsoft.

    Important

    Enabling/disabling features listed above may influence the API of the library and the ABI of the customers' projects.

    "},{"location":"getting_started/cpp_compiler_support/#stdformat","title":"std::format","text":"
    • Provides powerful text formatting capabilities for C++.
    • An alternative fmtlib library can be used instead if
      • the C++ language feature is not supported,
      • the customer's project did not switch to std::format yet (even when the compiler supports it).
    • To write code with wide compatibility a dedicated macro may be used.
    • Tested with __cpp_lib_format feature test macro.
    • Related build options:
      • Conan: std_format
      • CMake: MP_UNITS_API_STD_FORMAT
    "},{"location":"getting_started/cpp_compiler_support/#c-modules","title":"C++ modules","text":"
    • Provide new way to share declarations and definitions across translation units.
    • If used, the library will distribute both \"old-style\" headers and module interface units
      • associated with the same CMake targets.
    • Even with full compiler support, a user may still decide to not pay for C++ modules compilation if they are not needed by the customer's project.
    • Feature test macro is not used for testing here because even if the compiler does not support the entire C++ feature (e.g. header units), it is enough to build modules for this library.
    • Related build options:
      • Conan: cxx_modules
      • CMake: MP_UNITS_BUILD_CXX_MODULES

    Note

    More requirements for C++ modules support can be found in the CMake's documentation.

    "},{"location":"getting_started/cpp_compiler_support/#import-std","title":"import std;","text":"
    • If enabled, the library will obtain all the definitions from the std namespace via import std; instead of the \"old-style\" header includes.
    • Related build options:
      • Conan: import_std
      • CMake: CMAKE_CXX_MODULE_STD
    "},{"location":"getting_started/cpp_compiler_support/#explicit-this-parameter","title":"Explicit this parameter","text":"
    • This feature removes the need for the usage of the CRTP idiom in the quantity_spec definitions.
    • To write code with wide compatibility a dedicated macro may be used.
    • Tested with __cpp_explicit_this_parameter feature test macro.
    • Related build options:
      • Conan: no_crtp
      • CMake: MP_UNITS_API_NO_CRTP
    "},{"location":"getting_started/faq/","title":"Frequently Asked Questions","text":""},{"location":"getting_started/faq/#why-do-we-spell-metre-instead-of-meter","title":"Why do we spell metre instead of meter?","text":"

    This is how the BIPM defines it in the SI Brochure (British English spelling by default).

    "},{"location":"getting_started/faq/#why-dont-we-use-udls-to-create-quantities","title":"Why don't we use UDLs to create quantities?","text":"

    Many reasons make UDLs a poor choice for a physical units library:

    1. UDLs work only with literals (compile-time known values). Our observation is that besides the unit tests, there are only a few compile-time known quantity values used in the production code. Please note that for physical constants, we recommend using Faster-than-lightspeed Constants.
    2. Typical implementations of UDLs tend to always use the widest representation type available. In the case of std::chrono::duration, the following is true:

      using namespace std::chrono_literals;\nauto d1 = 42s;\nauto d2 = 42.s;\nstatic_assert(std::is_same_v<decltype(d1)::rep, std::int64_t>);\nstatic_assert(std::is_same_v<decltype(d2)::rep, long double>);\n

    When such UDL is intermixed in arithmetics with any quantity type of a shorter representation type, it will always expand it to the longest one. In other words, such long type spreads until all types use it everywhere.

    1. While increasing the coverage for the library, we learned that many unit symbols conflict with built-in types or numeric extensions. A few of those are: F (farad), J (joule), W (watt), K (kelvin), d (day), l or L (litre), erg, ergps. Usage of the _ prefix would make it work for mp-units, but in case the library is standardized, those naming collisions would be a big issue. This is why we came up with the _q_ prefix that would become q_ after standardization (e.g., 42q_s), which is not that nice anymore.

    2. UDLs with the same identifiers defined in different namespace can't be disambiguated in the C++ language. If both SI and CGS systems define _q_s UDL for a second unit, then it would not be possible to specify which one to use in case both namespaces are \"imported\" with using directives.

    3. Another bad property of UDLs is that they do not compose. A coherent unit of angular momentum would have a UDL specified as _q_kg_m2_per_s. Now imagine that we want to make every possible user happy. How many variations of that unit would we predefine for differently scaled versions of all unit ingredients?

    4. UDLs are also really expensive to define and specify. Typically, for each unit, we need two definitions. One for integral and another one for floating-point representation. Before the V2 framework, the coherent unit of angular momentum was defined as:

      constexpr auto operator\"\" _q_kg_m2_per_s(unsigned long long l)\n{\n  gsl_Expects(std::in_range<std::int64_t>(l));\n  return angular_momentum<kilogram_metre_sq_per_second, std::int64_t>(static_cast<std::int64_t>(l));\n}\n\nconstexpr auto operator\"\" _q_kg_m2_per_s(long double l)\n{\n  return angular_momentum<kilogram_metre_sq_per_second, long double>(l);\n}\n
    "},{"location":"getting_started/faq/#why-cant-i-create-a-quantity-by-passing-a-number-to-a-constructor","title":"Why can't I create a quantity by passing a number to a constructor?","text":"

    A quantity class template in the mp-units library has no publicly available constructor taking a raw value. Such support is provided by the std::chrono::duration and was pointed out to us as a red flag safety issue by a few parties already.

    Consider the following structure and a code using it:

    struct X {\n  std::vector<std::chrono::milliseconds> vec;\n  // ...\n};\n
    X x;\nx.vec.emplace_back(42);\n

    Everything works fine for years until, at some point, someone changes the structure to:

    struct X {\n  std::vector<std::chrono::microseconds> vec;\n  // ...\n};\n

    The code continues to compile just fine, but all the calculations are off now. This is why we decided to not follow this path.

    In the mp-units library, both a number and a unit have to always be explicitly provided in order to form a quantity.

    Note

    The same applies to the construction of quantity_point using an explicit point origin. To prevent similar safety issues during maintenance, the initialization always requires providing both a quantity and a PointOrigin that we use as a reference point.

    "},{"location":"getting_started/faq/#why-a-dimensionless-quantity-is-not-just-a-fundamental-arithmetic-type","title":"Why a dimensionless quantity is not just a fundamental arithmetic type?","text":"

    In the initial design of this library, the resulting type of division of two quantities was their common representation type:

    static_assert(std::is_same_v<decltype(10 * km / (5 * km)), int>);\n

    First of all, this was consistent with std::chrono::duration behavior. Additional reasoning behind it was not providing a false impression of a strong quantity type for something that looks and feels like a regular number. Also, all of the mathematic and trigonometric functions were working fine out of the box with such representation types, so we did not have to rewrite sin(), cos(), exp(), and others.

    However, the feedback we got from the production usage was that such an approach is really bad for generic programming. It is hard to handle the result of the two quantities' division (or multiplication) as it might be either a quantity or a fundamental type. If we want to raise such a result to some power, we must use units::pow or std::pow depending on the resulting type. Those are only a few issues related to such an approach.

    Moreover, suppose we divide quantities of the same dimension but with units of significantly different magnitudes. In that case, we may end up with a really small or a huge floating-point value, which may result in losing lots of precision. Returning a dimensionless quantity from such cases allows us to benefit from all the properties of scaled units and is consistent with the rest of the library.

    Note

    More information on the current design can be found in the Dimensionless Quantities chapter.

    "},{"location":"getting_started/faq/#why-derived-units-order-is-not-preserved-from-the-multiplication","title":"Why derived units order is not preserved from the multiplication?","text":"

    It might be surprising, but the quantities and units multiplication order does not impact the order of components in the derived unit. Let's try the following example:

    std::println(\"{}\", 42 * kW * h);\nconstexpr auto kWh = kW * h;\nstd::println(\"{}\", 42 * kWh);\n

    The above prints:

    42 h kW\n42 h kW\n

    Some users could expect to see 42 kWh or 42 kW h in the output. It is not the case and for a very good reason. As stated in Simplifying the resulting expression templates, to be able to reason about and simplify units, the library needs to order them in an appropriate order.

    Maybe this default order could be improved a bit, but according to international standards, there is no generic ordering rule. Various quantities use different, often domain-specific, ordering of derived unit components.

    Let's see what SI says here:

    Derived quantity Symbol Derived unit expressed in terms of base units electric field strength V m\u207b\u00b9 kg m s\u207b\u00b3 A\u207b\u00b9 electric charge density C m\u207b\u00b3 A s m\u207b\u00b3 exposure (x- and \u03b3-rays) C kg\u207b\u00b9 A s kg\u207b\u00b9

    However, there is a workaround. A user can define its own named unit for a derived unit and provide the custom symbol text that suits the project's requirements. For example, the above case could be addressed with:

    inline constexpr struct kilowatt_hour final : named_unit<\"kWh\", kW * h> {} kilowatt_hour;\ninline constexpr auto kWh = kilowatt_hour;\n

    With the above, we can refactor the above code to:

    std::println(\"{}\", 42 * kWh);\nstd::println(\"{}\", (42 * kW * h).in(kWh));\n

    Both lines will produce an expected \"42 kWh\" unit in the output.

    Important

    Please note that this makes the entire \"kWh\" a single, indivisible entity that is not subject to simplification rules. This means that 42 * kWh / (2 * h) will result with 21 kWh/h rather than 21 kW. To get the latter, the user needs to explicitly provide a new derived unit:

    std::println(\"{}\", (42 * kWh / (2 * h)).in(kW));\n
    "},{"location":"getting_started/faq/#why-do-the-identifiers-for-concepts-in-the-library-use-camelcase","title":"Why do the identifiers for concepts in the library use CamelCase?","text":"

    Initially, C++20 was meant to use CamelCase for all the concept identifiers. All the concepts from the std::ranges library were merged with such names into the standard document draft. Frustratingly, CamelCase concepts got dropped from the C++ standard at the last moment before releasing C++20. Now, we are facing the predictable consequences of running out of names.

    As long as some concepts in the library could be easily named with a standard_case there are some that are hard to distinguish from the corresponding type names, such as Quantity, QuantityPoint, QuantitySpec, or Reference. This is why we decided to use CamelCase consistently for all the concept identifiers to make it clear when we are talking about a type or concept identifier.

    However, we are aware that this might be a temporary solution. In case the library gets standardized, we can expect the ISO C++ Committee to bikeshed/rename all of the concept identifiers to a standard_case, even if it will result in a harder to understand code.

    Note

    In case you have a good idea of how to rename existing concepts to the standard_case, please let us know in the associated GitHub Issue.

    "},{"location":"getting_started/faq/#why-utf-8-quantity-symbols-are-used-by-default-instead-of-portable-characters","title":"Why UTF-8 quantity symbols are used by default instead of portable characters?","text":"

    Both C++ and ISO 80000 are standardized by the ISO. ISO 80000 and the SI standards specify UTF-8 symbols as the official unit names for some quantities (e.g. \u03a9 symbol for the resistance quantity). As the mp-units library will be proposed for standardization as a part of the C++ Standard Library we have to obey the rules and be consistent with ISO specifications.

    Note

    We do understand engineering reality and the constraints of some environments. This is why the library has the option of Portable Quantity Symbols.

    "},{"location":"getting_started/faq/#why-dont-we-have-cmake-options-to-disable-the-building-of-tests-and-examples","title":"Why don't we have CMake options to disable the building of tests and examples?","text":"

    Over time, many people provided PRs proposing adding options to build tests and examples conditionally. Here are a few examples:

    • Add CMake options for disabling docs, examples and tests
    • build: add options to disable part of the build
    • CMake Refactoring and Option Cleanup

    We admit this is a common practice in the industry, but we also believe this is a bad pattern.

    First, the only need for such options comes when a user wants to use add_subdirectory() in CMake to handle dependencies. Such an approach does not scale and should be discouraged. There is little use for such a practice in times when we have dedicated package managers like Conan.

    The second thing is that our observation is that many people are fixed on disabling \"unneeded\" subdirectories from compilation, but they do not see or address the biggest issue, which is polluting user's build environment with our development-specific settings. Propagating our restrictive compilation flags to user's project is not the best idea as it might cause a lot of harm if this project stops to compile because of that.

    Last but not least, not having those options is on purpose. Top level CMakeLists.txt file should only be used by mp-units developers and contributors as an entry point for the project's development. We want to ensure that everyone will build ALL the code correctly before pushing a commit. Having such options would allow unintended issues to leak to PRs and CI.

    This is why our projects have two entry points:

    • ./CMakeLists.txt is to be used by projects developers to build ALL the project code with really restrictive compilation flags,
    • ./src/CMakeLists.txt contains only a pure library definition and should be used by the customers that prefer to use CMake's add_subdirectory() to handle the dependencies.

    Note

    For more details on this please refer to the CMake + Conan: 3 Years Later - Mateusz Pusz lecture that Mateusz Pusz provided at the C++Now 2021 conference.

    "},{"location":"getting_started/installation_and_usage/","title":"Installation And Usage","text":"

    This chapter provides all the necessary information to obtain mp-units and build the user's source code using it.

    "},{"location":"getting_started/installation_and_usage/#obtaining-dependencies","title":"Obtaining dependencies","text":"

    This library assumes that most of the dependencies will be provided by the Conan Package Manager. If you want to obtain required dependencies by other means, some modifications to the library's CMake files might be needed.

    Conan quick intro

    In case you are not familiar with Conan, to install it (or upgrade) just do:

    pip install -U conan\n

    After that, you might need to add a custom profile file for your development environment in ~/.conan2/profiles directory. An example profile can look as follows:

    ~/.conan2/profiles/gcc12
    [settings]\narch=x86_64\nbuild_type=Release\ncompiler=gcc\ncompiler.cppstd=20\ncompiler.libcxx=libstdc++11\ncompiler.version=14\nos=Linux\n\n[conf]\ntools.build:compiler_executables={\"c\": \"gcc-14\", \"cpp\": \"g++-14\"}\n

    Setting the language version

    Please note that the mp-units library requires at least C++20 to be set in a Conan profile or forced via the Conan command line. If we do the former, we will not need to provide -s compiler.cppstd=20 every time we run a Conan command line (as provided in the command line instructions below).

    Using Ninja as a CMake generator for Conan

    It is highly recommended to set Ninja as a CMake generator for Conan. To do so, we could create a ~/.conan2/global.conf file that will set tools.cmake.cmaketoolchain:generator to one of the Ninja generators. For example:

    ~/.conan2/global.conf
    tools.cmake.cmaketoolchain:generator=\"Ninja Multi-Config\"\n

    Separate build folders for different configurations

    ~/.conan2/global.conf file may also set tools.cmake.cmake_layout:build_folder_vars which makes working with several compilers or build configurations easier. For example, the below line will force Conan to generate separate CMake presets and folders for each compiler and C++ standard version:

    ~/.conan2/global.conf
    tools.cmake.cmake_layout:build_folder_vars=[\"settings.compiler\", \"settings.compiler.version\", \"settings.compiler.cppstd\"]\n

    In such a case, we will need to use a configuration-specific preset name in the Conan instructions provided below rather than just conan-default and conan-release (e.g., conan-gcc-13-23 and conan-gcc-13-23-release)

    CMake with presets support

    It is recommended to use at least CMake 3.23 to build this project to benefit from CMake Presets generated by Conan. All build instructions below assume that you have such support. If not, your CMake invocations have to be replaced with something like:

    mkdir build && cd build\ncmake .. -G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=<path_to_generators_dir>/conan_toolchain.cmake\ncmake --build . --config Release\n

    Tip

    In case you can't use CMake 3.23 but you have access to CMake 3.20 or later, you can append -c tools.cmake.cmaketoolchain.presets:max_schema_version=2 to the conan install command which will force Conan to use an older version of the CMake Presets schema.

    "},{"location":"getting_started/installation_and_usage/#build-options","title":"Build options","text":"

    Note

    Most of the below options are related to the C++ language features available in the compilers. Please refer to the C++ compiler support chapter to learn more about which C++ features are required for each option and which compilers support them.

    "},{"location":"getting_started/installation_and_usage/#conan-options","title":"Conan options","text":"cxx_modules

    2.2.0 \u00b7 True/False (Default: automatically determined from settings)

    Configures CMake to add C++ modules to the list of default targets.

    import_std

    2.3.0 \u00b7 True/False (Default: automatically determined from settings)

    Enables import std; usage.

    std_format

    2.2.0 \u00b7 True/False (Default: automatically determined from settings)

    Enables the usage of std::format and associated facilities for text formatting. If it is not supported, then the {fmt} library is used instead.

    no_crtp

    2.2.0 \u00b7 True/False (Default: automatically determined from settings)

    Removes the need for the usage of the CRTP idiom in the quantity_spec definitions.

    contracts

    2.2.0 \u00b7 none/gsl-lite/ms-gsl (Default: see below)

    Enables checking of preconditions and additional assertions in the code.

    If the automatically determined default for import_std is True, then the contracts option is set to none by default. gsl-lite otherwise.

    freestanding

    2.2.0 \u00b7 True/False (Default: False)

    Configures the library in the freestanding mode. When enabled, the library's source code will build with the compiler's -ffreestanding compilation option without any issues.

    CMake options to set when Conan is not being used"},{"location":"getting_started/installation_and_usage/#cmake-options","title":"CMake options","text":"

    Conan will automatically set all the below CMake options based on its configuration (described above). Manual setting of the below CMake options is only needed when Conan is not being used.

    MP_UNITS_BUILD_AS_SYSTEM_HEADERS

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Exports library as system headers.

    MP_UNITS_BUILD_CXX_MODULES

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Adds C++ modules to the list of default targets.

    MP_UNITS_API_STD_FORMAT

    2.2.0 \u00b7 ON/OFF (Default: automatically determined)

    Enables the usage of std::format and associated facilities for text formatting. If it is not supported, then the {fmt} library is used instead.

    MP_UNITS_API_NO_CRTP

    2.2.0 \u00b7 ON/OFF (Default: automatically determined)

    Removes the need for the usage of the CRTP idiom in the quantity_spec definitions.

    MP_UNITS_API_CONTRACTS

    2.2.0 \u00b7 NONE/GSL-LITE/MS-GSL (Default: GSL-LITE)

    Enables checking of preconditions and additional asserts in the code.

    MP_UNITS_API_FREESTANDING

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Configures the library in the freestanding mode. When enabled, the library's source code should build with the compiler's -ffreestanding compilation option without any issues.

    "},{"location":"getting_started/installation_and_usage/#installation-and-reuse","title":"Installation and reuse","text":"

    There are many different ways of installing/reusing mp-units in your project. Below we mention only a few of many options possible.

    Important: Prefer using Conan if possible

    The easiest and most recommended way to obtain mp-units is with the Conan package manager. See Conan + CMake (release) for a detailed instruction.

    "},{"location":"getting_started/installation_and_usage/#conan-cmake-release","title":"Conan + CMake (release)","text":"

    Tip

    If you are new to the Conan package manager you may want to read Obtaining Dependencies and refer to the Consuming packages chapter of the official Conan documentation for more information.

    mp-units releases are hosted on Conan-Center. The following steps may be performed to obtain an official library release:

    1. Create Conan configuration file (either conanfile.txt or conanfile.py) in your project's top-level directory and add mp-units as a dependency of your project. For example, the simplest file may look as follows:

      conanfile.txtconanfile.py
      [requires]\nmp-units/2.4.0\n\n[options]\n# The below mp-units options are set to defaults by Conan.\n# Uncomment and set to an explicit value to override the defaults.\n#\n# mp-units*:cxx_modules=True\n# mp-units*:import_std=False\n# mp-units*:std_format=True\n# mp-units*:no_crtp=True\n# mp-units*:contracts=gsl-lite\n# mp-units*:freestanding=False\n\n[layout]\ncmake_layout\n\n[generators]\nCMakeToolchain\nCMakeDeps\n
      from conan import ConanFile\nfrom conan.tools.build import can_run\nfrom conan.tools.cmake import CMake, cmake_layout\n\nclass MPUnitsTestConan(ConanFile):\n    settings = \"os\", \"arch\", \"compiler\", \"build_type\"\n    generators = \"CMakeDeps\", \"CMakeToolchain\"\n\n    def requirements(self):\n        self.requires(\n            \"mp-units/2.4.0\",\n            options={\n                # The below mp-units options are set to defaults by Conan.\n                # Uncomment and set to an explicit value to override the defaults.\n                #\n                # \"cxx_modules\": False,\n                # \"import_std\": False,\n                # \"std_format\": True,\n                # \"no_crtp\": True,\n                # \"contracts\": \"gsl-lite\",\n                # \"freestanding\": False,\n            },\n        )\n\n    def layout(self):\n        cmake_layout(self)\n\n    def build(self):\n        cmake = CMake(self)\n        cmake.configure()\n        cmake.build()\n        if can_run(self):\n            cmake.ctest(cli_args=[\"--output-on-failure\"])\n
    2. Import mp-units and its dependencies definitions with find_package:

      find_package(mp-units REQUIRED)\n
    3. Link your CMake targets with mp-units:

      target_link_libraries(<your_target> <PUBLIC|PRIVATE|INTERFACE> mp-units::mp-units)\n
    4. Download, build, and install Conan dependencies before running the CMake configuration step:

      conanfile.txt or conanfile.pyconanfile.py only
      conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing\ncmake --preset conan-default\ncmake --build --preset conan-release\ncmake --build --preset conan-release --target test\n
      conan build . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing\n
    "},{"location":"getting_started/installation_and_usage/#conan-cmake-live-at-head","title":"Conan + CMake (Live At Head)","text":"

    This chapter describes the procedure to Live At Head, which means using the latest stable version of mp-units all the time.

    Note

    Please note that even though the Conan packages that you will be using are generated ONLY for builds that are considered stable (passed our CI tests), some minor regressions may happen (CI and C++ build environments are not perfect yet). Also, please expect that the library interface might, and probably will, change occasionally. Even though we do our best, such changes might not be reflected in the project's documentation right away.

    The procedure is similar to the one described in Conan + CMake (release) with the following differences:

    1. Before starting the previous procedure, add mp-units remote to your Conan configuration:

      conan remote add conan-mpusz https://mpusz.jfrog.io/artifactory/api/conan/conan-oss\n
    2. In your Conan configuration file, provide the package identifier of the mpusz/testing stream:

      conanfile.txtconanfile.py
      [requires]\nmp-units/2.5.0@mpusz/testing\n\n[options]\n# The below mp-units options are set to defaults by Conan.\n# Uncomment and set to an explicit value to override the defaults.\n#\n# mp-units*:cxx_modules=True\n# mp-units*:import_std=False\n# mp-units*:std_format=True\n# mp-units*:no_crtp=True\n# mp-units*:contracts=gsl-lite\n# mp-units*:freestanding=False\n\n[layout]\ncmake_layout\n\n[generators]\nCMakeToolchain\nCMakeDeps\n
      from conan import ConanFile\nfrom conan.tools.build import can_run\nfrom conan.tools.cmake import CMake, cmake_layout\n\nclass MPUnitsTestConan(ConanFile):\n    settings = \"os\", \"arch\", \"compiler\", \"build_type\"\n    generators = \"CMakeDeps\", \"CMakeToolchain\"\n\n    def requirements(self):\n        self.requires(\n            \"mp-units/2.5.0@mpusz/testing\",\n            options={\n                # The below mp-units options are set to defaults by Conan.\n                # Uncomment and set to an explicit value to override the defaults.\n                #\n                # \"cxx_modules\": False,\n                # \"import_std\": False,\n                # \"std_format\": True,\n                # \"no_crtp\": True,\n                # \"contracts\": \"gsl-lite\",\n                # \"freestanding\": False,\n            },\n        )\n\n    def layout(self):\n        cmake_layout(self)\n\n    def build(self):\n        cmake = CMake(self)\n        cmake.configure()\n        cmake.build()\n        if can_run(self):\n            cmake.ctest(cli_args=[\"--output-on-failure\"])\n

      Tip

      The identifiers of the latest packages can always be found in the project's README file or on the project's Artifactory.

    3. Force Conan to check for updated recipes with -u:

      conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing -u\n
    Alternative installation scenarios"},{"location":"getting_started/installation_and_usage/#copy","title":"Copy","text":"

    As mp-units is a C++ header-only library you can simply copy all needed src/*/include subdirectories to your source tree.

    Note

    In such a case, you are on your own to ensure all the dependencies are installed and their header files can be located during the build. Please also note that some compiler-specific flags are needed to make the code compile without issues.

    "},{"location":"getting_started/installation_and_usage/#copy-cmake","title":"Copy + CMake","text":"

    If you copy the mp-units library source code from the project's ./src directory (not the entire repo from its root), you can reuse CMake targets defined by the library. To do so, you should use CMakeLists.txt file from the ./src directory:

    add_subdirectory(<path_to_mp_units_lib_folder>)\n# ...\ntarget_link_libraries(<your_target> <PUBLIC|PRIVATE|INTERFACE> mp-units::mp-units)\n

    Note

    You are still on your own to make sure all the dependencies are installed and their header and CMake configuration files can be located during the build.

    Important: Library users should not use the top-level CMake file

    Top level CMakeLists.txt file should only be used by mp-units developers and contributors as an entry point for the project's development. ./src/CMakeLists.txt contains only a pure library definition and should be used by the customers that prefer to use CMake's add_subdirectory() to handle the dependencies.

    To learn more about the rationale, please check our FAQ.

    "},{"location":"getting_started/installation_and_usage/#install","title":"Install","text":"

    If you don't want to use Conan in your project and just want to install the mp-units library on your file system, and use find_package(mp-units) from another repository to find it; it is enough to perform the following steps:

    conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing\nmv CMakeUserPresets.json src\ncd src\ncmake --preset conan-default -DCMAKE_INSTALL_PREFIX=<your_installation_path>\ncmake --build --preset conan-release --target install\n
    "},{"location":"getting_started/introduction/","title":"Introduction","text":"

    mp-units is a Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation. The initial versions of the library were inspired by the std::chrono::duration but with each release, the interfaces diverged from the original to provide a better user experience.

    Info

    A brief introduction to the library's interfaces and the rationale for changes in version 2.0 of mp-units were provided in detail by Mateusz Pusz in the \"The Power of C++ Templates With mp-units: Lessons Learned & a New Library Design\" talk at the C++ on Sea 2023 conference.

    "},{"location":"getting_started/introduction/#open-source","title":"Open Source","text":"

    mp-units is Free and Open Source, with a permissive MIT license. Check out the source code and issue tracking (for questions and support, reporting bugs, suggesting feature requests and improvements) at https://github.com/mpusz/mp-units.

    "},{"location":"getting_started/introduction/#with-the-users-experience-in-mind","title":"With the User's Experience in Mind","text":"

    Most of the critical design decisions in the library are dictated by the requirement of providing the best user experience possible. Other C++ physical units libraries are \"famous\" for their enormous and hard-to-understand error messages (one line of the error log often does not fit on one slide). The ultimate goal of mp-units is to improve this and make compile-time errors and debugging as easy and user-friendly as possible.

    To achieve this goal, several techniques are applied:

    • usage of C++20 concepts that improve compile-times and the readability of error messages when compared to the traditional template metaprogramming with SFINAE,
    • usage of strong types for framework entities (instead of type aliases),
    • usage of expression templates to improve the readability of generated types,
    • limiting the number of template arguments to the bare minimum.

    Important: It is all about errors

    In many generic C++ libraries, compile-time errors do not happen often. It is hard to break std::string or std::vector in a way that won't compile with a huge error log. Physical quantities and units libraries are different. Generation of compile-time errors is the main reason to use such a library.

    "},{"location":"getting_started/introduction/#key-features","title":"Key Features","text":"Feature Description Safety - The affine space strong types (quantity and quantity_point)- Compile-time checked conversions of quantities and units- Unique support for many quantities of the same kind- Type-safe equations on scalar, vector, and tensor quantities and their units- Value-preserving conversions Performance - All the compile-time logic implemented as immediate (consteval) functions- As fast or even faster than working with fundamental types- No space size overhead needed to implement high-level abstractions Great User Experience - Optimized for readable compilation errors and great debugging experience- Efficient and composable way to specify a unit of choice- Value-based dimension, unit, and quantity equations Feature Rich - Systems of Quantities- Systems of Units- Scalar, vector, and tensor quantities- The affine space- Different models of the universe (e.g. natural units systems)- Strong dimensionless quantities- Strong angular system- Supports any unit's magnitude (huge, small, floating-point)- Faster-than-lightspeed constants- Highly adjustable text-output formatting Easy to Extend - Each entity can be defined with a single line of code- User can easily extend the systems with custom dimensions, quantities, and units Low Standardization Cost - Small number of predefined entities thanks to their composability- No external dependencies (assuming full C++20 support)- No macros in the user interface (besides portability and standard-compliance issues)- Possibility to be standardized as a freestanding part of the C++ Standard Library"},{"location":"getting_started/look_and_feel/","title":"Look and Feel","text":"

    Here is a small example of operations possible on scalar quantities:

    C++ modulesHeader files
    import mp_units;\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\n// simple numeric operations\nstatic_assert(10 * km / 2 == 5 * km);\n\n// conversions to common units\nstatic_assert(1 * h == 3600 * s);\nstatic_assert(1 * km + 1 * m == 1001 * m);\n\n// derived quantities\nstatic_assert(1 * km / (1 * s) == 1000 * m / s);\nstatic_assert(2 * km / h * (2 * h) == 4 * km);\nstatic_assert(2 * km / (2 * km / h) == 1 * h);\n\nstatic_assert(2 * m * (3 * m) == 6 * m2);\n\nstatic_assert(10 * km / (5 * km) == 2);\n\nstatic_assert(1000 / (1 * s) == 1 * kHz);\n
    #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\n// simple numeric operations\nstatic_assert(10 * km / 2 == 5 * km);\n\n// conversions to common units\nstatic_assert(1 * h == 3600 * s);\nstatic_assert(1 * km + 1 * m == 1001 * m);\n\n// derived quantities\nstatic_assert(1 * km / (1 * s) == 1000 * m / s);\nstatic_assert(2 * km / h * (2 * h) == 4 * km);\nstatic_assert(2 * km / (2 * km / h) == 1 * h);\n\nstatic_assert(2 * m * (3 * m) == 6 * m2);\n\nstatic_assert(10 * km / (5 * km) == 2);\n\nstatic_assert(1000 / (1 * s) == 1 * kHz);\n

    Try it on Compiler Explorer

    This library requires some C++20 features (concepts and constraints, classes as NTTP, ...). Thanks to them, a user gets a powerful but still easy-to-use interface where all unit conversions and dimensional analysis can be performed without sacrificing accuracy. Please see the below example for a quick preview of basic library features:

    C++ modulesHeader files
    #include <format>\n#include <iomanip>\n#include <iostream>\n#include <print>\nimport mp_units;\n\nusing namespace mp_units;\n\nconstexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d,\n                                                QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::international::unit_symbols;\n\n  constexpr quantity v1 = 110 * km / h;\n  constexpr quantity v2 = 70 * mph;\n  constexpr quantity v3 = avg_speed(220. * isq::distance[km], 2 * h);\n  constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * h);\n  constexpr quantity v5 = v3.in(m / s);\n  constexpr quantity v6 = value_cast<m / s>(v4);\n  constexpr quantity v7 = value_cast<int>(v6);\n\n  std::cout << v1 << '\\n';                                        // 110 km/h\n  std::cout << std::setw(10) << std::setfill('*') << v2 << '\\n';  // ***70 mi/h\n  std::cout << std::format(\"{:*^10}\\n\", v3);                      // *110 km/h*\n  std::println(\"{:%N in %U of %D}\", v4);                          // 70 in mi/h of LT\u207b\u00b9\n  std::println(\"{::N[.2f]}\", v5);                                 // 30.56 m/s\n  std::println(\"{::N[.2f]U[dn]}\", v6);                            // 31.29 m\u22c5s\u207b\u00b9\n  std::println(\"{:%N}\", v7);                                      // 31\n}\n
    #include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <format>\n#include <iomanip>\n#include <iostream>\n#include <print>\n\nusing namespace mp_units;\n\nconstexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d,\n                                                QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::international::unit_symbols;\n\n  constexpr quantity v1 = 110 * km / h;\n  constexpr quantity v2 = 70 * mph;\n  constexpr quantity v3 = avg_speed(220. * isq::distance[km], 2 * h);\n  constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * h);\n  constexpr quantity v5 = v3.in(m / s);\n  constexpr quantity v6 = value_cast<m / s>(v4);\n  constexpr quantity v7 = value_cast<int>(v6);\n\n  std::cout << v1 << '\\n';                                        // 110 km/h\n  std::cout << std::setw(10) << std::setfill('*') << v2 << '\\n';  // ***70 mi/h\n  std::cout << std::format(\"{:*^10}\\n\", v3);                      // *110 km/h*\n  std::println(\"{:%N in %U of %D}\", v4);                          // 70 in mi/h of LT\u207b\u00b9\n  std::println(\"{::N[.2f]}\", v5);                                 // 30.56 m/s\n  std::println(\"{::N[.2f]U[dn]}\", v6);                            // 31.29 m\u22c5s\u207b\u00b9\n  std::println(\"{:%N}\", v7);                                      // 31\n}\n

    Try it on Compiler Explorer

    Note

    More code examples can be found in the Examples chapter.

    "},{"location":"getting_started/project_structure/","title":"Project structure","text":"

    This chapter provides a high level overview of the project to make it easier to navigate, build, and use.

    "},{"location":"getting_started/project_structure/#cmake-projects-and-dependencies","title":"CMake projects and dependencies","text":"

    The GitHub repository contains three independent CMake-based projects:

    • ./src

      • header-only project containing whole mp-units library
      • ./src/CMakeLists.txt file is intended as an entry point for library users
      • in case this library becomes part of the C++ standard, it will have no external dependencies but until then, it depends on the following:

        • gsl-lite or ms-gsl to verify runtime contracts (if contract checking is enabled),
        • {fmt} to provide text formatting of quantities (if std::format is not supported yet on a specific compiler).
    • .

      • project used as an entry point for library development and CI/CD
      • it wraps ./src project together with usage examples and tests
      • additionally to the dependencies of ./src project, it uses:

        • Catch2 library as a unit tests framework,
        • linear algebra library based on proposal P1385 used in some examples and tests.
    • ./test_package

      • CMake library installation and Conan package verification.

    Important: Library users should not use the top-level CMake file

    Top level CMakeLists.txt file should only be used by mp-units developers and contributors as an entry point for the project's development. We want to ensure that everyone will build ALL the code correctly before pushing a commit. Having such options would allow unintended issues to leak to PRs and CI.

    This is why our projects have two entry points:

    • ./CMakeLists.txt is to be used by projects developers to build ALL the project code with really restrictive compilation flags,
    • ./src/CMakeLists.txt contains only a pure library definition and should be used by the customers that prefer to use CMake's add_subdirectory() to handle the dependencies.

    To learn more about the rationale, please check our FAQ.

    "},{"location":"getting_started/project_structure/#modules","title":"Modules","text":"

    The mp-units library provides the following C++ modules:

    flowchart TD\n    mp_units --- mp_units.systems --- mp_units.core
    C++ Module CMake Target Contents mp_units.core mp-units::core Core library framework and systems-independent utilities mp_units.systems mp-units::systems All the systems of quantities and units mp_units mp-units::mp-units Core + Systems

    Note

    C++ modules are provided within the package only when:

    • cxx_modules Conan option is set to True,
    • MP_UNITS_BUILD_CXX_MODULES CMake option is set to ON.
    "},{"location":"getting_started/project_structure/#header-files","title":"Header files","text":"

    All of the project's header files can be found in the mp-units/... subdirectory.

    "},{"location":"getting_started/project_structure/#core-library","title":"Core library","text":"
    • mp-units/framework.h contains the entire library's framework definitions,
    • mp-units/concepts.h exposes only the library's concepts for generic code needs,
    • mp-units/format.h provides text formatting support,
    • mp-units/ostream.h enables streaming of the library's objects to the text output,
    • mp-units/math.h provides overloads of common math functions for quantities,
    • mp-units/random.h provides C++ pseudo-random number generators for quantities,
    • mp-units/compat_macros.h provides macros for wide compatibility.
    More details

    More detailed header files can be found in subfolders which typically should not be included by the end users:

    • mp-units/framework/... provides all the public interfaces of the framework,
    • mp-units/bits/... provides private implementation details only (no public definitions),
    • mp-units/ext/... contains external dependencies that at some point in the future should be replaced with C++ standard library facilities.
    "},{"location":"getting_started/project_structure/#systems-and-associated-utilities","title":"Systems and associated utilities","text":"

    The systems definitions can be found in the mp-units/systems/... subdirectory:

    "},{"location":"getting_started/project_structure/#systems-of-quantities","title":"Systems of quantities","text":"
    • mp-units/systems/isq.h provides International System of Quantities (ISQ) definitions,
    • mp-units/systems/isq_angle.h provides a modification of the ISQ based on the proposals to make an angle a base quantity in the ISQ,
    Tip: Improving compile times

    mp-units/systems/isq.h might be expensive to compile in every translation unit. There are some smaller, domain targeted files available for explicit inclusion in the mp-units/systems/isq/... subdirectory.

    "},{"location":"getting_started/project_structure/#systems-of-units","title":"Systems of units","text":"
    • mp-units/systems/si.h provides International System of Units (SI) definitions and associated math functions,
    • mp-units/systems/iec.h provides units and prefixes defined by IEC (e.g., in the series of IEC 80000 standards),
    • mp-units/systems/angular.h provides strong angular units and associated math functions,
    • mp-units/systems/international.h provides international yard and pound units,
    • mp-units/systems/imperial.h includes international.h and extends it with imperial units,
    • mp-units/systems/usc.h includes international.h and extends it with United States customary system of units,
    • mp-units/systems/cgs.h provides centimetre-gram-second system of units,
    • mp-units/systems/iau.h provides astronomical system of units,
    • mp-units/systems/hep.h provides units used in high-energy physics,
    • mp-units/systems/typographic.h provides units used in typography or typesetting,
    • mp-units/systems/natural.h provides an example implementation of natural units.
    Tip: Improving compile times

    mp-units/systems/si.h might be expensive to compile in every translation unit. There are some smaller files available for explicit inclusion in the mp-units/systems/si/... subdirectory.

    mp-units/systems/si/unit_symbols.h is the most expensive to include.

    "},{"location":"getting_started/quick_start/","title":"Quick Start","text":"

    This chapter provides a quick introduction to get you started with mp-units. Much more details can be found in our User's Guide.

    "},{"location":"getting_started/quick_start/#quantities","title":"Quantities","text":"

    A quantity is a concrete amount of a unit representing a quantity type of a specified dimension with a specific representation. It is represented in the library with a quantity class template.

    The SI Brochure says:

    SI Brochure

    The value of the quantity is the product of the number and the unit. The space between the number and the unit is regarded as a multiplication sign (just as a space between units implies multiplication).

    Following the above, the value of a quantity in the mp-units library is created by multiplying a number with a predefined unit:

    C++ modulesHeader files
    import mp_units;\n\nusing namespace mp_units;\n\nquantity q = 42 * si::metre / si::second;\n
    #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\n\nquantity q = 42 * si::metre / si::second;\n

    Info

    In case someone doesn't like the multiply syntax or there is an ambiguity between operator* provided by this and other libraries, there are two other ways to create a quantity:

    1. delta construction helper:

      C++ modulesHeader files
      import mp_units;\n\nusing namespace mp_units;\n\nquantity q = delta<si::metre / si::second>(42);\n
      #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\n\nquantity q = delta<si::metre / si::second>(42);\n
    2. A two-parameter constructor:

      C++ modulesHeader files
      import mp_units;\n\nusing namespace mp_units;\n\nquantity q{42, si::metre / si::second};\n
      #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\n\nquantity q{42, si::metre / si::second};\n

    The above creates an instance of quantity<derived_unit<si::metre, per<si::second>>{}, int>. The same can be obtained using optional unit symbols:

    C++ modulesHeader files
    import mp_units;\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nquantity q = 42 * m / s;\n
    #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nquantity q = 42 * m / s;\n

    Important

    Unit symbols introduce a lot of short identifiers into the current scope, which may cause naming collisions with unrelated but already existing identifiers in the code base. This is why unit symbols are opt-in and typically should be imported only in the context where they are being used (e.g., function scope).

    A user has several options here to choose from depending on the required scenario and possible naming conflicts:

    using-directiveusing-declarationcustom short identifierunit names

    Explicitly \"import\" all of the symbols of a specific system of units from a dedicated unit_symbols namespace with a using-directive:

    using namespace mp_units;\n\nvoid foo(double speed_m_s)\n{\n  // imports all the SI symbols at once\n  using namespace si::unit_symbols;\n  quantity speed = speed_m_s * m / s;\n  // ...\n}\n

    Note

    This solution is perfect for small and isolated scopes but can cause surprising issues when used in larger scopes or when used for the entire program namespace.

    There are 29 named units in SI, and each of them has many prefixed variations (e.g., ng, kcd, ...). It is pretty easy to introduce a name collision with those.

    Selectively bring only the required and not-conflicting symbols with using-declarations:

    using namespace mp_units;\n\nvoid foo(double N)\n{\n  // 'N' function parameter would collide with the SI symbol for Newton, so we only bring what we need\n  using si::unit_symbols::m;\n  using si::unit_symbols::s;\n  quantity speed = N * m / s;\n  // ...\n}\n

    Specify a custom not conflicting unit identifier for a unit:

    using namespace mp_units;\n\nvoid foo(double speed_m_s)\n{\n  // names of some local variables are conflicting with the symbols we want to use\n  auto m = ...;\n  auto s = ...;\n\n  constexpr Unit auto mps = si::metre / si::second;\n  quantity speed = speed_m_s * mps;\n}\n

    Full unit names are straightforward to use and often provide the most readable code:

    using namespace mp_units;\n\nvoid foo(double m, double s)\n{\n  quantity speed = m * si::metre / (s * si::second);\n  // ...\n}\n

    Quantities of the same kind can be added, subtracted, and compared to each other:

    C++ modulesHeader files
    import mp_units;\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nstatic_assert(1 * km + 50 * m == 1050 * m);\n
    #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nstatic_assert(1 * km + 50 * m == 1050 * m);\n

    Various quantities can be multiplied or divided by each other:

    static_assert(140 * km / (2 * h) == 70 * km / h);\n

    Note

    In case you wonder why this library does not use UDLs to create quantities, please check our FAQ.

    "},{"location":"getting_started/quick_start/#quantity-points","title":"Quantity points","text":"

    The quantity point specifies an absolute quantity with respect to an origin. If no origin is provided explicitly, an implicit one will be provided by the library.

    Together with quantities, they model The Affine Space.

    Quantity points should be used in all places where adding two values is meaningless (e.g., temperature points, timestamps, altitudes, readouts from the car's odometer, etc.).

    The set of operations that can be done on quantity points is limited compared to quantities. This introduces an additional type-safety.

    C++ modulesHeader files
    #include <print>\nimport mp_units;\n\nint main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::usc::unit_symbols;\n\n  quantity_point temp = point<deg_C>(20.);\n  std::println(\"Temperature: {} ({})\",\n               temp.quantity_from_zero(),\n               temp.in(deg_F).quantity_from_zero());\n}\n
    #include <mp-units/format.h>\n#include <mp-units/systems/si.h>\n#include <mp-units/systems/usc.h>\n#include <print>\n\nint main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::usc::unit_symbols;\n\n  quantity_point temp = point<deg_C>(20.);\n  std::println(\"Temperature: {} ({})\",\n               temp.quantity_from_zero(),\n               temp.in(deg_F).quantity_from_zero());\n}\n

    The above outputs:

    Temperature: 20 \u2103 (68 \u2109)\n

    Info

    Check The Affine Space chapter to learn more about quantity points.

    "},{"location":"users_guide/terms_and_definitions/","title":"Terms and Definitions","text":"

    The mp-units project consistently uses the official metrology vocabulary defined by the ISO and BIPM. You can find essential project-related definitions in our documentation's \"Glossary\" chapter. Even more, terms are provided in the official metrology vocabulary of the ISO and BIPM.

    Tip

    Please familiarize yourself with terms from \"Glossary\" to better understand the documentation and improve domain-related communication and discussions.

    "},{"location":"users_guide/examples/avg_speed/","title":"avg_speed","text":"

    Try it on Compiler Explorer

    Let's continue the previous example. This time, our purpose will not be to showcase as many library features as possible, but we will scope on different interfaces one can provide with the mp-units. We will also describe some advantages and disadvantages of presented solutions.

    First, we either import a module or include all the necessary header files and import all the identifiers from the mp_units namespace:

    avg_speed.cpp
    #ifdef MP_UNITS_IMPORT_STD\nimport std;\n#else\n#include <exception>\n#include <iostream>\n#endif\n#ifdef MP_UNITS_MODULES\nimport mp_units;\n#else\n#include <mp-units/ostream.h>\n#include <mp-units/systems/cgs.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#endif\n\nnamespace {\n\nusing namespace mp_units;\n

    Next, we define two functions calculating average speed based on quantities of fixed units and integral and floating-point representation types, respectively, and a third function that we introduced in the previous example:

    avg_speed.cpp
    constexpr quantity<si::metre / si::second, int> fixed_int_si_avg_speed(quantity<si::metre, int> d,\n                                                                       quantity<si::second, int> t)\n{\n  return d / t;\n}\n\nconstexpr quantity<si::metre / si::second> fixed_double_si_avg_speed(quantity<si::metre> d, quantity<si::second> t)\n{\n  return d / t;\n}\n\nconstexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d, QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n

    We also added a simple utility to print our results:

    avg_speed.cpp
    template<QuantityOf<isq::length> D, QuantityOf<isq::time> T, QuantityOf<isq::speed> V>\nvoid print_result(D distance, T duration, V speed)\n{\n  const auto result_in_kmph = speed.force_in(si::kilo<si::metre> / non_si::hour);\n  std::cout << \"Average speed of a car that makes \" << distance << \" in \" << duration << \" is \" << result_in_kmph\n            << \".\\n\";\n}\n

    Now, let's analyze how those three utility functions behave with different sets of arguments. First, we are going to use quantities of SI units and integral representation:

    avg_speed.cpp
    void example()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  // SI (int)\n  {\n    constexpr auto distance = 220 * km;\n    constexpr auto duration = 2 * h;\n\n    std::cout << \"SI units with 'int' as representation\\n\";\n\n    print_result(distance, duration, fixed_int_si_avg_speed(distance, duration));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n

    The above provides the following output:

    SI units with 'int' as representation\nAverage speed of a car that makes 220 km in 2 h is 108 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\n

    Please note that in the first two cases, we must convert length from km to m and time from h to s. The converted values are used to calculate speed in m/s which is then again converted to the one in km/h. Those conversions not only impact the application's runtime performance but may also affect the precision of the final result. Such truncation can be easily observed in the first case where we deal with integral representation types (the resulting speed is 108 km/h).

    The second scenario is really similar to the previous one, but this time, function arguments have floating-point representation types:

    avg_speed.cpp
      // SI (double)\n  {\n    constexpr auto distance = 220. * km;\n    constexpr auto duration = 2. * h;\n\n    std::cout << \"\\nSI units with 'double' as representation\\n\";\n\n    // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed\n    print_result(distance, duration, fixed_int_si_avg_speed(value_cast<int>(distance), value_cast<int>(duration)));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n

    Conversion from floating-point to integral representation types is considered value-truncating and that is why now, in the first case, we need an explicit call to value_cast<int>.

    In the text output, we can observe that, again, the resulting value gets truncated during conversions in the first cast:

    SI units with 'double' as representation\nAverage speed of a car that makes 220 km in 2 h is 108 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\n

    Next, let's do the same for integral and floating-point representations, but this time using international mile:

    avg_speed.cpp
      // International mile (int)\n  {\n    using namespace mp_units::international::unit_symbols;\n\n    constexpr auto distance = 140 * mi;\n    constexpr auto duration = 2 * h;\n\n    std::cout << \"\\nInternational mile with 'int' as representation\\n\";\n\n    // it is not possible to make a lossless conversion of miles to meters on an integral type\n    // (explicit cast needed)\n    print_result(distance, duration, fixed_int_si_avg_speed(distance.force_in(m), duration));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n\n  // International mile (double)\n  {\n    using namespace mp_units::international::unit_symbols;\n\n    constexpr auto distance = 140. * mi;\n    constexpr auto duration = 2. * h;\n\n    std::cout << \"\\nInternational mile with 'double' as representation\\n\";\n\n    // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed\n    // also it is not possible to make a lossless conversion of miles to meters on an integral type\n    // (explicit cast needed)\n    print_result(distance, duration, fixed_int_si_avg_speed(value_cast<m, int>(distance), value_cast<int>(duration)));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n

    One important difference here is the fact that as it is not possible to make a lossless conversion of miles to meters on a quantity using an integral representation type, so this time, we need a value_cast<m, int> to force it.

    If we check the text output of the above, we will see the following:

    International mile with 'int' as representation\nAverage speed of a car that makes 140 mi in 2 h is 111 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112.654 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112 km/h.\n\nInternational mile with 'double' as representation\nAverage speed of a car that makes 140 mi in 2 h is 111 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112.654 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112.654 km/h.\n

    Please note how the first and third results get truncated using integral representation types.

    In the end, we repeat the scenario for CGS units:

    avg_speed.cpp
      // CGS (int)\n  {\n    constexpr auto distance = 22'000'000 * cgs::centimetre;\n    constexpr auto duration = 7200 * cgs::second;\n\n    std::cout << \"\\nCGS units with 'int' as representation\\n\";\n\n    // it is not possible to make a lossless conversion of centimeters to meters on an integral type\n    // (explicit cast needed)\n    print_result(distance, duration, fixed_int_si_avg_speed(distance.force_in(m), duration));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n\n  // CGS (double)\n  {\n    constexpr auto distance = 22'000'000. * cgs::centimetre;\n    constexpr auto duration = 7200. * cgs::second;\n\n    std::cout << \"\\nCGS units with 'double' as representation\\n\";\n\n    // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed\n    // it is not possible to make a lossless conversion of centimeters to meters on an integral type\n    // (explicit cast needed)\n    print_result(distance, duration, fixed_int_si_avg_speed(value_cast<m, int>(distance), value_cast<int>(duration)));\n\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n}\n\n}  // namespace\n

    Again, we observe value_cast being used in the same places and consistent truncation errors in the text output:

    CGS units with 'int' as representation\nAverage speed of a car that makes 22000000 cm in 7200 s is 108 km/h.\nAverage speed of a car that makes 22000000 cm in 7200 s is 110 km/h.\nAverage speed of a car that makes 22000000 cm in 7200 s is 109 km/h.\n\nCGS units with 'double' as representation\nAverage speed of a car that makes 2.2e+07 cm in 7200 s is 108 km/h.\nAverage speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h.\nAverage speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h.\n

    The example file ends with a simple main() function:

    avg_speed.cpp
    int main()\n{\n  try {\n    example();\n  } catch (const std::exception& ex) {\n    std::cerr << \"Unhandled std exception caught: \" << ex.what() << '\\n';\n  } catch (...) {\n    std::cerr << \"Unhandled unknown exception caught\\n\";\n  }\n}\n
    ","tags":["CGS System","International System"]},{"location":"users_guide/examples/hello_units/","title":"hello_units","text":"

    Try it on Compiler Explorer

    This is a really simple example showcasing the features of the mp-units library.

    First, we either import the mp_units module or include the headers for:

    • an International System of Quantities (ISQ),
    • an International System of units (SI),
    • units derived from the International Yard and Pound,
    • text formatting and stream output support.
    hello_units.cpp
    #include <mp-units/compat_macros.h>\n#include <mp-units/ext/format.h>\n#ifdef MP_UNITS_IMPORT_STD\nimport std;\n#else\n#include <iomanip>\n#include <iostream>\n#endif\n#ifdef MP_UNITS_MODULES\nimport mp_units;\n#else\n#include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#endif\n

    Also, to shorten the definitions, we \"import\" all the symbols from the mp_units namespace.

    hello_units.cpp
    using namespace mp_units;\n

    Next, we define a simple function that calculates the average speed based on the provided arguments of length and time:

    hello_units.cpp
    constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d, QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n

    The above function template takes any quantities implicitly convertible to isq::length and isq::time, respectively. Those quantities can use any compatible unit and a representation type. The function returns a result of a straightforward equation and ensures that its quantity type is implicitly convertible to isq::speed.

    Tip

    Besides verifying the type returned from the function, constraining a generic return type is beneficial for users of such a function as it provides more information of what to expect from a function than just using auto.

    hello_units.cpp
    int main()\n{\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::international::unit_symbols;\n

    The above lines explicitly opt into using unit symbols from two systems of units. As this introduces a lot of short identifiers into the current scope, it is not done implicitly while including a header file.

    hello_units.cpp
      constexpr quantity v1 = 110 * km / h;\n  constexpr quantity v2 = 70 * mph;\n  constexpr quantity v3 = avg_speed(220. * km, 2 * h);\n  constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * isq::duration[h]);\n  constexpr quantity v5 = v3.in(m / s);\n  constexpr quantity v6 = value_cast<m / s>(v4);\n  constexpr quantity v7 = value_cast<int>(v6);\n
    • Lines 27 & 28 create a quantity of kind isq::length / isq::time with the numbers and units provided. Such quantities can be converted or assigned to any other quantity with a matching kind.
    • Line 29 calls our function template with quantities of kind isq::length and isq::time and number and units provided.
    • Line 30 explicitly provides quantity types of the quantities passed to a function template. This time, those will not be quantity kinds anymore and will have more restrictive conversion rules.
    • Line 31 changes the unit of a quantity v3 to m / s in a value-preserving way (floating-point representations are considered to be value-preserving).
    • Line 32 does a similar operation, but this time, it would also succeed for value-truncating cases (if that was the case).
    • Line 33 does a value-truncating conversion of changing the underlying representation type from double to int.
    hello_units.cpp
      std::cout << v1 << '\\n';                                           // 110 km/h\n  std::cout << std::setw(10) << std::setfill('*') << v2 << '\\n';     // ***70 mi/h\n  std::cout << MP_UNITS_STD_FMT::format(\"{:*^10}\\n\", v3);            // *110 km/h*\n  std::cout << MP_UNITS_STD_FMT::format(\"{:%N in %U of %D}\\n\", v4);  // 70 in mi/h of LT\u207b\u00b9\n  std::cout << MP_UNITS_STD_FMT::format(\"{::N[.2f]}\\n\", v5);         // 30.56 m/s\n  std::cout << MP_UNITS_STD_FMT::format(\"{::N[.2f]U[dn]}\\n\", v6);    // 31.29 m\u22c5s\u207b\u00b9\n  std::cout << MP_UNITS_STD_FMT::format(\"{:%N}\\n\", v7);              // 31\n}\n

    The above presents various ways to print a quantity. Both stream insertion operations and std::format facilities are supported.

    Tip

    MP_UNITS_STD_FMT is used for compatibility reasons. If a specific compiler does not support std::format or a user prefers to use the {fmt} library, this macro will resolve to fmt namespace. Otherwise, the std namespace will be used.

    More about it can be found in the Wide Compatibility chapter.

    ","tags":["International System","Text Formatting"]},{"location":"users_guide/examples/hw_voltage/","title":"hw_voltage","text":"

    Try it on Compiler Explorer

    As it was stated in The Affine Space chapter, every measurement can (and probably should) be modelled as a quantity_point. This is a perfect example of such a use case.

    This example implements a simplified scenario of measuring voltage read from hardware through a mapped 16-bits register. The actual voltage range of [-10 V, 10 V] is mapped to [-32767, 32767] on hardware. Translation of the value requires not only scaling of the value but also applying of an offset.

    First we include all the dependencies:

    hw_voltage.cpp
    #include <mp-units/compat_macros.h>\n#include <mp-units/ext/format.h>\n#ifdef MP_UNITS_IMPORT_STD\nimport std;\n#else\n#include <iostream>\n#include <optional>\n#endif\n#ifdef MP_UNITS_MODULES\nimport mp_units;\n#else\n#include <mp-units/format.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#endif\n\nusing namespace mp_units;\n

    Next, we specify the real measurement voltage range to be in the range of [-10, 10]:

    hw_voltage.cpp
    // real voltage range\ninline constexpr int min_voltage = -10;\ninline constexpr int max_voltage = 10;\ninline constexpr int voltage_range = max_voltage - min_voltage;\n

    and provide a storage type and special values for the hardware representation:

    hw_voltage.cpp
    // hardware encoding of voltage\nusing voltage_hw_t = std::uint16_t;\ninline constexpr voltage_hw_t voltage_hw_error = std::numeric_limits<voltage_hw_t>::max();\ninline constexpr voltage_hw_t voltage_hw_min = 0;\ninline constexpr voltage_hw_t voltage_hw_max = voltage_hw_error - 1;\ninline constexpr voltage_hw_t voltage_hw_range = voltage_hw_max - voltage_hw_min;\ninline constexpr voltage_hw_t voltage_hw_zero = voltage_hw_range / 2;\n

    Finally, we define a quantity point origin, an offset unit that scales the value and uses this origin to offset the zero of the sale, and a dedicated quantity point alias using those:

    hw_voltage.cpp
    inline constexpr struct hw_voltage_origin final :\n  relative_point_origin<point<si::volt>(min_voltage)> {} hw_voltage_origin;\n\ninline constexpr struct hw_voltage_unit final :\n  named_unit<\"hwV\", mag_ratio<voltage_range, voltage_hw_range> * si::volt, hw_voltage_origin> {} hw_voltage_unit;\n\nusing hw_voltage_quantity_point = quantity_point<hw_voltage_unit, hw_voltage_origin, voltage_hw_t>;\n

    Now, when everything is ready, we can simulate mapping of our hardware register, and provide a helper function that will read the value and construct a quantity point from the obtained copy:

    hw_voltage.cpp
    // mapped HW register\nvolatile voltage_hw_t hw_voltage_value;\n\nstd::optional<hw_voltage_quantity_point> read_hw_voltage()\n{\n  voltage_hw_t local_copy = hw_voltage_value;\n  if (local_copy == voltage_hw_error) return std::nullopt;\n  return point<hw_voltage_unit>(local_copy);\n}\n

    We also provide a simple print helper for our quantity points:

    hw_voltage.cpp
    void print(QuantityPoint auto qp)\n{\n  std::cout << MP_UNITS_STD_FMT::format(\"{:10} ({:5})\", qp.quantity_from_zero(),\n                                        value_cast<double, si::volt>(qp).quantity_from_zero());\n}\n

    In the main function we simulate setting of 3 values by our hardware. Each of them is read and printed in the voltage unit used on the hardware as well as in the standard SI unit:

    hw_voltage.cpp
    int main()\n{\n  // simulate reading of 3 values from the hardware\n  hw_voltage_value = voltage_hw_min;\n  quantity_point qp1 = read_hw_voltage().value();\n  hw_voltage_value = voltage_hw_zero;\n  quantity_point qp2 = read_hw_voltage().value();\n  hw_voltage_value = voltage_hw_max;\n  quantity_point qp3 = read_hw_voltage().value();\n\n  print(qp1);\n  print(qp2);\n  print(qp3);\n}\n

    The above program results with the following text output:

         0 hwV (-10 V)\n 32767 hwV (  0 V)\n 65534 hwV ( 10 V)\n
    ","tags":["Affine Space","Embedded","Text Formatting"]},{"location":"users_guide/examples/si_constants/","title":"si_constants","text":"

    Try it on Compiler Explorer

    The next example presents all the seven defining constants of the SI system. We can observe how Faster-than-lightspeed Constants work in practice.

    si_constants.cpp
    #include <mp-units/compat_macros.h>\n#include <mp-units/ext/format.h>\n#ifdef MP_UNITS_IMPORT_STD\nimport std;\n#else\n#include <iostream>\n#endif\n#ifdef MP_UNITS_MODULES\nimport mp_units;\n#else\n#include <mp-units/format.h>\n#include <mp-units/systems/si.h>\n#endif\n

    As always, we start with the inclusion of all the needed header files. The main part of the example prints all of the SI-defining constants:

    si_constants.cpp
    int main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si;\n  using namespace mp_units::si::unit_symbols;\n\n  std::cout << \"The seven defining constants of the SI and the seven corresponding units they define:\\n\";\n  std::cout << MP_UNITS_STD_FMT::format(\"- hyperfine transition frequency of Cs: {} = {::N[.0]}\\n\",\n                                        1. * si2019::hyperfine_structure_transition_frequency_of_cs,\n                                        (1. * si2019::hyperfine_structure_transition_frequency_of_cs).in(Hz));\n  std::cout << MP_UNITS_STD_FMT::format(\"- speed of light in vacuum:             {} = {::N[.0]}\\n\",\n                                        1. * si2019::speed_of_light_in_vacuum,\n                                        (1. * si2019::speed_of_light_in_vacuum).in(m / s));\n  std::cout << MP_UNITS_STD_FMT::format(\"- Planck constant:                      {} = {::N[.8e]}\\n\",\n                                        1. * si2019::planck_constant, (1. * si2019::planck_constant).in(J * s));\n  std::cout << MP_UNITS_STD_FMT::format(\"- elementary charge:                    {} = {::N[.9e]}\\n\",\n                                        1. * si2019::elementary_charge, (1. * si2019::elementary_charge).in(C));\n  std::cout << MP_UNITS_STD_FMT::format(\"- Boltzmann constant:                   {} = {::N[.6e]}\\n\",\n                                        1. * si2019::boltzmann_constant, (1. * si2019::boltzmann_constant).in(J / K));\n  std::cout << MP_UNITS_STD_FMT::format(\"- Avogadro constant:                    {} = {::N[.8e]}\\n\",\n                                        1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant).in(one / mol));\n  std::cout << MP_UNITS_STD_FMT::format(\"- luminous efficacy:                    {} = {}\\n\",\n                                        1. * si2019::luminous_efficacy, (1. * si2019::luminous_efficacy).in(lm / W));\n}\n

    While analyzing the output of this program (provided below), we can easily notice that a direct printing of the quantity provides just a value 1 with a proper constant symbol. This is the main power of the Faster-than-lightspeed Constants feature. Only after we explicitly convert the unit of a quantity to proper SI units we get an actual numeric value of the constant.

    The seven defining constants of the SI and the seven corresponding units they define:\n- hyperfine transition frequency of Cs: 1 \u0394\u03bd_Cs = 9192631770 Hz\n- speed of light in vacuum:             1 c = 299792458 m/s\n- Planck constant:                      1 h = 6.62607015e-34 J s\n- elementary charge:                    1 e = 1.602176634e-19 C\n- Boltzmann constant:                   1 k = 1.380649e-23 J/K\n- Avogadro constant:                    1 N_A = 6.02214076e+23 1/mol\n- luminous efficacy:                    1 K_cd = 683 lm/W\n
    ","tags":["Physical Constants","Text Formatting"]},{"location":"users_guide/framework_basics/character_of_a_quantity/","title":"Character of a Quantity","text":"

    Warning

    This chapter's features are experimental and subject to change or removal. Please share your feedback if something seems wrong or could be improved.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#scalars-vectors-and-tensors","title":"Scalars, vectors, and tensors","text":"

    ISO 80000-2

    Scalars, vectors and tensors are mathematical objects that can be used to denote certain physical quantities and their values. They are as such independent of the particular choice of a coordinate system, whereas each scalar component of a vector or a tensor and each component vector and component tensor depend on that choice.

    Such distinction is important because each quantity character represents different properties and allows different operations to be done on its quantities.

    For example, imagine a physical units library that allows the creation of a \\(speed\\) quantity from both \\(length / time\\) and \\(length * time\\). It wouldn't be too safe to use such a product, right?

    Now we have to realize that both of the above operations (multiplication and division) are not even mathematically defined for linear algebra types such as vectors or tensors. On the other hand, two vectors can be passed as arguments to dot and cross-product operations. The result of the first one is a scalar. The second one results in a vector that is perpendicular to both vectors passed as arguments. Again, it wouldn't be safe to allow replacing those two operations with each other or expect the same results from both cases. This simply can't work.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#isq-defines-quantities-of-all-characters","title":"ISQ defines quantities of all characters","text":"

    While defining quantities ISO 80000 explicitly mentions when a specific quantity has a vector or tensor character. Here are some examples:

    Quantity Character Quantity Equation \\(duration\\) scalar {base quantity} \\(mass\\) scalar {base quantity} \\(length\\) scalar {base quantity} \\(path\\; length\\) scalar {base quantity} \\(radius\\) scalar {base quantity} \\(position\\; vector\\) vector {base quantity} \\(velocity\\) vector \\(position\\; vector / duration\\) \\(acceleration\\) vector \\(velocity / duration\\) \\(force\\) vector \\(mass * acceleration\\) \\(power\\) scalar \\(force \\cdot velocity\\) \\(moment\\; of\\; force\\) vector \\(position\\; vector \\times force\\) \\(torque\\) scalar \\(moment\\; of\\; force \\cdot \\{unit\\; vector\\}\\) \\(surface\\; tension\\) scalar \\(\\lvert force \\rvert / length\\) \\(angular\\; displacement\\) scalar \\(path\\; length / radius\\) \\(angular\\; velocity\\) vector \\(angular\\; displacement / duration * \\{unit\\; vector\\}\\) \\(momentum\\) vector \\(mass * velocity\\) \\(angular\\; momentum\\) vector \\(position\\; vector \\times momentum\\) \\(moment\\; of\\; inertia\\) tensor \\(angular\\; momentum \\otimes angular\\; velocity\\)

    In the above equations:

    • \\(a * b\\) - regular multiplication where one of the arguments has to be scalar
    • \\(a / b\\) - regular division where the divisor has to be scalar
    • \\(a \\cdot b\\) - dot product of two vectors
    • \\(a \\times b\\) - cross product of two vectors
    • \\(\\lvert a \\rvert\\) - magnitude of a vector
    • \\(\\{unit\\; vector\\}\\) - a special vector with the magnitude of \\(1\\)
    • \\(a \\otimes b\\) - tensor product of two vectors or tensors

    Note

    As of now, all of the C++ physical units libraries on the market besides mp-units do not support the operations mentioned above. They expose only multiplication and division operators, which do not work for linear algebra-based representation types. If a user of those libraries would like to create the quantities provided in the above table properly, this would result in a compile-time error stating that multiplication and division of two linear algebra vectors is impossible.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#characters-dont-apply-to-dimensions-and-units","title":"Characters don't apply to dimensions and units","text":"

    ISO 80000 explicitly states that dimensions are orthogonal to quantity characters:

    ISO 80000-1:2009

    In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character.

    Also, it explicitly states that:

    ISO 80000-2

    All units are scalars.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#defining-vector-and-tensor-quantities","title":"Defining vector and tensor quantities","text":"

    To specify that a specific quantity has a vector or tensor character a value of quantity_character enumeration can be appended to the quantity_spec describing such a quantity type:

    C++23C++20Portable
    inline constexpr struct displacement final : quantity_spec<length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final : quantity_spec<displacement> {} position_vector;\n
    inline constexpr struct displacement final : quantity_spec<displacement, length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final : quantity_spec<position_vector, displacement> {} position_vector;\n
    QUANTITY_SPEC(displacement, length, quantity_character::vector);\nQUANTITY_SPEC(position_vector, displacement);\n

    With the above, all the quantities derived from position_vector or displacement will have a correct character determined according to the kind of operations included in the quantity equation defining a derived quantity.

    For example, velocity in the below definition will be defined as a vector quantity (no explicit character override is needed):

    C++23C++20Portable
    inline constexpr struct velocity final : quantity_spec<speed, displacement / duration> {} velocity;\n
    inline constexpr struct velocity final : quantity_spec<velocity, speed, displacement / duration> {} velocity;\n
    QUANTITY_SPEC(velocity, speed, displacement / duration);\n
    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#representation-types-for-vector-and-tensor-quantities","title":"Representation types for vector and tensor quantities","text":"

    As we remember, the quantity class template is defined as follows:

    template<Reference auto R,\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity;\n

    The second template parameter is constrained with a RepresentationOf concept that checks if the provided representation type satisfies the requirements for the character associated with this quantity type.

    Note

    The current version of the C++ Standard Library does not provide any types that could be used as a representation type for vector and tensor quantities. This is why users are on their own here .

    To provide examples and implement unit tests, our library uses the types proposed in the P1385 and available as a Conan package in the Conan Center. However, thanks to the provided customization points, any linear algebra library types can be used as a vector or tensor quantity representation type.

    To enable the usage of a user-defined type as a representation type for vector or tensor quantities, we need to provide a partial specialization of is_vector or is_tensor customization points.

    For example, here is how it can be done for the P1385 types:

    #include <matrix>\n\nusing la_vector = STD_LA::fixed_size_column_vector<double, 3>;\n\ntemplate<>\nconstexpr bool mp_units::is_vector<la_vector> = true;\n

    With the above, we can use la_vector as a representation type for our quantity:

    Quantity auto q = la_vector{1, 2, 3} * isq::velocity[m / s];\n

    In case there is an ambiguity of operator* between mp-units and a linear algebra library, we can either:

    • use two-parameter constructor

      Quantity auto q = quantity{la_vector{1, 2, 3}, isq::velocity[m / s]};\n
    • provide a dedicated overload of operator* that will resolve the ambiguity and wrap the above

      template<Reference R>\nQuantity auto operator*(la_vector rep, R)\n{\n  return quantity{rep, R{}};\n}\n

    Note

    The following does not work:

    Quantity auto q1 = la_vector{1, 2, 3} * m / s;\nQuantity auto q2 = isq::velocity(la_vector{1, 2, 3} * m / s);\nquantity<isq::velocity[m/s]> q3{la_vector{1, 2, 3} * m / s};\n

    In all the cases above, the SI unit m / s has an associated scalar quantity of isq::length / isq::time. la_vector is not a correct representation type for a scalar quantity so the construction fails.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#hacking-the-character","title":"Hacking the character","text":"

    Sometimes we want to use a vector quantity, but we don't care about its direction. For example, the standard gravity acceleration constant always points down, so we might not care about this in a particular scenario. In such a case, we may want to \"hack\" the library to allow scalar types to be used as a representation type for scalar quantities.

    For example, we can do the following:

    template<class T>\n  requires mp_units::is_scalar<T>\nconstexpr bool mp_units::is_vector<T> = true;\n

    which says that every type that can be used as a scalar representation is also allowed for vector quantities.

    Doing the above is actually not such a big \"hack\" as the ISO 80000 explicitly allows it:

    ISO 80000-2

    A vector is a tensor of the first order and a scalar is a tensor of order zero.

    Despite it being allowed by ISO 80000, for type-safety reasons, we do not allow such a behavior by default, and a user has to opt into such scenarios explicitly.

    "},{"location":"users_guide/framework_basics/concepts/","title":"Concepts","text":"

    This chapter enumerates all the user-facing concepts in the mp-units library.

    "},{"location":"users_guide/framework_basics/concepts/#Dimension","title":"Dimension<T>","text":"

    Dimension concept matches a dimension of either a base or derived quantity:

    • Base dimensions are explicitly defined by the user by inheriting from the instantiation of a base_dimension class template. It should be instantiated with a unique symbol identifier describing this dimension in a specific system of quantities.
    • Derived dimensions are implicitly created by the library's framework based on the quantity equation provided in the quantity specification.

    All of the above dimensions have to be marked as final.

    "},{"location":"users_guide/framework_basics/concepts/#DimensionOf","title":"DimensionOf<T, V>","text":"

    DimensionOf concept is satisfied when both arguments satisfy a Dimension concept and when they compare equal.

    "},{"location":"users_guide/framework_basics/concepts/#QuantitySpec","title":"QuantitySpec<T>","text":"

    QuantitySpec concept matches all the quantity specifications including:

    • Base quantities defined by a user by inheriting from the quantity_spec class template instantiated with a base dimension argument.
    • Derived named quantities defined by a user by inheriting from the quantity_spec class template instantiated with a result of a quantity equation passed as an argument.
    • Other named quantities forming a hierarchy of quantities of the same kind defined by a user by inheriting from the quantity_spec class template instantiated with another \"parent\" quantity specification passed as an argument.
    • Quantity kinds describing a family of mutually comparable quantities.
    • Intermediate derived quantity specifications being a result of a quantity equations on other specifications.

    All of the above quantity specifications have to be marked as final.

    "},{"location":"users_guide/framework_basics/concepts/#QuantitySpecOf","title":"QuantitySpecOf<T, V>","text":"

    QuantitySpecOf concept is satisfied when both arguments satisfy a QuantitySpec concept and when T is implicitly convertible to V.

    More details

    Additionally:

    • T should not be a nested quantity specification of V
    • either T is quantity kind or V should not be a nested quantity specification of T

    Those additional conditions are required to make the following work:

    static_assert(ReferenceOf<si::radian, isq::angular_measure>);\nstatic_assert(!ReferenceOf<si::radian, dimensionless>);\nstatic_assert(!ReferenceOf<isq::angular_measure[si::radian], dimensionless>);\nstatic_assert(ReferenceOf<one, isq::angular_measure>);\nstatic_assert(!ReferenceOf<dimensionless[one], isq::angular_measure>);\n
    "},{"location":"users_guide/framework_basics/concepts/#Unit","title":"Unit<T>","text":"

    Unit concept matches all the units in the library including:

    • Base units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier describing this unit in a specific system of units.
    • Named scaled units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier and a product of multiplying another unit with some magnitude.
    • Prefixed units defined by a user by inheriting from the prefixed_unit class template instantiated with a prefix symbol, a magnitude, and a unit to be prefixed.
    • Derived named units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier and a result of unit equation passed as an argument.
    • Derived unnamed units being a result of a unit equations on other units.

    All of the above units have to be marked as final.

    Note

    In the mp-units library, physical constants are also implemented as units.

    "},{"location":"users_guide/framework_basics/concepts/#AssociatedUnit","title":"AssociatedUnit<T>","text":"

    AssociatedUnit concept describes a unit with an associated quantity and is satisfied by:

    • All units derived from a named_unit class template instantiated with a unique symbol identifier and a QuantitySpec of a quantity kind.
    • All units being a result of unit equations on other associated units.
    Examples

    All units in the SI have associated quantities. For example, si::second is specified to measure isq::time.

    Natural units typically do not have an associated quantity. For example, if we assume c = 1, a natural::second unit can be used to measure both time and length. In such case, speed would have a unit of one.

    "},{"location":"users_guide/framework_basics/concepts/#PrefixableUnit","title":"PrefixableUnit<T>","text":"

    PrefixableUnit concept is satisfied by all units derived from a named_unit class template. Such units can be passed as an argument to a prefixed_unit class template.

    "},{"location":"users_guide/framework_basics/concepts/#UnitOf","title":"UnitOf<T, V>","text":"

    UnitOf concept is satisfied for all units T matching an AssociatedUnit concept with an associated quantity type implicitly convertible to V.

    More details

    Additionally, the kind of V and the kind of quantity type associated with T must be the same, or the quantity type associated with T may not be derived from the kind of V.

    This condition is required to make dimensionless[si::radian] invalid as si::radian should be only used for isq::angular_measure, which is a nested quantity kind within the dimensionless quantities tree.

    "},{"location":"users_guide/framework_basics/concepts/#Reference","title":"Reference<T>","text":"

    Reference concept is satisfied by all quantity reference types. Such types provide all the meta-information required to create a Quantity. A Reference can either be:

    • An AssociatedUnit.
    • The instantiation of a reference class template with a QuantitySpec passed as the first template argument and a Unit passed as the second one.
    "},{"location":"users_guide/framework_basics/concepts/#ReferenceOf","title":"ReferenceOf<T, V>","text":"

    ReferenceOf concept is satisfied by references T which have a quantity specification that satisfies QuantitySpecOf<V> concept. |

    "},{"location":"users_guide/framework_basics/concepts/#Representation","title":"Representation<T>","text":"

    Representation concept constraints a type of a number that stores the value of a quantity.

    "},{"location":"users_guide/framework_basics/concepts/#RepresentationOf","title":"RepresentationOf<T, V>","text":"

    RepresentationOf concept is satisfied:

    • if the type of V satisfies QuantitySpec:

      • by all Representation types when V describes a quantity kind,
      • otherwise, by Representation types that are of a quantity character associated with a provided quantity specification V.
    • if V is of quantity_character type:

      • by Representation types that are of a provided quantity character.

    A user can declare a custom representation type to be of a specific character by providing the specialization with true for one or more of the following variable templates:

    • is_scalar<T>
    • is_complex<T>
    • is_vector<T>
    • is_tensor<T>
    Tip

    If we want to use scalar types to also express vector quantities (e.g., ignoring the \"direction\" of the vector) the following definition can be provided to enable such a behavior:

    template<class T>\n  requires mp_units::is_scalar<T>\nconstexpr bool mp_units::is_vector<T> = true;\n
    "},{"location":"users_guide/framework_basics/concepts/#Quantity","title":"Quantity<T>","text":"

    Quantity concept matches every quantity in the library and is satisfied by all types being or deriving from an instantiation of a quantity class template.

    "},{"location":"users_guide/framework_basics/concepts/#QuantityOf","title":"QuantityOf<T, V>","text":"

    QuantityOf concept is satisfied by all the quantities for which a QuantitySpecOf<V> is true.

    "},{"location":"users_guide/framework_basics/concepts/#PointOrigin","title":"PointOrigin<T>","text":"

    PointOrigin concept matches all quantity point origins in the library. It is satisfied by either:

    • All types derived from an absolute_point_origin class template.
    • All types derived from a relative_point_origin class template.
    "},{"location":"users_guide/framework_basics/concepts/#PointOriginFor","title":"PointOriginFor<T, V>","text":"

    PointOriginFor concept is satisfied by all PointOrigin types that have quantity type implicitly convertible from quantity specification V, which means that V must satisfy QuantitySpecOf<T::quantity_spec>.

    Examples

    si::ice_point can serve as a point origin for points of isq::Celsius_temperature because this quantity type implicitly converts to isq::thermodynamic_temperature.

    However, if we define mean_sea_level in the following way:

    inline constexpr struct mean_sea_level final : absolute_point_origin<isq::altitude> {} mean_sea_level;\n

    then it can't be used as a point origin for points of isq::length or isq::width as none of them is implicitly convertible to isq::altitude:

    • not every length is an altitude,
    • width is not compatible with altitude.
    "},{"location":"users_guide/framework_basics/concepts/#QuantityPoint","title":"QuantityPoint<T>","text":"

    QuantityPoint concept is satisfied by all types being either a specialization or derived from quantity_point class template.

    "},{"location":"users_guide/framework_basics/concepts/#QuantityPointOf","title":"QuantityPointOf<T, V>","text":"

    QuantityPointOf concept is satisfied by all the quantity points T that match the following value V:

    V Condition QuantitySpec The quantity point quantity specification satisfies QuantitySpecOf<V> concept. PointOrigin The point and V have the same absolute point origin."},{"location":"users_guide/framework_basics/concepts/#QuantityLike","title":"QuantityLike<T>","text":"

    QuantityLike concept provides interoperability with other libraries and is satisfied by a type T for which an instantiation of quantity_like_traits type trait yields a valid type that provides:

    • reference static data member that matches the Reference concept,
    • rep type that matches RepresentationOf concept with the character provided in reference,
    • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity type should happen explicitly (if true),
    • explicit_export static data member convertible to bool that specifies that the conversion from a quantity type to T should happen explicitly (if true),
    • to_numerical_value(T) static member function returning a raw value of the quantity,
    • from_numerical_value(rep) static member function returning T.
    Examples

    This is how support for std::chrono::seconds can be provided:

    template<>\nstruct mp_units::quantity_like_traits<std::chrono::seconds> {\n  static constexpr auto reference = si::second;\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = false;\n  using rep = std::chrono::seconds::rep;\n\n  [[nodiscard]] static constexpr rep to_numerical_value(const std::chrono::seconds& d)\n  {\n    return d.count();\n  }\n\n  [[nodiscard]] static constexpr std::chrono::seconds from_numerical_value(const rep& v)\n  {\n    return std::chrono::seconds(v);\n  }\n};\n\nquantity q = 42s;\nstd::chrono::seconds dur = 42 * s;\n
    "},{"location":"users_guide/framework_basics/concepts/#QuantityPointLike","title":"QuantityPointLike<T>","text":"

    QuantityPointLike concept provides interoperability with other libraries and is satisfied by a type T for which an instantiation of quantity_point_like_traits type trait yields a valid type that provides:

    • reference static data member that matches the Reference concept.
    • point_origin static data member that matches the PointOrigin concept.
    • rep type that matches RepresentationOf concept with the character provided in reference.
    • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity_point type should happen explicitly (if true),
    • explicit_export static data member convertible to bool that specifies that the conversion from a quantity_point type to T should happen explicitly (if true),
    • to_numerical_value(T) static member function returning a raw value of the quantity being the offset of the point from the origin,
    • from_numerical_value(rep) static member function returning T.
    Examples

    This is how support for a std::chrono::time_point of std::chrono::seconds can be provided:

    template<typename C>\nstruct mp_units::quantity_point_like_traits<std::chrono::time_point<C, std::chrono::seconds>> {\n  static constexpr auto reference = si::second;\n  static constexpr struct point_origin_ final : absolute_point_origin<isq::time> {} point_origin{};\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = false;\n  using rep = std::chrono::seconds::rep;\n  using T = std::chrono::time_point<C, std::chrono::seconds>;\n\n  [[nodiscard]] static constexpr rep to_numerical_value(const T& tp)\n  {\n    return tp.time_since_epoch().count();\n  }\n\n  [[nodiscard]] static constexpr T from_numerical_value(const rep& v)\n  {\n    return T(std::chrono::seconds(v));\n  }\n};\n\nquantity_point qp = time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());\nstd::chrono::sys_seconds q = qp + 42 * s;\n
    "},{"location":"users_guide/framework_basics/design_overview/","title":"Design Overview","text":"

    The most important entities in the mp-units library are:

    • quantity,
    • quantity point,
    • unit,
    • dimension,
    • quantity specification
    • quantity reference,
    • quantity representation.

    The graph provided below presents how those and a few other entities depend on each other:

    flowchart TD\n    Unit --- Reference\n    Dimension --- QuantitySpec[\"Quantity specification\"]\n    quantity_character[\"Quantity character\"] --- QuantitySpec\n    QuantitySpec --- Reference[\"Quantity reference\"]\n    Reference --- Quantity\n    quantity_character -.- Representation\n    Representation --- Quantity\n    Quantity --- QuantityPoint[\"Quantity point\"]\n    PointOrigin[\"Point origin\"] --- QuantityPoint\n\n    click Dimension \"#dimension\"\n    click quantity_character \"#quantity-character\"\n    click QuantitySpec \"#quantity-specification\"\n    click Unit \"#unit\"\n    click Reference \"#quantity-reference\"\n    click Representation \"#quantity-representation\"\n    click Quantity \"#quantity\"\n    click PointOrigin \"#point-origin\"\n    click QuantityPoint \"#quantity-point\"
    "},{"location":"users_guide/framework_basics/design_overview/#dimension","title":"Dimension","text":"

    Dimension specifies the dependence of a quantity on the base quantities of a particular system of quantities. It is represented as a product of powers of factors corresponding to the base quantities, omitting any numerical factor.

    In the mp-units library, we use the terms:

    • base dimension to refer to the dimension of a base quantity,
    • derived dimension to refer to the dimension of a derived quantity.

    For example:

    • length (\\(\\mathsf{L}\\)), mass (\\(\\mathsf{M}\\)), time (\\(\\mathsf{T}\\)), electric current (\\(\\mathsf{I}\\)), thermodynamic temperature (\\(\\mathsf{\u0398}\\)), amount of substance (\\(\\mathsf{N}\\)), and luminous intensity (\\(\\mathsf{J}\\)) are the base dimensions of the ISQ.
    • A derived dimension of force in the ISQ is denoted by \\(\\textsf{dim }F = \\mathsf{LMT}^{\u20132}\\).
    • The implementation of IEC 80000 in this library provides iec::dim_traffic_intensity base dimension to extend ISQ with strong information technology quantities.

    Base dimensions can be defined by the user in the following way:

    inline constexpr struct dim_length final : base_dimension<\"L\"> {} dim_length;\ninline constexpr struct dim_time final : base_dimension<\"T\"> {} dim_time;\n

    Derived dimensions are implicitly created by the library's framework based on the quantity equation provided in the quantity specification:

    C++23C++20Portable
    inline constexpr struct length final : quantity_spec<dim_length> {} length;\ninline constexpr struct time final : quantity_spec<dim_time> {} time;\ninline constexpr struct speed final : quantity_spec<length / time> {} speed;\n\nstatic_assert(speed.dimension == dim_length / dim_time);\n
    inline constexpr struct length final : quantity_spec<length, dim_length> {} length;\ninline constexpr struct time final : quantity_spec<time, dim_time> {} time;\ninline constexpr struct speed final : quantity_spec<speed, length / time> {} speed;\n\nstatic_assert(speed.dimension == dim_length / dim_time);\n
    QUANTITY_SPEC(length, dim_length);\nQUANTITY_SPEC(time, dim_time);\nQUANTITY_SPEC(speed, length / time);\n\nstatic_assert(speed.dimension == dim_length / dim_time);\n

    Important

    Users should not explicitly define any derived dimensions. Those should always be implicitly created by the framework.

    The multiplication/division on quantity specifications also multiplies/divides their dimensions:

    static_assert((length / time).dimension == dim_length / dim_time);\n

    The dimension equation of isq::dim_length / isq::dim_time results in the derived_dimension<isq::dim_length, per<isq::dim_time>> type.

    "},{"location":"users_guide/framework_basics/design_overview/#quantity-character","title":"Quantity character","text":"

    ISO 80000 explicitly states that quantities (even of the same kind) may have different characters:

    • scalar,
    • vector,
    • tensor.

    The quantity character in the mp-units library is implemented with the quantity_character enumeration:

    enum class quantity_character { scalar, vector, tensor };\n

    Info

    You can read more on quantity characters in the \"Character of a Quantity\" chapter.

    "},{"location":"users_guide/framework_basics/design_overview/#quantity-specification","title":"Quantity specification","text":"

    Dimension is not enough to describe a quantity. This is why the ISO 80000 provides hundreds of named quantity types. It turns out that there are many more quantity types in the ISQ than the named units in the SI.

    This is why the mp-units library introduces a quantity specification entity that stores:

    • dimension,
    • quantity type/name,
    • quantity character,
    • the quantity equation being the recipe to create this quantity (only for derived quantities that specify such a recipe).

    Note

    We know that it might be sometimes confusing to talk about quantities, quantity types/names, and quantity specifications. However, it might be important to notice here that even the ISO 80000 admits that:

    It is customary to use the same term, \"quantity\", to refer to both general quantities, such as length, mass, etc., and their instances, such as given lengths, given masses, etc. Accordingly, we are used to saying both that length is a quantity and that a given length is a quantity by maintaining the specification \u2013 \"general quantity, \\(Q\\)\" or \"individual quantity, \\(Q_\\textsf{a}\\)\" \u2013 implicit and exploiting the linguistic context to remove the ambiguity.

    In the mp-units library, we have a:

    • quantity - implemented as a quantity class template,
    • quantity specification - implemented with a quantity_spec class template that among others identifies a specific quantity type/name.

    For example:

    • isq::length, isq::mass, isq::time, isq::electric_current, isq::thermodynamic_temperature, isq::amount_of_substance, and isq::luminous_intensity are the specifications of base quantities in the ISQ.
    • isq::width, isq::height, isq::radius, and isq::position_vector are only a few of many quantities of a kind length specified in the ISQ.
    • isq::area, isq::speed, isq::moment_of_force are only a few of many derived quantities provided in the ISQ.

    Quantity specification can be defined by the user in one of the following ways:

    C++23C++20Portable
    inline constexpr struct length final : quantity_spec<dim_length> {} length;\ninline constexpr struct height final : quantity_spec<length> {} height;\ninline constexpr struct speed final : quantity_spec<length / time> {} speed;\n
    inline constexpr struct length final : quantity_spec<length, dim_length> {} length;\ninline constexpr struct height final : quantity_spec<height, length> {} height;\ninline constexpr struct speed final : quantity_spec<speed, length / time> {} speed;\n
    QUANTITY_SPEC(length, dim_length);\nQUANTITY_SPEC(height, length);\nQUANTITY_SPEC(speed, length / time);\n

    The quantity equation of isq::length / isq::time results in the derived_quantity_spec<isq::length, per<isq::time>> type.

    "},{"location":"users_guide/framework_basics/design_overview/#unit","title":"Unit","text":"

    A unit is a concrete amount of a quantity that allows us to measure the values of quantities of the same kind and represent the result as a number being the ratio of the two quantities.

    For example:

    • si::second, si::metre, si::kilogram, si::ampere, si::kelvin, si::mole, and si::candela are the base units of the SI.
    • si::kilo<si::metre> is a prefixed unit of length.
    • si::radian, si::newton, and si::watt are examples of named derived units within the SI.
    • non_si::minute is an example of a scaled unit of time.
    • si::si2019::speed_of_light_in_vacuum is a physical constant standardized by the SI in 2019.

    Note

    In the mp-units library, physical constants are also implemented as units.

    A unit can be defined by the user in one of the following ways:

    template<PrefixableUnit U> struct kilo_ : prefixed_unit<\"k\", mag_power<10, 3>, U{}> {};\ntemplate<PrefixableUnit auto U> constexpr kilo_<decltype(U)> kilo;\n\ninline constexpr struct second final : named_unit<\"s\", kind_of<isq::time>> {} second;\ninline constexpr struct minute final : named_unit<\"min\", mag<60> * second> {} minute;\ninline constexpr struct gram   final : named_unit<\"g\", kind_of<isq::mass>> {} gram;\ninline constexpr auto kilogram = kilo<gram>;\ninline constexpr struct newton final : named_unit<\"N\", kilogram * metre / square(second)> {} newton;\n\ninline constexpr struct speed_of_light_in_vacuum final : named_unit<\"c\", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum;\n

    The unit equation of si::metre / si::second results in the derived_unit<si::metre, per<si::second>> type.

    "},{"location":"users_guide/framework_basics/design_overview/#quantity-reference","title":"Quantity reference","text":"

    ISO defines a quantity as:

    Quote

    property of a phenomenon, body, or substance, where the property has a magnitude that can be expressed as a number and a reference

    After that, it says:

    Quote

    A reference can be a measurement unit, a measurement procedure, a reference material, or a combination of such.

    In the mp-units library, a quantity reference provides all the domain-specific metadata for the quantity besides its numerical value:

    • all the data stored in the quantity specification,
    • unit.

    Together with the value of a representation type, it forms a quantity.

    In the library, we have two different ways to provide a reference:

    • every unit with the associated quantity kind is a valid reference,
    • providing a unit to an indexing operator of a quantity specification explicitly instantiates a reference class template with this quantity spec and a unit passed as arguments.

    Note

    All the units of the SI have associated quantity kinds and may serve as a reference.

    For example:

    • si::metre is defined in the SI as a unit of isq::length and thus can be used as a reference to instantiate a quantity of length (e.g., 42 * m).
    • The expression isq::height[m] results with reference<isq::height, si::metre>, which can be used to instantiate a quantity of isq::height with a unit of si::metre (e.g., 42 * isq::height[m]).
    "},{"location":"users_guide/framework_basics/design_overview/#quantity-representation","title":"Quantity representation","text":"

    Quantity representation defines the type used to store the numerical value of a quantity. Such a type should be of a specific quantity character provided in the quantity specification.

    Note

    By default, all floating-point and integral (besides bool) types are treated as scalars.

    "},{"location":"users_guide/framework_basics/design_overview/#quantity","title":"Quantity","text":"

    ISO defines a quantity as:

    Quote

    property of a phenomenon, body, or substance, where the property has a magnitude that can be expressed as a number and a reference

    This is why a quantity class template is defined in the library as:

    template<Reference auto R,\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity;\n

    Its value can be easily created by multiplying/dividing the numerical value and a reference.

    For example:

    • All of 42 * m, 42 * si::metre, 42 * isq::height[m], and isq::height(42 * m) create a quantity.
    • A quantity type can also be specified explicitly (e.g., quantity<si::metre, int>, quantity<isq::height[m]>).
    "},{"location":"users_guide/framework_basics/design_overview/#point-origin","title":"Point origin","text":"

    In the affine space theory, the point origin specifies where the \"zero\" of our measurement's scale is.

    In the mp-units library, we have two types of point origins:

    • absolute - defines an absolute \"zero\" for our point,
    • relative - defines an origin that has some \"offset\" relative to an absolute point.

    For example:

    • the absolute point origin can be defined in the following way:
    inline constexpr struct absolute_zero final : absolute_point_origin<isq::thermodynamic_temperature> {} absolute_zero;\n
    • the relative point origin can be defined in the following way:
    inline constexpr struct ice_point final : relative_point_origin<absolute_zero + 273'150 * milli<kelvin>> {} ice_point;\n
    "},{"location":"users_guide/framework_basics/design_overview/#quantity-point","title":"Quantity point","text":"

    Quantity point implements a point in the affine space theory.

    In the mp-units library, the quantity point is implemented as:

    template<Reference auto R,\n         PointOriginFor<get_quantity_spec(R)> auto PO,\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity_point;\n

    Its value can be easily created by adding/subtracting the quantity with a point origin.

    For example:

    • The following specifies a quantity point defined in terms of an ice_point provided in the previous example:
    constexpr auto room_reference_temperature = ice_point + delta<isq::Celsius_temperature[deg_C]>(21);\n
    "},{"location":"users_guide/framework_basics/dimensionless_quantities/","title":"Dimensionless Quantities","text":"

    The quantities we discussed so far always had some specific type and physical dimension. However, this is not always the case. While performing various computations, we sometimes end up with so-called \"dimensionless\" quantities, which ISO defines as quantities of dimension one:

    ISO/IEC Guide 99

    • Quantity for which all the exponents of the factors corresponding to the base quantities in its quantity dimension are zero.
    • The measurement units and values of quantities of dimension one are numbers, but such quantities convey more information than a number.
    • Some quantities of dimension one are defined as the ratios of two quantities of the same kind.
    • Numbers of entities are quantities of dimension one.
    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-two-quantities-of-the-same-kind","title":"Dividing two quantities of the same kind","text":"

    Dividing two quantities of the same kind always results in a quantity of dimension one. However, depending on what type of quantities we divide or what their units are, we may end up with slightly different results.

    Note

    In mp-units, dividing two quantities of the same dimension always results in a quantity with the dimension being dimension_one. This is often different for other physical units libraries, which may return a raw representation type for such cases. A raw value is also always returned from the division of two std::chrono::duration objects.

    To read more about the reasoning for this design decision, please check our FAQ.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-quantities-of-the-same-type","title":"Dividing quantities of the same type","text":"

    First, let's analyze what happens if we divide two quantities of the same type:

    constexpr QuantityOf<dimensionless> auto q = isq::height(200 * m) / isq::height(50 * m);\n

    In such a case, we end up with a dimensionless quantity that has the following properties:

    static_assert(q.quantity_spec == dimensionless);\nstatic_assert(q.dimension == dimension_one);\nstatic_assert(q.unit == one);\n

    In case we would like to print its value, we would see a raw value of 4 in the output with no unit being printed.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-quantities-of-different-types","title":"Dividing quantities of different types","text":"

    Now let's see what happens if we divide quantities of the same dimension and unit but which have different quantity types:

    constexpr QuantityOf<dimensionless> auto q = isq::work(200 * J) / isq::heat(50 * J);\n

    Again we end up with dimension_one and one, but this time:

    static_assert(q.quantity_spec == isq::work / isq::heat);\n

    As shown above, the result is not of a dimensionless type anymore. Instead, we get a quantity type derived from the performed quantity equation. According to the ISQ, work divided by heat is the recipe for the thermodynamic efficiency quantity, thus:

    static_assert(implicitly_convertible(q.quantity_spec, isq::efficiency_thermodynamics));\n

    Note

    The quantity of isq::efficiency_thermodynamics is of a kind dimensionless, so it is implicitly convertible to dimensionless and satisfies the QuantityOf<dimensionless> concept.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-quantities-of-different-units","title":"Dividing quantities of different units","text":"

    Now, let's see what happens when we divide two quantities of the same type but different units:

    constexpr QuantityOf<dimensionless> auto q = isq::height(4 * km) / isq::height(2 * m);\n

    This time, we still get a quantity of the dimensionless type with a dimension_one as its dimension. However, the resulting unit is not one anymore:

    static_assert(q.unit == mag_power<10, 3> * one);\n

    In case we would print the text output of this quantity, we would not see a raw value of 2000, but 2 km/m.

    First, it may look surprising, but this is consistent with dividing quantities of different dimensions. For example, if we divide 4 * km / 2 * s, we do not expect km to be \"expanded\" to m before the division, right? We would expect the result of 2 km/s, which is exactly what we get when we divide quantities of the same kind.

    This is a compelling feature that allows us to express huge or tiny ratios without the need for big and expensive representation types. With this, we can easily define things like a Hubble's constant that uses a unit that is proportional to the ratio of kilometers per megaparsecs, which are both units of length:

    inline constexpr struct hubble_constant final :\n    named_unit<{u8\"H\u2080\", \"H_0\"}, mag_ratio<701, 10> * si::kilo<si::metre> / si::second / si::mega<parsec>> {} hubble_constant;\n
    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#counts-of-things","title":"Counts of things","text":"

    Another important use case for dimensionless quantities is to provide strong types for counts of things. For example:

    • ISO-80000-3 provides a rotation quantity defined as the number of revolutions,
    • IEC-80000-6 provides a number of turns in a winding quantity,
    • IEC-80000-13 provides a Hamming distance quantity defined as the number of digit positions in which the corresponding digits of two words of the same length are different.

    Thanks to assigning strong names to such quantities, later on, they can be explicitly used as arguments in the quantity equations of other quantities deriving from them.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#predefined-units-of-the-dimensionless-quantity","title":"Predefined units of the dimensionless quantity","text":"

    As we observed above, the most common unit for dimensionless quantities is one. It has the ratio of 1 and does not output any textual symbol.

    Important: one is an identity

    A unit one is special in the entire type system of units as it is considered to be an identity operand in the unit expression templates. This means that, for example:

    static_assert(one * one == one);\nstatic_assert(one * si::metre == si::metre);\nstatic_assert(si::metre / si::metre == one);\n

    The same is also true for dimension_one and dimensionless in the domains of dimensions and quantity specifications.

    Besides the unit one, there are a few other scaled units predefined in the library for usage with dimensionless quantities:

    inline constexpr struct percent final : named_unit<\"%\", mag_ratio<1, 100> * one> {} percent;\ninline constexpr struct per_mille final : named_unit<{u8\"\u2030\", \"%o\"}, mag_ratio<1, 1000> * one> {} per_mille;\ninline constexpr struct parts_per_million final : named_unit<\"ppm\", mag_ratio<1, 1'000'000> * one> {} parts_per_million;\ninline constexpr auto ppm = parts_per_million;\n
    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#superpowers-of-the-unit-one","title":"Superpowers of the unit one","text":"

    Quantities of the unit one are the only ones that are:

    • implicitly constructible from the raw value,
    • explicitly convertible to a raw value,
    • comparable to a raw value.
    quantity<one> inc(quantity<one> q) { return q + 1; }\nvoid legacy(double) { /* ... */ }\n\nif (auto q = inc(42); q != 0)\n  legacy(static_cast<int>(q));\n

    This property also expands to usual arithmetic operators.

    Note

    Those rules do not apply to all the dimensionless quantities. It would be unsafe and misleading to allow such operations on units with a magnitude different than 1 (e.g., percent or radian).

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#angular-quantities","title":"Angular quantities","text":"

    Special, often controversial, examples of dimensionless quantities are an angular measure and solid angular measure quantities that are defined in the ISQ to be the result of a division of \\(arc\\; length / radius\\) and \\(area / radius^2\\) respectively. Moreover, ISQ also explicitly states that both can be expressed in the unit one. This means that both angular measure and solid angular measure should be of a kind dimensionless.

    On the other hand, ISQ also specifies that a unit radian can be used for angular measure, and a unit steradian can be used for solid angular measure. Those should not be mixed or used to express other types of dimensionless quantities. This means that both angular measure and solid angular measure should also be quantity kinds by themselves.

    Note

    Many people claim that angle being a dimensionless quantity is a bad idea. There are proposals submitted to make an angle a base quantity and rad to become a base unit. More on this topic can be found in the \"Strong Angular System\" chapter.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#radians-and-degrees-support","title":"Radians and degrees support","text":"

    Thanks to the usage of magnitudes the library provides efficient strong types for all angular types. This means that with the built-in support for magnitudes of \\(\\pi\\) we can provide accurate conversions between radians and degrees. The library also provides common trigonometric functions for angular quantities:

    using namespace mp_units::si::unit_symbols;\nusing mp_units::angular::unit_symbols::rad;\nusing mp_units::angular::unit_symbols::deg;\nusing mp_units::angular::unit_symbols::grad;\n\nquantity speed = 110 * km / h;\nquantity rate_of_climb = -0.63657 * m / s;\nquantity glide_ratio = speed / -rate_of_climb;\nquantity glide_angle = angular::asin(1 / glide_ratio);\n\nstd::println(\"Glide ratio: {::N[.1f]}\", glide_ratio.in(one));\nstd::println(\"Glide angle:\");\nstd::println(\" - {::N[.4f]}\", glide_angle.in(rad));\nstd::println(\" - {::N[.2f]}\", glide_angle.in(deg));\nstd::println(\" - {::N[.2f]}\", glide_angle.in(grad));\n

    The above program prints:

    Glide ratio: 48.0\nGlide angle:\n - 0.0208 rad\n - 1.19\u00b0\n - 1.33\u1d4d\n

    Note

    In the production code the above speed and rate_of_climb quantities should probably be modelled as separate typed quantities of the same kind.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#nested-quantity-kinds","title":"Nested quantity kinds","text":"

    Angular quantities are not the only ones with such a \"strange\" behavior. Another but a similar case is a storage capacity quantity specified in IEC-80000-13 that again allows expressing it in both one and bit units.

    Those cases make dimensionless quantities an exceptional tree in the library. This quantity hierarchy contains more than one quantity kind and more than one unit in its tree:

    flowchart TD\n    dimensionless[\"<b>dimensionless</b><br>[one]\"]\n    dimensionless --- rotation[\"<b>rotation</b>\"]\n    dimensionless --- thermodynamic_efficiency[\"<b>thermodynamic_efficiency</b><br><i>(work / heat)</i>\"]\n    dimensionless --- angular_measure[\"<b>angular_measure</b><br><i>(arc_length / radius)</i><br>[rad]\"]\n    angular_measure --- rotational_displacement[\"<b>rotational_displacement</b><br><i>(path_length / radius)</i>\"]\n    angular_measure --- phase_angle[\"<b>phase_angle</b>\"]\n    dimensionless --- solid_angular_measure[\"<b>solid_angular_measure</b><br><i>(area / pow<2>(radius))</i><br>[sr]\"]\n    dimensionless --- drag_factor[\"<b>drag_factor</b><br><i>(drag_force / (mass_density * pow<2>(speed) * area))</i>\"]\n    dimensionless --- storage_capacity[\"<b>storage_capacity</b><br>[bit]\"] --- equivalent_binary_storage_capacity[\"<b>equivalent_binary_storage_capacity</b>\"]\n    dimensionless --- ...

    To provide such support in the library, we provided an is_kind specifier that can be appended to the quantity specification:

    C++23C++20Portable
    inline constexpr struct angular_measure final : quantity_spec<dimensionless, arc_length / radius, is_kind> {} angular_measure;\ninline constexpr struct solid_angular_measure final : quantity_spec<dimensionless, area / pow<2>(radius), is_kind> {} solid_angular_measure;\ninline constexpr struct storage_capacity final : quantity_spec<dimensionless, is_kind> {} storage_capacity;\n
    inline constexpr struct angular_measure final : quantity_spec<angular_measure, dimensionless, arc_length / radius, is_kind> {} angular_measure;\ninline constexpr struct solid_angular_measure final : quantity_spec<solid_angular_measure, dimensionless, area / pow<2>(radius), is_kind> {} solid_angular_measure;\ninline constexpr struct storage_capacity final : quantity_spec<storage_capacity, dimensionless, is_kind> {} storage_capacity;\n
    QUANTITY_SPEC(angular_measure, dimensionless, arc_length / radius, is_kind);\nQUANTITY_SPEC(solid_angular_measure, dimensionless, area / pow<2>(radius), is_kind);\nQUANTITY_SPEC(storage_capacity, dimensionless, is_kind);\n

    With the above, we can constrain radian, steradian, and bit to be allowed for usage with specific quantity kinds only:

    inline constexpr struct radian final : named_unit<\"rad\", metre / metre, kind_of<isq::angular_measure>> {} radian;\ninline constexpr struct steradian final : named_unit<\"sr\", square(metre) / square(metre), kind_of<isq::solid_angular_measure>> {} steradian;\ninline constexpr struct bit final : named_unit<\"bit\", one, kind_of<storage_capacity>> {} bit;\n

    but still allow the usage of one and its scaled versions for such quantities.

    "},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/","title":"Faster-than-lightspeed Constants","text":"

    In most libraries, physical constants are implemented as constant (possibly constexpr) quantity values. Such an approach has some disadvantages, often affecting the run time performance and causing a loss of precision.

    "},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/#simplifying-constants-in-an-equation","title":"Simplifying constants in an equation","text":"

    When dealing with equations involving physical constants, they often occur more than once in an expression. Such a constant may appear both in a numerator and denominator of a quantity equation. As we know from fundamental physics, we can simplify such an expression by striking a constant out of the equation. Supporting such behavior allows a faster runtime performance and often a better precision of the resulting value.

    "},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/#physical-constants-as-units","title":"Physical constants as units","text":"

    The mp-units library allows and encourages the implementation of physical constants as regular units. With that, the constant's value is handled at compile-time, and under favorable circumstances, it can be simplified in the same way as all other repeated units do. If it is not simplified, the value is stored in a type, and the expensive multiplication or division operations can be delayed in time until a user selects a specific unit to represent/print the data.

    Such a feature often also allows using simpler or faster representation types in the equation. For example, instead of always having to multiply a small integral value with a big floating-point constant number, we can just use the integral type all the way. Only in case a constant will not simplify in the equation, and the user will require a specific unit, such a multiplication will be lazily invoked, and the representation type will need to be expanded to facilitate that. With that, addition, subtractions, multiplications, and divisions will always be the fastest - compiled away or done in out-of-order execution.

    To benefit from all of the above, in the mp-units library, SI defining and other constants are implemented as units in the following way:

    namespace si {\n\nnamespace si2019 {\n\ninline constexpr struct speed_of_light_in_vacuum final :\n  named_unit<\"c\", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum;\n\n}  // namespace si2019\n\ninline constexpr struct magnetic_constant final :\n  named_unit<{u8\"\u03bc\u2080\", \"u_0\"}, mag<4> * mag<\u03c0> * mag_power<10, -7> * henry / metre> {} magnetic_constant;\n\n}  // namespace mp_units::si\n
    "},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/#usage-examples","title":"Usage examples","text":"

    With the above definitions, we can calculate vacuum permittivity as:

    constexpr auto permeability_of_vacuum = 1. * si::magnetic_constant;\nconstexpr auto speed_of_light_in_vacuum = 1 * si::si2019::speed_of_light_in_vacuum;\n\nQuantityOf<isq::permittivity_of_vacuum> auto q = 1 / (permeability_of_vacuum * pow<2>(speed_of_light_in_vacuum));\n\nstd::println(\"permittivity of vacuum = {} = {::N[.3e]}\", q, q.in(F / m));\n

    The above first prints the following:

    permittivity of vacuum = 1  \u03bc\u2080\u207b\u00b9 c\u207b\u00b2 = 8.854e-12 F/m\n

    As we can clearly see, all the calculations above were just about multiplying and dividing the number 1 with the rest of the information provided as a compile-time type. Only when a user wants a specific SI unit as a result, the unit ratios are lazily resolved.

    Another similar example can be an equation for total energy:

    QuantityOf<isq::mechanical_energy> auto total_energy(QuantityOf<isq::momentum> auto p,\n                                                     QuantityOf<isq::mass> auto m,\n                                                     QuantityOf<isq::speed> auto c)\n{\n  return isq::mechanical_energy(sqrt(pow<2>(p * c) + pow<2>(m * pow<2>(c))));\n}\n
    constexpr auto GeV = si::giga<si::electronvolt>;\nconstexpr QuantityOf<isq::speed> auto c = 1. * si::si2019::speed_of_light_in_vacuum;\nconstexpr auto c2 = pow<2>(c);\n\nconst auto p1 = isq::momentum(4. * GeV / c);\nconst QuantityOf<isq::mass> auto m1 = 3. * GeV / c2;\nconst auto E = total_energy(p1, m1, c);\n\nstd::cout << \"in `GeV` and `c`:\\n\"\n          << \"p = \" << p1 << \"\\n\"\n          << \"m = \" << m1 << \"\\n\"\n          << \"E = \" << E << \"\\n\";\n\nconst auto p2 = p1.in(GeV / (m / s));\nconst auto m2 = m1.in(GeV / pow<2>(m / s));\nconst auto E2 = total_energy(p2, m2, c).in(GeV);\n\nstd::cout << \"\\nin `GeV`:\\n\"\n          << \"p = \" << p2 << \"\\n\"\n          << \"m = \" << m2 << \"\\n\"\n          << \"E = \" << E2 << \"\\n\";\n\nconst auto p3 = p1.in(kg * m / s);\nconst auto m3 = m1.in(kg);\nconst auto E3 = total_energy(p3, m3, c).in(J);\n\nstd::cout << \"\\nin SI base units:\\n\"\n          << \"p = \" << p3 << \"\\n\"\n          << \"m = \" << m3 << \"\\n\"\n          << \"E = \" << E3 << \"\\n\";\n

    The above prints the following:

    in `GeV` and `c`:\np = 4 GeV/c\nm = 3 GeV/c\u00b2\nE = 5 GeV\n\nin `GeV`:\np = 1.33426e-08 GeV s/m\nm = 3.33795e-17 GeV s\u00b2/m\u00b2\nE = 5 GeV\n\nin SI base units:\np = 2.13771e-18 kg m/s\nm = 5.34799e-27 kg\nE = 8.01088e-10 J\n
    "},{"location":"users_guide/framework_basics/generic_interfaces/","title":"Generic Interfaces","text":"

    Using a concrete unit in the interface often makes a lot of sense. It is especially useful if we store the data internally in the object. In such a case, we have to select a specific unit anyway.

    For example, let's consider a simple storage tank:

    class StorageTank {\n  quantity<horizontal_area[m2]> base_;\n  quantity<isq::height[m]> height_;\n  quantity<isq::mass_density[kg / m3]> density_ = air_density;\npublic:\n  constexpr StorageTank(const quantity<horizontal_area[m2]>& base, const quantity<isq::height[m]>& height) :\n      base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n

    As the quantities provided in the function's interface are then stored in the class, there is probably no sense in using generic interfaces here.

    "},{"location":"users_guide/framework_basics/generic_interfaces/#the-issues-with-unit-specific-interfaces","title":"The issues with unit-specific interfaces","text":"

    However, in many cases, using a specific unit in the interface is counterproductive. Let's consider the following function:

    quantity<km / h> avg_speed(quantity<km> distance, quantity<h> duration)\n{\n  return distance / duration;\n}\n

    Everything seems fine for now. It also works great if we call it with:

    quantity<km / h> s1 = avg_speed(220 * km, 2 * h);\n

    However, if the user starts doing the following:

    quantity<mi / h> s2 = avg_speed(140 * mi, 2 * h);\nquantity<m / s> s3 = avg_speed(20 * m, 2 * s);\n

    some issues start to be clearly visible:

    1. The arguments must be converted to units mandated by the function's parameters at each call. This involves potentially expensive multiplication/division operations at runtime.
    2. After the function returns the speed in a unit of km/h, another potentially expensive multiplication/division operations must be performed to convert the resulting quantity into a unit being the derived unit of the initial function's arguments.
    3. Besides the obvious runtime cost, some unit conversions may result in a value truncation, which means that the result will not be exactly equal to a direct division of the function's arguments.
    4. We have to use a floating-point representation type (the quantity class template by default uses double as a representation type) which is considered value-preserving. Trying to use an integral type in this scenario will work only for s1, while s2 and s3 will fail to compile. Failing to compile is a good thing here as the library tries to prevent the user from doing a clearly wrong thing. To make the code compile, the user needs to use dedicated value_cast or force_in like this:

      quantity<isq::speed[mi / h]> s2 = avg_speed(value_cast<km>(140 * mi), 2 * h);\nquantity<isq::speed[m / s]> s3 = avg_speed((20 * m).force_in(km), (2 * s).force_in(h));\n

      but the above will obviously provide an incorrect behavior (e.g., division by 0 in the evaluation of s3).

    "},{"location":"users_guide/framework_basics/generic_interfaces/#a-naive-solution","title":"A naive solution","text":"

    A naive solution here would be to implement the function as an unconstrained function template:

    auto avg_speed(auto distance, auto duration)\n{\n  return distance / duration;\n}\n

    Beware, this is not a good solution. The above code is too generic. Such a function template accepts everything:

    • quantities of other types
      • the compiler will not prevent accidental reordering of the function's arguments,
      • quantities of different types can be passed as well,
    • plain double arguments,
    • std::vector and std::lock_guard will be accepted as well (of course, this will fail in the instantiation of a function's body later in the compilation process).

    Note

    The usage of auto instead of a function parameter type is a C++20 feature. It makes such a code a function template where the type of such a parameter will be deduced during the template instantiation process from the argument type passed by the user.

    "},{"location":"users_guide/framework_basics/generic_interfaces/#constraining-function-template-arguments-with-concepts","title":"Constraining function template arguments with concepts","text":"

    Much better generic code can be implemented using basic concepts provided with the library:

    Original template notationThe shorthand notationTerse notation
    template<typename Distance, typename Duration>\n  requires QuantityOf<Distance, isq::length> && QuantityOf<Duration, isq::time>\nauto avg_speed(Distance distance, Duration duration)\n{\n  return isq::speed(distance / duration);\n}\n
    template<QuantityOf<isq::length> Distance, QuantityOf<isq::time> Duration>\nauto avg_speed(Distance distance, Duration duration)\n{\n  return isq::speed(distance / duration);\n}\n
    auto avg_speed(QuantityOf<isq::length> auto distance,\n               QuantityOf<isq::time> auto duration)\n{\n  return isq::speed(distance / duration);\n}\n

    This explicitly states that the arguments passed by the user must not only satisfy a Quantity concept, but also their quantity specification must be implicitly convertible to isq::length and isq::time accordingly. This no longer leaves room for error while still allowing the compiler to generate the most efficient code.

    Tip

    Please note that now it is safe just to use integral types all the way which again improves the runtime performance as the multiplication/division operations are often faster on the integral rather than floating-point types.

    "},{"location":"users_guide/framework_basics/generic_interfaces/#constraining-function-template-return-type","title":"Constraining function template return type","text":"

    The above function template resolves all of the issues described before. However, we can do even better here by additionally constraining the return type:

    QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto distance,\n                                      QuantityOf<isq::time> auto duration)\n{\n  return isq::speed(distance / duration);\n}\n

    Doing so has two important benefits:

    1. It informs the users of our interface about what to expect to be the result of a function invocation. It is superior to just returning auto, which does not provide any hint about the thing being returned there.
    2. Such a concept constrains the type returned from the function. This means that it works as a unit test to verify if our function actually performs what it is supposed to do. If there is an error in quantity equations, we will learn about it right away.
    "},{"location":"users_guide/framework_basics/generic_interfaces/#constraining-a-variable-on-the-stack","title":"Constraining a variable on the stack","text":"

    If we know precisely what the function does in its internals and if we know the exact argument types passed to such a function, we often know the exact type that will be returned from its invocation.

    However, if we care about performance, we should often use the generic interfaces described in this chapter. A side effect is that we sometimes are unsure about the return type. Even if we know it today, it might change a week from now due to some code refactoring.

    In such cases, we can again use auto to denote the type:

    auto s1 = avg_speed(220 * km, 2 * h);\nauto s2 = avg_speed(140 * mi, 2 * h);\nauto s3 = avg_speed(20 * m, 2 * s);\n

    or benefit from CTAD:

    quantity s1 = avg_speed(220 * km, 2 * h);\nquantity s2 = avg_speed(140 * mi, 2 * h);\nquantity s3 = avg_speed(20 * m, 2 * s);\n

    In both cases, it is probably OK to do so as the avg_speed function name explicitly provides the information on what to expect as a result.

    In other scenarios where the returned quantity type is not so obvious, it is again helpful to constrain the type with a concept like so:

    QuantityOf<isq::speed> auto s1 = avg_speed(220 * km, 2 * h);\nQuantityOf<isq::speed> auto s2 = avg_speed(140 * mi, 2 * h);\nQuantityOf<isq::speed> auto s3 = avg_speed(20 * m, 2 * s);\n

    The above explicitly provides additional information about the quantity we are dealing with in the code, and it serves as a unit test checking if the \"thing\" returned from a function is actually what we expected here.

    Note

    The QuantityOf and QuantityPointOf concepts are probably the most useful, but there are a few more to play with. A list of all the concepts can be found in the Basic Concepts chapter.

    "},{"location":"users_guide/framework_basics/interface_introduction/","title":"Interface Introduction","text":""},{"location":"users_guide/framework_basics/interface_introduction/#new-style-of-definitions","title":"New style of definitions","text":"

    The mp-units library decided to use a rather unusual pattern to define entities. Here is how we define metre and second SI base units:

    inline constexpr struct metre final : named_unit<\"m\", kind_of<isq::length>> {} metre;\ninline constexpr struct second final : named_unit<\"s\", kind_of<isq::time>> {} second;\n

    Please note that the above reuses the same identifier for a type and its value. The rationale behind this is that:

    • Users always work with values and never have to spell such a type name.
    • The types appear in the compilation errors and during debugging.

    Important

    To improve compiler errors' readability and make it easier to correlate them with a user's written code, a new idiom in the library is to use the same identifier for a type and its instance.

    Also, to prevent possible issues in compile-time logic, all of the library's entities must be marked final. This prevents the users to derive own strong types from them, which would prevent expression template simplification of equivalent entities.

    "},{"location":"users_guide/framework_basics/interface_introduction/#strong-types-instead-of-aliases","title":"Strong types instead of aliases","text":"

    Let's look again at the above units definitions. Another important point to notice is that all the types describing entities in the library are short, nicely named identifiers that derive from longer, more verbose class template instantiations. This is really important to improve the user experience while debugging the program or analyzing the compilation error.

    Note

    Such a practice is rare in the industry. Some popular C++ physical units libraries generate enormously long error messages where even only the first line failed to fit on a slide with a tiny font.

    "},{"location":"users_guide/framework_basics/interface_introduction/#entities-composability","title":"Entities composability","text":"

    Many physical units libraries (in C++ or any other programming language) assign strong types to library entities (e.g., derived units). While metre_per_second as a type may not look too scary, consider, for example, units of angular momentum. If we followed this path, its coherent unit would look like kilogram_metre_sq_per_second. Now, consider how many scaled versions of this unit you would predefine in the library to ensure that all users are happy with your choice? How expensive would it be from the implementation point of view? What about potential future standardization efforts?

    This is why in mp-units, we put a strong requirement to make everything as composable as possible. For example, to create a quantity with a unit of speed, one may write:

    quantity<si::metre / si::second> q;\n

    In case we use such a unit often and would prefer to have a handy helper for it, we can always do something like this:

    constexpr auto metre_per_second = si::metre / si::second;\nquantity<metre_per_second> q;\n

    or choose any shorter identifier of our choice.

    Coming back to the angular momentum case, thanks to the composability of units, a user can create such a quantity in the following way:

    using namespace mp_units::si::unit_symbols;\nauto q = la_vector{1, 2, 3} * isq::angular_momentum[kg * m2 / s];\n

    It is a much better solution. It is terse and easy to understand. Please also notice how easy it is to obtain any scaled version of such a unit (e.g., mg * square(mm) / min) without having to introduce hundreds of types to predefine them.

    "},{"location":"users_guide/framework_basics/interface_introduction/#value-based-equations","title":"Value-based equations","text":"

    The mp-units library is based on C++20, significantly improving user experience. One of such improvements is the usage of value-based equations.

    As we have learned above, the entities are being used as values in the code, and they compose. Moreover, derived entities can be defined in the library using such value-based equations. This is a huge improvement compared to what we can find in other physical units libraries or what we have to deal with when we want to write some equations for std::ratio.

    For example, below are a few definitions of the SI derived units showing the power of C++20 extensions to Non-Type Template Parameters, which allow us to directly pass a result of the value-based unit equation to a class template definition:

    inline constexpr struct newton final : named_unit<\"N\", kilogram * metre / square(second)> {} newton;\ninline constexpr struct pascal final : named_unit<\"Pa\", newton / square(metre)> {} pascal;\ninline constexpr struct joule  final : named_unit<\"J\", newton * metre> {} joule;\n
    "},{"location":"users_guide/framework_basics/interface_introduction/#expression-templates","title":"Expression templates","text":"

    The previous chapter provided a rationale for not having predefined types for derived entities. In many libraries, such an approach results in long and unreadable compilation errors, as framework-generated types are typically far from being easy to read and understand.

    The mp-units library greatly improves the user experience by extensively using expression templates. Such expressions are used consistently throughout the entire library to describe the results of:

    • dimension equation - the result is put into the derived_dimension<> class template
    • quantity equation - the result is put into the derived_quantity_spec<> class template
    • unit equation - the result is put into the derived_unit<> class template

    For example, if we take the above-defined base units and put the results of their division into the quantity class template like this:

    quantity<metre / second> q;\n

    we will observe the following type in the debugger

    (gdb) ptype q\ntype = class mp_units::quantity<mp_units::derived_unit<metre, mp_units::per<second>>(), double> [with Rep = double] {\n

    The same type identifier will be visible in the compilation error (in case it happens).

    Important

    Expressions templates are extensively used throughout the library to improve the readability of the resulting types.

    "},{"location":"users_guide/framework_basics/interface_introduction/#identities","title":"Identities","text":"

    As mentioned above, equations can be performed on dimensions, quantities, and units. Each such domain must introduce an identity object that can be used in the resulting expressions. Here is the list of identities used in the library:

    Domain Concept Identity Dimension dimension_one QuantitySpec dimensionless Unit one

    In the equations, a user can explicitly refer to an identity object. For example:

    constexpr auto my_unit = one / second;\n

    Note

    Another way to achieve the same result is to call an inverse() function:

    constexpr auto my_unit = inverse(second);\n

    Both cases will result in the same expression template being generated and put into the wrapper class template.

    "},{"location":"users_guide/framework_basics/interface_introduction/#supported-operations-and-their-results","title":"Supported operations and their results","text":"

    There are only a few operations that one can do on such entities, and the result of each of them has its unique representation in the library:

    Operation Resulting template expression arguments A * B A, B B * A A, B A * A power<A, 2> {identity} * A A A * {identity} A A / B A, per<B> A / A {identity} A / {identity} A {identity} / A {identity}, per<A> pow<2>(A) power<A, 2> pow<2>({identity}) {identity} sqrt(A) or pow<1, 2>(A) power<A, 1, 2> sqrt({identity}) or pow<1, 2>({identity}) {identity}"},{"location":"users_guide/framework_basics/interface_introduction/#simplifying-the-resulting-expression-templates","title":"Simplifying the resulting expression templates","text":"

    To limit the length and improve the readability of generated types, there are many rules to simplify the resulting expression template.

    1. Ordering

      The resulting comma-separated arguments of multiplication are always sorted according to a specific predicate. This is why:

      static_assert(A * B == B * A);\nstatic_assert(std::is_same_v<decltype(A * B), decltype(B * A)>);\n

      This is probably the most important of all the steps, as it allows comparing types and enables the rest of the simplification rules.

    2. Aggregation

      In case two of the same identifiers are found next to each other on the argument list they will be aggregated in one entry:

      Before After A, A power<A, 2> A, power<A, 2> power<A, 3> power<A, 1, 2>, power<A, 2> power<A, 5, 2> power<A, 1, 2>, power<A, 1, 2> A
    3. Simplification

      In case two of the same identifiers are found in the numerator and denominator argument lists; they are being simplified into one entry:

      Before After A, per<A> {identity} power<A, 2>, per<A> A power<A, 3>, per<A> power<A, 2> A, per<power<A, 2>> {identity}, per<A>

      It is important to notice here that only the elements with exactly the same type are being simplified. This means that, for example, m/m results in one, but km/m will not be simplified. The resulting derived unit will preserve both symbols and their relative magnitude. This allows us to properly print symbols of some units or constants that require such behavior. For example, the Hubble constant is expressed in km\u22c5s\u207b\u00b9\u22c5Mpc\u207b\u00b9, where both km and Mpc are units of length.

      Also, to prevent possible issues in compile-time logic, all of the library's entities must be marked final. This prevents the users to derive own strong types from them, which would prevent expression template simplification of equivalent entities.

    4. Repacking

      In case an expression uses two results of other operations, the components of its arguments are repacked into one resulting type and simplified there.

      For example, assuming:

      constexpr auto X = A / B;\n

      then:

      Operation Resulting template expression arguments X * B A X * A power<A, 2>, per<B> X * X power<A, 2>, per<power<B, 2>> X / X {identity} X / A {identity}, per<B> X / B A, per<power<B, 2>>
    "},{"location":"users_guide/framework_basics/interface_introduction/#example","title":"Example","text":"

    Thanks to all of the features described above, a user may write the code like this one:

    using namespace mp_units::si::unit_symbols;\nquantity speed = 60. * isq::speed[km / h];\nquantity duration = 8 * s;\nquantity acceleration = speed / duration;\nstd::cout << \"acceleration: \" << acceleration << \" (\" << acceleration.in(m / s2) << \")\\n\";\n

    The acceleration quantity, being the result of the above code, has the following type (after stripping the mp_units namespace for brevity):

    quantity<reference<derived_quantity_spec<isq::speed, per<isq::time>>{}, derived_unit<si::kilo_<si::metre{}>, per<non_si::hour, si::second>>{}>{}, int>\n

    and the text output presents:

    acceleration: 7.5 km h\u207b\u00b9 s\u207b\u00b9 (2.08333 m/s\u00b2)\n
    "},{"location":"users_guide/framework_basics/obtaining_metadata/","title":"Obtaining Metadata","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#quantity-spec","title":"quantity spec","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#unit","title":"unit","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#reference","title":"reference","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#quantity","title":"quantity","text":""},{"location":"users_guide/framework_basics/quantity_arithmetics/","title":"Quantity Arithmetics","text":""},{"location":"users_guide/framework_basics/quantity_arithmetics/#quantity-is-a-numeric-wrapper","title":"quantity is a numeric wrapper","text":"

    If we think about it, the quantity class template is just a \"smart\" numeric wrapper. It exposes properly constrained set of arithmetic operations on one or two operands.

    Important: quantity propagates the underlying interface

    Every single arithmetic operator is exposed by the quantity class template only if the underlying representation type provides it as well, and when its implementation has proper semantics (e.g., returns a reasonable type).

    For example, in the following code, -a will compile only if MyInt exposes such an operation as well:

    quantity a = MyInt{42} * m;\nquantity b = -a;\n

    Assuming that:

    • q is our quantity,
    • qi is a quantity implicitly convertible to q,
    • qk is a quantity of the same kind as q,
    • q1 is a quantity of dimension_one with the unit one,
    • qq is any other quantity,
    • number is a value of a type \"compatible\" with q's representation type,

    here is the list of all the supported operators:

    • unary:
      • +q
      • -q
      • ++q
      • q++
      • --q
      • q--
    • compound assignment:
      • q += qi
      • q -= qi
      • q %= qi
      • q *= number
      • q *= q1
      • q /= number
      • q /= q1
    • binary:
      • q + qk
      • q - qk
      • q % qk
      • q * qq
      • q * number
      • number * q
      • q / qq
      • q / number
      • number / q
    • ordering and comparison:
      • q == qk
      • q <=> qk

    As we can see, there are plenty of operations one can do on a value of a quantity type. As most of them are obvious, in the following chapters, we will discuss only the most important or non-trivial aspects of quantity arithmetics.

    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#addition-and-subtraction","title":"Addition and subtraction","text":"

    Quantities can easily be added or subtracted from each other:

    static_assert(1 * m + 1 * m == 2 * m);\nstatic_assert(2 * m - 1 * m == 1 * m);\nstatic_assert(isq::height(1 * m) + isq::height(1 * m) == isq::height(2 * m));\nstatic_assert(isq::height(2 * m) - isq::height(1 * m) == isq::height(1 * m));\n

    The above uses the same types for LHS, RHS, and the result, but in general, we can add, subtract, or compare the values of any quantity type as long as both quantities are of the same kind. The result of such an operation will be the common type of the arguments:

    static_assert(1 * km + 1.5 * m == 1001.5 * m);\nstatic_assert(isq::height(1 * m) + isq::width(1 * m) == isq::length(2 * m));\nstatic_assert(isq::height(2 * m) - isq::distance(0.5 * m) == 1.5 * m);\nstatic_assert(isq::radius(1 * m) - 0.5 * m == isq::radius(0.5 * m));\n

    Note

    Please note that for the compound assignment operators, we always need to end up with the left-hand-side argument type:

    static_assert((1 * m += 1 * km) == 1001 * m);\nstatic_assert((isq::length(1 * m) += isq::height(1 * m)) == isq::length(1 * m));\nstatic_assert((isq::height(1.5 * m) -= 1 * m) == isq::height(0.5 * m));\n

    If we will break typical library's convertibility rules, the following code will not compile:

    quantity q1 = 1 * m -= 0.5 * m;                         // Compile-time error(1)\nquantity q2 = 1 * km += 1 * m;                          // Compile-time error(2)\nquantity q3 = isq::height(1 * m) += isq::length(1 * m); // Compile-time error(3)\n
    1. Conversion of the floating-point to integral representation type is considered narrowing.
    2. Conversion of quantity with integral representation type from a unit of a higher resolution to the one with a lower resolution is considered narrowing.
    3. Conversion from a more generic quantity type to a more specific one is considered unsafe.
    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#multiplication-and-division","title":"Multiplication and division","text":"

    Multiplying or dividing a quantity by a number does not change its quantity type or unit. However, its representation type may change. For example:

    static_assert(isq::height(3 * m) * 0.5 == isq::height(1.5 * m));\n

    Note

    Unless we use a compound assignment operator, in which case we always have to result with the type of the left-hand-side argument. This, together with the fact that this library tries to prevent truncation of a quantity value means, that the following does not compile:

    quantity q = isq::height(3 * m) *= 0.5; // Compile-time error\n

    However, suppose we multiply or divide quantities of the same or different types or we divide a raw number by a quantity. In that case, we most probably will end up in a quantity of yet another type:

    static_assert(120 * km / (2 * h) == 60 * km / h);\nstatic_assert(isq::width(2 * m) * isq::length(2 * m) == isq::area(4 * m2));\nstatic_assert(50 / isq::time(1 * s) == isq::frequency(50 * Hz));\n

    Note

    An exception from the above rule happens when one of the arguments is a dimensionless quantity. If we multiply or divide by such a quantity, the quantity type will not change. If such a quantity has a unit one, also the unit of a quantity will not change:

    static_assert(120 * m / (2 * one) == 60 * m);\n

    An interesting special case happens when we divide the same quantity kinds or multiply a quantity by its inverted type. In such a case, we end up with a dimensionless quantity.

    static_assert(isq::height(4 * m) / isq::width(2 * m) == 2 * one); // (1)!\nstatic_assert(5 * h / (120 * min) == 0 * one);  // (2)!\nstatic_assert(5. * h / (120 * min) == 2.5 * one);\n
    1. The resulting quantity type of the LHS is isq::height / isq::width, which is a quantity of the dimensionless kind.
    2. The resulting quantity of the LHS is 0 * dimensionless[h / min]. To be consistent with the division of different quantity types, we do not convert quantity values to a common unit before the division.

    Important: Beware of integral division

    The physical units library can't do any runtime branching logic for the division operator. All logic must be done at compile-time when the actual values are unknown, and the quantity types can't change at runtime.

    If we expect 120 * km / (2 * h) to return 60 km / h, we have to agree with the fact that 5 * km / (24 * h) returns 0 km/h. We can't do a range check at runtime to dynamically adjust scales and types based on the values of provided function arguments.

    This is why we often prefer floating-point representation types when dealing with units. Some popular physical units libraries even forbid integer division at all.

    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#modulo","title":"Modulo","text":"

    Now that we know how addition, subtraction, multiplication, and division work, it is time to discuss modulo. What would we expect to be returned from the following quantity equation?

    auto q = 5 * h % (120 * min);\n

    Most of us would probably expect to see 1 h or 60 min as a result. And this is where the problems start.

    C++ language defines its / and % operators with the quotient-remainder theorem:

    q = a / b;\nr = a % b;\nq * b + r == a;\n

    The important property of the modulo operation is that it only works for integral representation types (it is undefined what modulo for floating-point types means). However, as we saw in the previous chapter, integral types are tricky because they often truncate the value.

    From the quotient-remainder theorem, the result of modulo operation is r = a - q * b. Let's see what we get from such a quantity equation on integral representation types:

    const quantity a = 5 * h;\nconst quantity b = 120 * min;\nconst quantity q = a / b;\nconst quantity r = a - q * b;\n\nstd::cout << \"reminder: \" << r << \"\\n\";\n

    The above code outputs:

    reminder: 5 h\n

    And now, a tough question needs an answer. Do we really want modulo operation on physical units to be consistent with the quotient-remainder theorem and return 5 h for 5 * h % (120 * min)?

    This is exactly why we decided not to follow this hugely surprising path in the mp-units library. The selected approach was also consistent with the feedback from the C++ experts. For example, this is what Richard Smith said about this issue:

    Richard Smith

    I think the quotient-remainder property is a less important motivation here than other factors -- the constraints on % and / are quite different, so they lack the inherent connection they have for integers. In particular, I would expect that A / B works for all quantities A and B, whereas A % B is only meaningful when A and B have the same dimension. It seems like a nice-to-have for the property to apply in the case where both / and % are defined, but internal consistency of / across all cases seems much more important to me.

    I would expect 61 min % 1 h to be 1 min, and 1 h % 59 min to also be 1 min, so my intuition tells me that the result type of A % B, where A and B have the same dimension, should have the smaller unit of A and B (and if the smaller one doesn't divide the larger one, we should either use the gcd / std::common_type of the units of A and B or perhaps just produce an error). I think any other behavior for % is hard to defend.

    On the other hand, for division it seems to me that the choice of unit should probably not affect the result, and so if we want that 5 mm / 120 min = 0 mm/min, then 5 h / 120 min == 0 hc (where hc is a dimensionless \"hexaconta\", or 60x, unit). I don't like the idea of taking SI base units into account; that seems arbitrary and like it would do the wrong thing as often as it does the right thing, especially when the units have a multiplier that is very large or small. We could special-case the situation of a dimensionless quantity, but that could lead to problematic overflow pretty easily: a calculation such as 10 s * 5 GHz * 2 uW would overflow an int if it produces a dimensionless quantity for 10 s * 5 GHz, but it could equally produce 50 G * 2 uW = 100 kW without any overflow, and presumably would if the terms were merely reordered.

    If people want to use integer-valued quantities, I think it's fundamental that you need to know what the units of the result of an operation will be, and take that into account in how you express computations; the simplest rule for heterogeneous operators like * or / seems to be that the units of the result are determined by applying the operator to the units of the operands -- and for homogeneous operators like + or %, it seems like the only reasonable option is that you get the std::common_type of the units of the operands.

    To summarize, the modulo operation on physical units has more in common with addition and division operators than with the quotient-remainder theorem. To avoid surprising results, the operation uses a common unit to do the calculation and provide its result:

    static_assert(5 * h / (120 * min) == 0 * one);\nstatic_assert(5 * h % (120 * min) == 60 * min);\nstatic_assert(61 * min % (1 * h) == 1 * min);\nstatic_assert(1 * h % (59 * min) == 1 * min);\n
    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#comparison-against-zero","title":"Comparison against zero","text":"

    In our code, we often want to compare the value of a quantity against zero. For example, we do it every time we want to ensure that we deal with a non-zero or positive value.

    We could implement such checks in the following way:

    if (q1 / q2 != 0 * m / s)\n  // ...\n

    The above would work (assuming we are dealing with the quantity of speed) but could be suboptimal if the result of q1 / q2 is not expressed in m / s. To eliminate the need for conversion, we need to write:

    if (auto q = q1 / q2; q != q.zero())\n  // ...\n

    but that is a bit inconvenient, and inexperienced users could be unaware of this technique and its reasons.

    For the above reasons, the library provides dedicated interfaces to compare against zero that follow the naming convention of named comparison functions in the C++ Standard Library. The mp-units/compare.h header file exposes the following functions:

    • is_eq_zero
    • is_neq_zero
    • is_lt_zero
    • is_gt_zero
    • is_lteq_zero
    • is_gteq_zero

    Thanks to them, to save typing and not pay for unneeded conversions, our check could be implemented as follows:

    if (is_neq_zero(q1 / q2))\n  // ...\n

    Tip

    Those functions will work with any type T that exposes zero() member function returning something comparable to T. Thanks to that, we can use them not only with quantities but also with std::chrono::duration or any other type that exposes such an interface.

    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#other-maths","title":"Other maths","text":"

    This chapter scopes only on the quantity type's operators. However, there are many named math functions taking quantities as arguments. Those can be found in the mp-units/math.h header file. Among others, we can find there the following:

    • pow(), sqrt(), cbrt(),
    • exp(),
    • abs(),
    • epsilon(),
    • fma(), fmod(), remainder(),
    • isfinite(), isinf(), isnan(),
    • floor(), ceil(), round(),
    • inverse(),
    • hypot(),
    • sin(), cos(), tan(),
    • asin(), acos(), atan(), atan2().

    In the library, we can also find mp-units/random.h header file with all the pseudo-random number generators working on quantity types.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/","title":"Simple and Typed Quantities","text":"

    ISO defines a quantity as:

    Quote

    property of a phenomenon, body, or substance, where the property has a magnitude that can be expressed as a number and a reference

    After that, it says:

    Quote

    A reference can be a measurement unit, a measurement procedure, a reference material, or a combination of such.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#quantity-class-template","title":"quantity class template","text":"

    In the mp-units library, a quantity is represented with the following class template:

    template<Reference auto R,\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity;\n

    The concept Reference is satisfied by a type that provides all the domain-specific metadata describing a quantity (besides the representation type and its value). Such a type can be either:

    • a unit with an associated quantity type (e.g., si::metre, m / s),
    • a reference type explicitly specifying the quantity type and its unit.

    Important

    All units in the SI system have an associated quantity type.

    A reference type is implicitly created as a result of the following expression:

    constexpr auto ref = isq::length[m];\n

    The above example results in the following type reference<isq::length(), si::metre()> being instantiated.

    As we have two alternative options that satisfy the Reference concept in the mp-units library, we also have two modes of dealing with quantities.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#simple-quantities","title":"Simple quantities","text":"

    The simple mode might be preferred by many developers. It is all about units. Quantities using this mode have shorter type identifiers, resulting in easier-to-understand error messages and better debugging experience.

    Here is a simple example showing how to deal with such quantities:

    C++ modulesHeader files
    #include <print>\nimport mp_units;\n\nusing namespace mp_units;\n\nconstexpr quantity<si::metre / si::second> avg_speed(quantity<si::metre> dist,\n                                                     quantity<si::second> time)\n{\n  return dist / time;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  const quantity distance = 110 * km;\n  const quantity duration = 2 * h;\n  const quantity speed = avg_speed(distance, duration);\n\n  std::println(\"A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})\",\n               distance, duration, speed, speed.in(km / h));\n}\n
    #include <mp-units/format.h>\n#include <mp-units/systems/si.h>\n#include <print>\n\nusing namespace mp_units;\n\nconstexpr quantity<si::metre / si::second> avg_speed(quantity<si::metre> dist,\n                                                     quantity<si::second> time)\n{\n  return dist / time;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  const quantity distance = 110 * km;\n  const quantity duration = 2 * h;\n  const quantity speed = avg_speed(distance, duration);\n\n  std::println(\"A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})\",\n               distance, duration, speed, speed.in(km / h));\n}\n

    The code above prints:

    A car driving 110 km in 2 h has an average speed of 15.28 m/s (55 km/h)\n

    Try it on Compiler Explorer

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#user-provided-unit-wrappers","title":"User-provided unit wrappers","text":"

    Sometimes it might be awkward to type some derived units:

    quantity speed = 60 * km / h;\n

    In case such a unit is used a lot in the project, a user can easily provide a nicely named wrapper for it with:

    constexpr auto kmph = km / h;\nquantity speed = 60 * kmph;\n
    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#easy-to-understand-compilation-error-messages","title":"Easy-to-understand compilation error messages","text":"

    In case a user makes an error in a quantity equation and the result of the calculation will not match the function return type, the compiler will detect such an issue at compile-time.

    For example, in case we will make the following error:

    constexpr quantity<si::metre / si::second> avg_speed(quantity<si::metre> dist,\n                                                     quantity<si::second> time)\n{\n  return dist * time;  // (1)!\n}\n
    1. Quantities multiplied (instead of divided) by accident.

    the following compilation error message will be provided:

    error: no viable conversion from returned value of type\n       'quantity<mp_units::derived_unit<mp_units::si::metre, mp_units::si::second>{{{}}}, [...]>'\n       to function return type\n       'quantity<mp_units::derived_unit<mp_units::si::metre, mp_units::per<mp_units::si::second>>{{{}}}, [...]>'\n   10 |   return dist * time;\n      |          ^~~~~~~~~~~\n
    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#typed-quantities","title":"Typed quantities","text":"

    Simple mode is all about and just about units. In case we care about a specific quantity type, typed quantities should be preferred. With this mode, for example, we can specify if we deal with width, height, or radius and ensure we will not assign one to another by accident.

    The previous example can be re-typed using typed quantities in the following way:

    C++ modulesHeader files
    #include <print>\nimport mp_units;\n\nusing namespace mp_units;\n\nconstexpr quantity<isq::speed[si::metre / si::second]> avg_speed(quantity<isq::length[si::metre]> dist,\n                                                                 quantity<isq::time[si::second]> time)\n{\n  return dist / time;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  const quantity distance = isq::distance(110 * km);\n  const quantity duration = isq::time(2 * h);\n  const quantity speed = avg_speed(distance, duration);\n\n  std::println(\"A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})\",\n               distance, duration, speed, speed.in(km / h));\n}\n
    #include <mp-units/format.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <print>\n\nusing namespace mp_units;\n\nconstexpr quantity<isq::speed[si::metre / si::second]> avg_speed(quantity<isq::length[si::metre]> dist,\n                                                                 quantity<isq::time[si::second]> time)\n{\n  return dist / time;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  const quantity distance = isq::distance(110 * km);\n  const quantity duration = isq::time(2 * h);\n  const quantity speed = avg_speed(distance, duration);\n\n  std::println(\"A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})\",\n               distance, duration, speed, speed.in(km / h));\n}\n
    A car driving 110 km in 2 h has an average speed of 15.28 m/s (55 km/h)\n

    Try it on Compiler Explorer

    In case we will accidentally make the same calculation error as before, this time, we will get a bit longer error message, this time also containing information about the quantity type:

    error: no viable conversion from returned value of type\n       'quantity<reference<get_quantity_spec(metre{}) * struct time{{{}}}, metre{} * second{{}}>{}, [...]>'\n       to function return type\n       'quantity<reference<speed{}, derived_unit<metre, per<second>>{}>{}, [...]>'\n   12 |   return dist * time;\n      |          ^~~~~~~~~~~\n

    As we can see above, the compilation error is longer but still relatively easy to understand.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#additional-type-safety-with-typed-quantities","title":"Additional type safety with typed quantities","text":"

    Based on the previous example, it might seem that typed quantities are not that useful, more to type and provide harder-to-understand error messages. It might be true in some cases, but there are scenarios where they offer additional level of safety.

    Let's see another example:

    C++ modulesHeader files SimpleTyped
    #include <numbers>\nimport mp_units;\n\nusing namespace mp_units;\n\nclass StorageTank {\n  quantity<square(si::metre)> base_;\n  quantity<si::metre> height_;\npublic:\n  constexpr StorageTank(const quantity<square(si::metre)>& base,\n                        const quantity<si::metre>& height) :\n    base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n\nclass CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<si::metre>& radius,\n                                   const quantity<si::metre>& height) :\n    StorageTank(std::numbers::pi * pow<2>(radius), height)\n  {\n  }\n};\n\nclass RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<si::metre>& length,\n                                   const quantity<si::metre>& width,\n                                   const quantity<si::metre>& height) :\n    StorageTank(length * width, height)\n  {\n  }\n};\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  auto tank = RectangularStorageTank(1'000 * mm, 500 * mm, 200 * mm);\n  // ...\n}\n
    #include <numbers>\nimport mp_units;\n\nusing namespace mp_units;\n\n// add a custom quantity type of kind isq::length\ninline constexpr struct horizontal_length final :\n    quantity_spec<isq::length> {} horizontal_length;\n\n// add a custom derived quantity type of kind isq::area\n// with a constrained quantity equation\ninline constexpr struct horizontal_area final :\n    quantity_spec<isq::area, horizontal_length * isq::width> {} horizontal_area;\n\nclass StorageTank {\n  quantity<horizontal_area[square(si::metre)]> base_;\n  quantity<isq::height[si::metre]> height_;\npublic:\n  constexpr StorageTank(const quantity<horizontal_area[square(si::metre)]>& base,\n                        const quantity<isq::height[si::metre]>& height) :\n    base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n\nclass CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<isq::radius[si::metre]>& radius,\n                                   const quantity<isq::height[si::metre]>& height) :\n    StorageTank(quantity_cast<horizontal_area>(std::numbers::pi * pow<2>(radius)),\n                height)\n  {\n  }\n};\n\nclass RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<horizontal_length[si::metre]>& length,\n                                   const quantity<isq::width[si::metre]>& width,\n                                   const quantity<isq::height[si::metre]>& height) :\n    StorageTank(length * width, height)\n  {\n  }\n};\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\n                                     isq::width(500 * mm),\n                                     isq::height(200 * mm));\n  // ...\n}\n
    SimpleTyped
    #include <mp-units/math.h>\n#include <mp-units/systems/si.h>\n#include <numbers>\n\nusing namespace mp_units;\n\nclass StorageTank {\n  quantity<square(si::metre)> base_;\n  quantity<si::metre> height_;\npublic:\n  constexpr StorageTank(const quantity<square(si::metre)>& base,\n                        const quantity<si::metre>& height) :\n    base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n\nclass CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<si::metre>& radius,\n                                   const quantity<si::metre>& height) :\n    StorageTank(std::numbers::pi * pow<2>(radius), height)\n  {\n  }\n};\n\nclass RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<si::metre>& length,\n                                   const quantity<si::metre>& width,\n                                   const quantity<si::metre>& height) :\n    StorageTank(length * width, height)\n  {\n  }\n};\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  auto tank = RectangularStorageTank(1'000 * mm, 500 * mm, 200 * mm);\n  // ...\n}\n
    #include <mp-units/math.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <numbers>\n\nusing namespace mp_units;\n\n// add a custom quantity type of kind isq::length\ninline constexpr struct horizontal_length final :\n    quantity_spec<isq::length> {} horizontal_length;\n\n// add a custom derived quantity type of kind isq::area\n// with a constrained quantity equation\ninline constexpr struct horizontal_area final :\n    quantity_spec<isq::area, horizontal_length * isq::width> {} horizontal_area;\n\nclass StorageTank {\n  quantity<horizontal_area[square(si::metre)]> base_;\n  quantity<isq::height[si::metre]> height_;\npublic:\n  constexpr StorageTank(const quantity<horizontal_area[square(si::metre)]>& base,\n                        const quantity<isq::height[si::metre]>& height) :\n    base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n\nclass CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<isq::radius[si::metre]>& radius,\n                                   const quantity<isq::height[si::metre]>& height) :\n    StorageTank(quantity_cast<horizontal_area>(std::numbers::pi * pow<2>(radius)),\n                height)\n  {\n  }\n};\n\nclass RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<horizontal_length[si::metre]>& length,\n                                   const quantity<isq::width[si::metre]>& width,\n                                   const quantity<isq::height[si::metre]>& height) :\n    StorageTank(length * width, height)\n  {\n  }\n};\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\n                                     isq::width(500 * mm),\n                                     isq::height(200 * mm));\n  // ...\n}\n

    In the above example, the highlighted call doesn't look that safe anymore in the case of simple quantities, right? Suppose someone, either by mistake or due to some refactoring, will call the function with an invalid order of arguments. In that case, the program will compile fine but not work as expected.

    Let's see what will happen if we reorder the arguments in the case of typed quantities:

    auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\n                                   isq::height(200 * mm),\n                                   isq::width(500 * mm));\n

    This time, a compiler provides the following compilation error:

    <source>:53:15: error: no matching constructor for initialization of 'RectangularStorageTank'\n   53 |   auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\n      |               ^                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n   54 |                                      isq::height(200 * mm),\n      |                                      ~~~~~~~~~~~~~~~~~~~~~~\n   55 |                                      isq::width(500 * mm));\n      |                                      ~~~~~~~~~~~~~~~~~~~~\n<source>:43:13: note: candidate constructor not viable: no known conversion from\n                'quantity<mp_units::reference<mp_units::isq::height{{{{{}}}}},\n                                              mp_units::si::milli_<mp_units::si::metre{{}}>{{{{}}}}>{}, int>' to\n                'const quantity<reference<width{}, metre{}>{}, (default) double>' for 2nd argument\n   43 |   constexpr RectangularStorageTank(const quantity<horizontal_length[m]>& length,\n      |             ^\n   44 |                                    const quantity<isq::width[m]>& width,\n      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n

    What about derived quantities? In the above example, you probably noticed that we also defined a custom horizontal_area quantity of kind isq::area. This quantity has the unique property of being implicitly constructible only from the result of the multiplication of quantities of horizontal_area and isq::width or the ones that implicitly convert to them.

    Based on the above error message, we already know that a quantity of isq::height is not implicitly constructible to the quantity of isq::width. This property is transitively passed to derived quantities using them. If by accident, we will try to create a StorageTank base class in the following way:

    class RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<horizontal_length[m]>& length,\n                                   const quantity<isq::width[m]>& width,\n                                   const quantity<isq::height[m]>& height) :\n    StorageTank(length * height, height)\n  {\n  }\n};\n

    we will again get a compilation error message like this one:

    error: no matching constructor for initialization of 'StorageTank'\n   46 |     StorageTank(length * height, height)\n      |     ^           ~~~~~~~~~~~~~~~~~~~~~~~\n<source>:22:13: note: candidate constructor not viable: no known conversion from\n                'quantity<mp_units::reference<mp_units::derived_quantity_spec<horizontal_length, mp_units::isq::height>{{}, {{}}},\n                                              mp_units::derived_unit<mp_units::power<mp_units::si::metre, 2>>{{{}}}>{}, [...]>' to\n                'const quantity<reference<horizontal_area{}, derived_unit<power<metre, 2>>{}>{}, [...]>' for 1st argument\n   22 |   constexpr StorageTank(const quantity<horizontal_area[m2]>& base,\n      |             ^           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n

    Tip

    If you need to use various quantities of the same kind, consider using typed quantities to bring an additional level of safety to your project.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#quantity_cast-to-force-unsafe-conversions","title":"quantity_cast() to force unsafe conversions","text":"

    Did you notice the quantity_cast() usage in the other child class?

    class CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<isq::radius[m]>& radius,\n                                   const quantity<isq::height[m]>& height) :\n    StorageTank(quantity_cast<horizontal_area>(std::numbers::pi * pow<2>(radius)),\n                height)\n  {\n  }\n};\n

    As isq::radius is not convertible to horizontal_length, the derived quantity of pow<2>(radius) can't be converted to horizontal_area as well. It would be unsafe to allow such a conversion as not all of the circles lie flat on the ground, right?

    In such a case, the user has to explicitly force such an unsafe conversion with the help of a quantity_cast(). This function name is easy to spot in code reviews or while searching the project for problems if something goes sideways. In case of unexpected quantities-related issues, this should be the first function to look for.

    Tip

    Do not overuse quantity_cast(). Use it only when necessary and ensure that the requested conversion is exactly what you need in this case.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#which-mode-should-i-use-in-my-project","title":"Which mode should I use in my project?","text":"

    We have good news for you if you wonder which mode you should choose for your project. Simple and typed quantity modes can be freely mixed with each other. When you use different quantities of the same kind (e.g., radius, wavelength, altitude, ...), you should probably reach for typed quantities to bring additional safety for those cases. Otherwise, just use simple mode for the remaining quantities. The mp-units library will do its best to protect your project based on the information provided.

    Tip

    You can easily mix simple and typed quantities in your project.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/","title":"Systems of Quantities","text":"

    The physical units libraries on the market typically only scope on modeling one or more systems of units. However, this is not the only system kind to model. Another, and maybe even more important, system kind is a system of quantities.

    Info

    Please note that the mp-units is probably the first library on the Open Source market (in any programming language) that models the ISQ with all its definitions provided in ISO 80000. Please provide feedback if something looks odd or could be improved.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#dimension-is-not-enough-to-describe-a-quantity","title":"Dimension is not enough to describe a quantity","text":"

    Most of the products on the market are aware of physical dimensions. However, a dimension is not enough to describe a quantity. For example, let's see the following implementation:

    class Box {\n  area base_;\n  length height_;\npublic:\n  Box(length l, length w, length h) : base_(l * w), height_(h) {}\n  // ...\n};\n\nBox my_box(2 * m, 3 * m, 1 * m);\n

    How do you like such an interface? It turns out that in most existing strongly-typed libraries this is often the best we can do

    Another typical question many users ask is how to deal with work and torque. Both of those have the same dimension but are different quantities.

    A similar issue is related to figuring out what should be the result of:

    auto res = 1 * Hz + 1 * Bq + 1 * Bd;\n

    where:

    • Hz (hertz) - unit of frequency
    • Bq (becquerel) - unit of activity
    • Bd (baud) - unit of modulation rate

    All of those quantities have the same dimension, namely \\(\\mathsf{T}^{-1}\\), but probably it is not wise to allow adding, subtracting, or comparing them, as they describe vastly different physical properties.

    If the above example seems too abstract, let's consider a fuel consumption (fuel volume divided by distance, e.g., 6.7 l/km) and an area. Again, both have the same dimension \\(\\mathsf{L}^{2}\\), but probably it wouldn't be wise to allow adding, subtracting, or comparing a fuel consumption of a car and the area of a football field. Such an operation does not have any physical sense and should fail to compile.

    Important

    More than one quantity may be defined for the same dimension:

    • quantities of different kinds (e.g. frequency, modulation rate, activity, ...)
    • quantities of the same kind (e.g. length, width, altitude, distance, radius, wavelength, position vector, ...)

    It turns out that the above issues can't be solved correctly without proper modeling of a system of quantities.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#quantities-of-the-same-kind","title":"Quantities of the same kind","text":"

    ISO 80000-1

    • Quantities may be grouped together into categories of quantities that are mutually comparable
    • Mutually comparable quantities are called quantities of the same kind
    • Two or more quantities cannot be added or subtracted unless they belong to the same category of mutually comparable quantities
    • Quantities of the same kind within a given system of quantities have the same quantity dimension
    • Quantities of the same dimension are not necessarily of the same kind

    The above quotes from ISO 80000 provide answers to all the issues above. Two quantities can't be added, subtracted, or compared unless they belong to the same kind. As frequency, activity, and modulation rate are of different kinds, the expression provided above should not compile.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#system-of-quantities-is-not-only-about-kinds","title":"System of quantities is not only about kinds","text":"

    ISO 80000 specify hundreds of different quantities. There are plenty of different kinds provided and often each kind contains more than one quantity. In fact, it turns out that such quantities form a hierarchy of quantities of the same kind.

    For example, here are all quantities of the kind length provided in the ISO 80000:

    flowchart TD\n    length[\"<b>length</b><br>[m]\"]\n    length --- width[\"<b>width</b> / <b>breadth</b>\"]\n    length --- height[\"<b>height</b> / <b>depth</b> / <b>altitude</b>\"]\n    width --- thickness[\"<b>thickness</b>\"]\n    width --- diameter[\"<b>diameter</b>\"]\n    width --- radius[\"<b>radius</b>\"]\n    length --- path_length[\"<b>path_length</b>\"]\n    path_length --- distance[\"<b>distance</b>\"]\n    distance --- radial_distance[\"<b>radial_distance</b>\"]\n    length --- wavelength[\"<b>wavelength</b>\"]\n    length --- displacement[\"<b>displacement</b><br>{vector}\"]\n    displacement --- position_vector[\"<b>position_vector</b>\"]\n    radius --- radius_of_curvature[\"<b>radius_of_curvature</b>\"]

    Each of the above quantities expresses some kind of length, and each can be measured with si::metre. However, each of them has different properties, usage, and sometimes even requires a different representation type (notice that position_vector and displacement are vector quantities).

    Forming such a hierarchy helps us in defining arithmetics and conversion rules for various quantities of the same kind.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#defining-quantities","title":"Defining quantities","text":"

    In the mp-units library all the information about the quantity is provided with the quantity_spec class template. In order to define a specific quantity a user should inherit a strong type from such an instantiation.

    Tip

    Quantity specification definitions benefit from an explicit object parameter added in C++23 to remove the need for CRTP idiom, which significantly simplifies the code. However, as C++23 is far from being mainstream today, a portability macro QUANTITY_SPEC() is provided and used consistently through the library to allow the code to compile with C++20 compilers, thanks to the CRTP usage under the hood.

    See more in the C++ compiler support chapter.

    For example, here is how the above quantity kind tree can be modeled in the library:

    C++23C++20Portable
    inline constexpr struct length final : quantity_spec<dim_length> {} length;\ninline constexpr struct width final : quantity_spec<length> {} width;\ninline constexpr auto breadth = width;\ninline constexpr struct height final : quantity_spec<length> {} height;\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\ninline constexpr struct thickness final : quantity_spec<width> {} thickness;\ninline constexpr struct diameter final : quantity_spec<width> {} diameter;\ninline constexpr struct radius final : quantity_spec<width> {} radius;\ninline constexpr struct radius_of_curvature final : quantity_spec<radius> {} radius_of_curvature;\ninline constexpr struct path_length final : quantity_spec<length> {} path_length;\ninline constexpr auto arc_length = path_length;\ninline constexpr struct distance final : quantity_spec<path_length> {} distance;\ninline constexpr struct radial_distance final : quantity_spec<distance> {} radial_distance;\ninline constexpr struct wavelength final : quantity_spec<length> {} wavelength;\ninline constexpr struct displacement final : quantity_spec<length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final : quantity_spec<displacement> {} position_vector;\n
    inline constexpr struct length final : quantity_spec<length, dim_length> {} length;\ninline constexpr struct width final : quantity_spec<width, length> {} width;\ninline constexpr auto breadth = width;\ninline constexpr struct height final : quantity_spec<height, length> {} height;\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\ninline constexpr struct thickness final : quantity_spec<thickness, width> {} thickness;\ninline constexpr struct diameter final : quantity_spec<diameter, width> {} diameter;\ninline constexpr struct radius final : quantity_spec<radius, width> {} radius;\ninline constexpr struct radius_of_curvature final : quantity_spec<radius_of_curvature, radius> {} radius_of_curvature;\ninline constexpr struct path_length final : quantity_spec<path_length, length> {} path_length;\ninline constexpr auto arc_length = path_length;\ninline constexpr struct distance final : quantity_spec<distance, path_length> {} distance;\ninline constexpr struct radial_distance final : quantity_spec<radial_distance, distance> {} radial_distance;\ninline constexpr struct wavelength final : quantity_spec<wavelength, length> {} wavelength;\ninline constexpr struct displacement final : quantity_spec<displacement, length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final : quantity_spec<position_vector, displacement> {} position_vector;\n
    QUANTITY_SPEC(length, dim_length);\nQUANTITY_SPEC(width, length);\ninline constexpr auto breadth = width;\nQUANTITY_SPEC(height, length);\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\nQUANTITY_SPEC(thickness, width);\nQUANTITY_SPEC(diameter, width);\nQUANTITY_SPEC(radius, width);\nQUANTITY_SPEC(radius_of_curvature, radius);\nQUANTITY_SPEC(path_length, length);\ninline constexpr auto arc_length = path_length;\nQUANTITY_SPEC(distance, path_length);\nQUANTITY_SPEC(radial_distance, distance);\nQUANTITY_SPEC(wavelength, length);\nQUANTITY_SPEC(displacement, length, quantity_character::vector);\nQUANTITY_SPEC(position_vector, displacement);\n

    Note

    More information on how to define a system of quantities can be found in the \"International System of Quantities (ISQ)\" chapter.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#comparing-adding-and-subtracting-quantities","title":"Comparing, adding, and subtracting quantities","text":"

    ISO 80000 explicitly states that width and height are quantities of the same kind, and as such they:

    • are mutually comparable,
    • can be added and subtracted.

    If we take the above for granted, the only reasonable result of 1 * width + 1 * height is 2 * length, where the result of length is known as a common quantity type. A result of such an equation is always the first common node in a hierarchy tree of the same kind. For example:

    static_assert(get_common_quantity_spec(isq::width, isq::height) == isq::length);\nstatic_assert(get_common_quantity_spec(isq::thickness, isq::radius) == isq::width);\nstatic_assert(get_common_quantity_spec(isq::distance, isq::path_length) == isq::path_length);\n
    "},{"location":"users_guide/framework_basics/systems_of_quantities/#converting-between-quantities","title":"Converting between quantities","text":"

    Based on the same hierarchy of quantities of kind length, we can define quantity conversion rules.

    1. Implicit conversions

      • every width is a length
      • every radius is a width
      static_assert(implicitly_convertible(isq::width, isq::length));\nstatic_assert(implicitly_convertible(isq::radius, isq::width));\nstatic_assert(implicitly_convertible(isq::radius, isq::length));\n
    2. Explicit conversions

      • not every length is a width
      • not every width is a radius
      static_assert(!implicitly_convertible(isq::length, isq::width));\nstatic_assert(!implicitly_convertible(isq::width, isq::radius));\nstatic_assert(!implicitly_convertible(isq::length, isq::radius));\nstatic_assert(explicitly_convertible(isq::length, isq::width));\nstatic_assert(explicitly_convertible(isq::width, isq::radius));\nstatic_assert(explicitly_convertible(isq::length, isq::radius));\n
    3. Explicit casts

      • height is not a width
      • both height and width are quantities of kind length
      static_assert(!implicitly_convertible(isq::height, isq::width));\nstatic_assert(!explicitly_convertible(isq::height, isq::width));\nstatic_assert(castable(isq::height, isq::width));\n
    4. No conversion

      • time has nothing in common with length
      static_assert(!implicitly_convertible(isq::time, isq::length));\nstatic_assert(!explicitly_convertible(isq::time, isq::length));\nstatic_assert(!castable(isq::time, isq::length));\n
    "},{"location":"users_guide/framework_basics/systems_of_quantities/#hierarchies-of-derived-quantities","title":"Hierarchies of derived quantities","text":"

    Derived quantity equations often do not automatically form a hierarchy tree. This is why it is sometimes not obvious what such a tree should look like. Also, ISO explicitly states:

    ISO/IEC Guide 99

    The division of \u2018quantity\u2019 according to \u2018kind of quantity\u2019 is, to some extent, arbitrary.

    The below presents some arbitrary hierarchy of derived quantities of kind energy:

    flowchart TD\n    energy[\"<b>energy</b><br><i>(mass * length<sup>2</sup> / time<sup>2</sup>)</i><br>[J]\"]\n    energy --- mechanical_energy[\"<b>mechanical_energy</b>\"]\n    mechanical_energy --- potential_energy[\"<b>potential_energy</b>\"]\n    potential_energy --- gravitational_potential_energy[\"<b>gravitational_potential_energy</b><br><i>(mass * acceleration_of_free_fall * height)</i>\"]\n    potential_energy --- elastic_potential_energy[\"<b>elastic_potential_energy</b><br><i>(spring_constant * amount_of_compression<sup>2</sup>)</i>\"]\n    mechanical_energy --- kinetic_energy[\"<b>kinetic_energy</b><br><i>(mass * speed<sup>2</sup>)</i>\"]\n    energy --- enthalpy[\"<b>enthalpy</b>\"]\n    enthalpy --- internal_energy[\"<b>internal_energy</b> / <b>thermodynamic_energy</b>\"]\n    internal_energy --- Helmholtz_energy[\"<b>Helmholtz_energy</b> / <b>Helmholtz_function</b>\"]\n    enthalpy --- Gibbs_energy[\"<b>Gibbs_energy</b> / <b>Gibbs_function</b>\"]\n    energy --- active_energy[\"<b>active_energy</b>\"]

    Notice, that even though all of those quantities have the same dimension and can be expressed in the same units, they have different quantity equations that can be used to create them implicitly:

    • energy is the most generic one and thus can be created from base quantities of mass, length, and time. As those are also the roots of quantities of their kinds and all other quantities from their trees are implicitly convertible to them (we agreed on that \"every width is a length\" already), it means that an energy can be implicitly constructed from any quantity of mass, length, and time:

      static_assert(implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time), isq::energy));\nstatic_assert(implicitly_convertible(isq::mass * pow<2>(isq::height) / pow<2>(isq::time), isq::energy));\n
    • mechanical energy is a more \"specialized\" quantity than energy (not every energy is a mechanical energy). It is why an explicit cast is needed to convert from either energy or the results of its quantity equation:

      static_assert(!implicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                      isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                     isq::mechanical_energy));\n
    • gravitational potential energy is not only even more specialized one but additionally, it is special in a way that it provides its own \"constrained\" quantity equation. Maybe not every mass * pow<2>(length) / pow<2>(time) is a gravitational potential energy, but every mass * acceleration_of_free_fall * height is.

      static_assert(!implicitly_convertible(isq::energy, gravitational_potential_energy));\nstatic_assert(explicitly_convertible(isq::energy, gravitational_potential_energy));\nstatic_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                      gravitational_potential_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                     gravitational_potential_energy));\nstatic_assert(implicitly_convertible(isq::mass * isq::acceleration_of_free_fall * isq::height,\n                                     gravitational_potential_energy));\n
    "},{"location":"users_guide/framework_basics/systems_of_quantities/#modeling-a-quantity-kind","title":"Modeling a quantity kind","text":"

    In the physical units library, we also need an abstraction describing an entire family of quantities of the same kind. Such quantities have not only the same dimension but also can be expressed in the same units.

    To annotate a quantity to represent its kind (and not just a hierarchy tree's root quantity) we introduced a kind_of<> specifier. For example, to express any quantity of length, we need to type kind_of<isq::length>.

    Important

    isq::length and kind_of<isq::length> are two different things.

    Such an entity behaves as any quantity of its kind. This means that it is implicitly convertible to any quantity in a tree.

    static_assert(!implicitly_convertible(isq::length, isq::height));\nstatic_assert(implicitly_convertible(kind_of<isq::length>, isq::height));\n

    Additionally, the result of operations on quantity kinds is also a quantity kind:

    static_assert(same_type<kind_of<isq::length> / kind_of<isq::time>, kind_of<isq::length / isq::time>>);\n

    However, if at least one equation's operand is not a quantity kind, the result becomes a \"strong\" quantity where all the kinds are converted to the hierarchy tree's root quantities:

    static_assert(!same_type<kind_of<isq::length> / isq::time, kind_of<isq::length / isq::time>>);\nstatic_assert(same_type<kind_of<isq::length> / isq::time, isq::length / isq::time>);\n

    Info

    Only a root quantity from the hierarchy tree or the one marked with is_kind specifier in the quantity_spec definition can be put as a template parameter to the kind_of specifier. For example, kind_of<isq::width> will fail to compile. However, we can call get_kind(q) to obtain a kind of any quantity:

    static_assert(get_kind(isq::width) == kind_of<isq::length>);\n
    "},{"location":"users_guide/framework_basics/systems_of_units/","title":"Systems of Units","text":"

    Modeling a system of units is probably the most important feature and a selling point of every physical units library. Thanks to that, the library can protect users from performing invalid operations on quantities and provide automated conversion factors between various compatible units.

    Probably all the libraries in the wild model the SI and many of them provide support for additional units belonging to various other systems (e.g., imperial, cgs, etc).

    "},{"location":"users_guide/framework_basics/systems_of_units/#systems-of-units-are-based-on-systems-of-quantities","title":"Systems of Units are based on Systems of Quantities","text":"

    Systems of quantities specify a set of quantities and equations relating to those quantities. Those equations do not take any unit or a numerical representation into account at all. To create a quantity, we need to add those missing pieces of information. This is where a system of units kicks in.

    The SI is explicitly stated to be based on the ISQ. Among others, it defines 7 base units, one for each base quantity. In the mp-units this is expressed by associating a quantity kind (that we discussed in detail in the previous chapter) with a unit that is used to express it:

    inline constexpr struct metre final : named_unit<\"m\", kind_of<isq::length>> {} metre;\n

    Important

    The kind_of<isq::length> above states explicitly that this unit has an associated quantity kind. In other words, si::metre (and scaled units based on it) can be used to express the amount of any quantity of kind length.

    "},{"location":"users_guide/framework_basics/systems_of_units/#units-compose","title":"Units compose","text":"

    One of the most vital points of the SI system is that its units compose. This allows providing thousands of different units for hundreds of various quantities with a tiny set of predefined units and prefixes.

    The same is modeled in the mp-units library, which also allows composing predefined units to create a nearly infinite number of different derived units. For example, one can write:

    quantity<si::metre / si::second> q;\n

    to express a quantity of speed. The resulting quantity type is implicitly inferred from the unit equation by repeating the same operations on the associated quantity kinds.

    "},{"location":"users_guide/framework_basics/systems_of_units/#many-shades-of-the-same-unit","title":"Many shades of the same unit","text":"

    The SI provides the names for 22 common coherent units of 22 derived quantities.

    Each such named derived unit is a result of a specific predefined unit equation. For example, a unit of power quantity is defined in the library as:

    inline constexpr struct watt final : named_unit<\"W\", joule / second> {} watt;\n

    However, a power quantity can be expressed in other units as well. For example, the following:

    auto q1 = 42 * W;\nstd::cout << q1 << \"\\n\";\nstd::cout << q1.in(J / s) << \"\\n\";\nstd::cout << q1.in(N * m / s) << \"\\n\";\nstd::cout << q1.in(kg * m2 / s3) << \"\\n\";\n

    prints:

    42 W\n42 J/s\n42 N m/s\n42 kg m\u00b2/s\u00b3\n

    All of the above quantities are equivalent and mean exactly the same.

    Note

    The above code example may give the impression that the order of components in a derived unit is determined by the multiplication order. This is not the case. As stated in Simplifying the resulting expression templates, to be able to reason about and simplify units, the library needs to order them in an appropriate order. This will affect the order of components in a resulting type and text output.

    Please refer to our FAQ for more information.

    "},{"location":"users_guide/framework_basics/systems_of_units/#constraining-a-derived-unit-to-work-only-with-a-specific-derived-quantity","title":"Constraining a derived unit to work only with a specific derived quantity","text":"

    Some derived units are valid only for specific derived quantities. For example, SI specifies both hertz and becquerel derived units with the same unit equation 1 / s. However, it also explicitly states:

    SI Brochure

    The hertz shall only be used for periodic phenomena and the becquerel shall only be used for stochastic processes in activity referred to a radionuclide.

    The above means that the usage of becquerel as a unit of a frequency quantity is an error.

    The library allows constraining such units to work only with quantities of a specific kind in the following way:

    inline constexpr struct hertz final : named_unit<\"Hz\", one / second, kind_of<isq::frequency>> {} hertz;\ninline constexpr struct becquerel final : named_unit<\"Bq\", one / second, kind_of<isq::activity>> {} becquerel;\n

    With the above, hertz can only be used with frequencies, while becquerel should only be used with quantities of activity. This means that the following equation will not compile:

    auto q = 1 * Hz + 1 * Bq;   // Fails to compile\n

    This is exactly what we wanted to achieve to improve the type-safety of the library.

    "},{"location":"users_guide/framework_basics/systems_of_units/#prefixed-units","title":"Prefixed units","text":"

    Besides named units, the SI specifies also 24 prefixes (all being a power of 10) that can be prepended to all named units to obtain various scaled versions of them.

    Implementation of std::ratio provided by all major compilers is able to express only 16 of them. This is why, in the mp-units, we had to find an alternative way to represent unit magnitude in a more flexible way.

    Each prefix is implemented similarly to the following:

    template<PrefixableUnit U> struct quecto_ : prefixed_unit<\"q\", mag_power<10, -30>, U{}> {};\ntemplate<PrefixableUnit auto U> constexpr quecto_<decltype(U)> quecto;\n

    and then a PrefixableUnit can be prefixed in the following way:

    inline constexpr auto qm = quecto<metre>;\n

    The usage of mag_power not only enables providing support for SI prefixes, but it can also efficiently represent any rational magnitude. For example, IEC 80000 prefixes used in the IT industry can be implemented as:

    template<PrefixableUnit U> struct yobi_ : prefixed_unit<\"Yi\", mag_power<2, 80>, U{}> {};\ntemplate<PrefixableUnit auto U> constexpr yobi_<decltype(U)> yobi;\n
    "},{"location":"users_guide/framework_basics/systems_of_units/#scaled-units","title":"Scaled units","text":"

    In the SI, all units are either base or derived units or prefixed versions of those. However, those are only some of the options possible.

    For example, there is a list of off-system units accepted for use with SI. Those are scaled versions of the SI units with ratios that can't be explicitly expressed with predefined SI prefixes. Those include units like minute, hour, or electronvolt:

    inline constexpr struct minute final : named_unit<\"min\", mag<60> * si::second> {} minute;\ninline constexpr struct hour final : named_unit<\"h\", mag<60> * minute> {} hour;\ninline constexpr struct electronvolt final : named_unit<\"eV\", mag_ratio<1'602'176'634, 1'000'000'000> * mag_power<10, -19> * si::joule> {} electronvolt;\n

    Also, units of other systems of units are often defined in terms of scaled versions of the SI units. For example, the international yard is defined as:

    inline constexpr struct yard final : named_unit<\"yd\", mag_ratio<9'144, 10'000> * si::metre> {} yard;\n

    For some units, a magnitude might also be irrational. The best example here is a degree which is defined using a floating-point magnitude having a factor of the number \u03c0 (Pi):

    inline constexpr struct pi final : mag_constant<symbol_text{u8\"\u03c0\", \"pi\"}, std::numbers::pi_v<long double>> {} pi;\ninline constexpr auto \u03c0 = pi;\n
    inline constexpr struct degree final : named_unit<{u8\"\u00b0\", \"deg\"}, mag<\u03c0> / mag<180> * si::radian> {} degree;\n
    "},{"location":"users_guide/framework_basics/systems_of_units/#unit-symbols","title":"Unit symbols","text":"

    Units are available via their full names or through their short symbols. To use a long version, it is enough to type:

    quantity q1 = 42 * si::metre / si::second;\nquantity q2 = 42 * si::kilo<si::metre> / si::hour;\n

    To simplify how we spell it a short, user-friendly symbols are provided in a dedicated subnamespace in systems definitions:

    namespace si::unit_symbols {\n\nconstexpr auto m = si::metre;\nconstexpr auto km = si::kilo<si::metre>;\nconstexpr auto s = si::second;\nconstexpr auto h = si::hour;\n\n}\n

    Unit symbols introduce a lot of short identifiers into the current namespace. This is why they are opt-in. A user has to explicitly \"import\" them from a dedicated unit_symbols namespace:

    using-declarationusing-directive
    using namespace si::unit_symbols;\n\nquantity q1 = 42 * m / s;\nquantity q2 = 42 * km / h;\n
    using si::unit_symbols::m;\nusing si::unit_symbols::km;\nusing si::unit_symbols::s;\nusing si::unit_symbols::h;\n\nquantity q1 = 42 * m / s;\nquantity q2 = 42 * km / h;\n

    We also provide alternative object identifiers using UTF-8 characters in their names for most unit symbols. The code using UTF-8 looks nicer, but it is harder to type on the keyboard. This is why we provide both versions of identifiers for such units.

    PortableWith UTF-8 glyphs
    quantity resistance = 60 * kohm;\nquantity capacitance = 100 * uF;\n
    quantity resistance = 60 * k\u03a9;\nquantity capacitance = 100 * \u00b5F;\n
    "},{"location":"users_guide/framework_basics/systems_of_units/#common-units","title":"Common units","text":"

    Adding, subtracting, or comparing two quantities of different units will force the library to find a common unit for those. This is to prevent data truncation. For the cases when one of the units is an integral multiple of the another, the resulting quantity will use a \"smaller\" one in its result. For example:

    static_assert((1 * kg + 1 * g).unit == g);\nstatic_assert((1 * km + 1 * mm).unit == mm);\nstatic_assert((1 * yd + 1 * mi).unit == yd);\n

    However, in many cases an arithmetic operation on quantities of different units will result in a yet another unit. This happens when none of the source units is an integral multiple of another. In such cases, the library returns a special type that denotes that we are dealing with a common unit of such an equation:

    quantity q1 = 1 * km + 1 * mi;     // quantity<common_unit<international::mile, si::kilo_<si::metre>>{}, int>\nquantity q2 = 1. * rad + 1. * deg; // quantity<common_unit<si::degree, si::radian>{}, double>\n

    Note

    A user should never explicitly instantiate a common_unit class template. The library's framework will do it based on the provided quantity equation.

    "},{"location":"users_guide/framework_basics/text_output/","title":"Text Output","text":"

    Besides providing dimensional analysis and unit conversions, the library also tries hard to print any quantity in the most user-friendly way. We can print the entire quantity or its selected parts (numerical value, unit, or dimension).

    Note

    The library does not provide a text output for quantity points. The quantity stored inside is just an implementation detail of this type. It is a vector from a specific origin. Without the knowledge of the origin, the vector by itself is useless as we can't determine which point it describes.

    In the current library design, point origin does not provide any text in its definition. Even if we could add such information to the point's definition, we would not know how to output it in the text. There may be many ways to do it. For example, should we prepend or append the origin part to the quantity text?

    For example, the text output of 42 m for a quantity point may mean many things. It may be an offset from the mountain top, sea level, or maybe the center of Mars. Printing 42 m AMSL for altitudes above mean sea level is a much better solution, but the library does not have enough information to print it that way by itself.

    Please let us know if you have a good idea of how to solve this issue.

    "},{"location":"users_guide/framework_basics/text_output/#predefined-symbols","title":"Predefined symbols","text":"

    The definitions of dimensions, units, prefixes, and constants require assigning text symbols for each entity. Those symbols will be composed by the library's framework to express dimensions and units of derived quantities.

    DimensionsUnitsPrefixesConstants
    inline constexpr struct dim_length final : base_dimension<\"L\"> {} dim_length;\ninline constexpr struct dim_mass final : base_dimension<\"M\"> {} dim_mass;\ninline constexpr struct dim_time final : base_dimension<\"T\"> {} dim_time;\ninline constexpr struct dim_electric_current final : base_dimension<\"I\"> {} dim_electric_current;\ninline constexpr struct dim_thermodynamic_temperature final : base_dimension<{u8\"\u0398\", \"O\"}> {} dim_thermodynamic_temperature;\ninline constexpr struct dim_amount_of_substance final : base_dimension<\"N\"> {} dim_amount_of_substance;\ninline constexpr struct dim_luminous_intensity final : base_dimension<\"J\"> {} dim_luminous_intensity;\n
    inline constexpr struct second final : named_unit<\"s\", kind_of<isq::time>> {} second;\ninline constexpr struct metre final : named_unit<\"m\", kind_of<isq::length>> {} metre;\ninline constexpr struct gram final : named_unit<\"g\", kind_of<isq::mass>> {} gram;\ninline constexpr auto kilogram = kilo<gram>;\n\ninline constexpr struct newton final : named_unit<\"N\", kilogram * metre / square(second)> {} newton;\ninline constexpr struct joule final : named_unit<\"J\", newton * metre> {} joule;\ninline constexpr struct watt final : named_unit<\"W\", joule / second> {} watt;\ninline constexpr struct coulomb final : named_unit<\"C\", ampere * second> {} coulomb;\ninline constexpr struct volt final : named_unit<\"V\", watt / ampere> {} volt;\ninline constexpr struct farad final : named_unit<\"F\", coulomb / volt> {} farad;\ninline constexpr struct ohm final : named_unit<{u8\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n
    template<PrefixableUnit U> struct micro_ : prefixed_unit<{u8\"\u00b5\", \"u\"}, mag_power<10, -6>, U{}> {};\ntemplate<PrefixableUnit U> struct milli_ : prefixed_unit<\"m\", mag_power<10, -3>, U{}> {};\ntemplate<PrefixableUnit U> struct centi_ : prefixed_unit<\"c\", mag_power<10, -2>, U{}> {};\ntemplate<PrefixableUnit U> struct deci_  : prefixed_unit<\"d\", mag_power<10, -1>, U{}> {};\ntemplate<PrefixableUnit U> struct deca_  : prefixed_unit<\"da\", mag_power<10, 1>, U{}> {};\ntemplate<PrefixableUnit U> struct hecto_ : prefixed_unit<\"h\", mag_power<10, 2>, U{}> {};\ntemplate<PrefixableUnit U> struct kilo_  : prefixed_unit<\"k\", mag_power<10, 3>, U{}> {};\ntemplate<PrefixableUnit U> struct mega_  : prefixed_unit<\"M\", mag_power<10, 6>, U{}> {};\n
    inline constexpr struct hyperfine_structure_transition_frequency_of_cs final : named_unit<{u8\"\u0394\u03bd_Cs\", \"dv_Cs\"}, mag<9'192'631'770> * hertz> {} hyperfine_structure_transition_frequency_of_cs;\ninline constexpr struct speed_of_light_in_vacuum final : named_unit<\"c\", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum;\ninline constexpr struct planck_constant final : named_unit<\"h\", mag_ratio<662'607'015, 100'000'000> * mag_power<10, -34> * joule * second> {} planck_constant;\ninline constexpr struct elementary_charge final : named_unit<\"e\", mag_ratio<1'602'176'634, 1'000'000'000> * mag_power<10, -19> * coulomb> {} elementary_charge;\ninline constexpr struct boltzmann_constant final : named_unit<\"k\", mag_ratio<1'380'649, 1'000'000> * mag_power<10, -23> * joule / kelvin> {} boltzmann_constant;\ninline constexpr struct avogadro_constant final : named_unit<\"N_A\", mag_ratio<602'214'076, 100'000'000> * mag_power<10, 23> / mole> {} avogadro_constant;\ninline constexpr struct luminous_efficacy final : named_unit<\"K_cd\", mag<683> * lumen / watt> {} luminous_efficacy;\n

    Important

    Two symbols always have to be provided if the primary symbol contains characters outside of the basic literal character set. The first must be provided as a UTF-8 literal and may contain any Unicode characters. The second one must provide an alternative spelling and only use characters from within of basic literal character set.

    Note

    Unicode provides only a minimal set of characters available as subscripts, which are often used to differentiate various constants and quantities of the same kind. To workaround this issue, mp-units uses the '_' character to specify that the following characters should be considered a subscript of the symbol.

    Tip

    For older compilers, it might be required to specify a symbol_text class explicitly template name to initialize it with two symbols:

    inline constexpr struct ohm final : named_unit<symbol_text{u8\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-for-derived-entities","title":"Symbols for derived entities","text":""},{"location":"users_guide/framework_basics/text_output/#text_encoding","title":"text_encoding","text":"

    ISQ and SI standards always specify symbols using UTF-8 encoding. This is why it is a default and primary target for text output. However, in some applications or environments, a standard portable text output using only the characters from the basic literal character set can be preferred by users.

    This is why the library provides an option to change the default encoding to the portable one with:

    enum class text_encoding : std::int8_t {\n  utf8,       // \u00b5s; m\u00b3;  L\u00b2MT\u207b\u00b3\n  portable,   // us; m^3; L^2MT^-3\n  default_encoding = utf8\n};\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-of-derived-dimensions","title":"Symbols of derived dimensions","text":""},{"location":"users_guide/framework_basics/text_output/#dimension_symbol_formatting","title":"dimension_symbol_formatting","text":"

    dimension_symbol_formatting is a data type describing the configuration of the symbol generation algorithm.

    struct dimension_symbol_formatting {\n  text_encoding encoding = text_encoding::default_encoding;\n};\n
    "},{"location":"users_guide/framework_basics/text_output/#dimension_symbol","title":"dimension_symbol()","text":"

    Returns a std::string_view with the symbol of a dimension for the provided configuration:

    template<dimension_symbol_formatting fmt = dimension_symbol_formatting{}, typename CharT = char, Dimension D>\n[[nodiscard]] consteval std::string_view dimension_symbol(D);\n

    For example:

    static_assert(dimension_symbol<{.encoding = text_encoding::portable}>(isq::power.dimension) == \"L^2MT^-3\");\n

    Note

    std::string_view is returned only when C++23 is available. Otherwise, an instance of a basic_fixed_string is being returned.

    "},{"location":"users_guide/framework_basics/text_output/#dimension_symbol_to","title":"dimension_symbol_to()","text":"

    Inserts the generated dimension symbol into the output text iterator at runtime.

    template<typename CharT = char, std::output_iterator<CharT> Out, Dimension D>\nconstexpr Out dimension_symbol_to(Out out, D d, dimension_symbol_formatting fmt = dimension_symbol_formatting{});\n

    For example:

    std::string txt;\ndimension_symbol_to(std::back_inserter(txt), isq::power.dimension, {.encoding = text_encoding::portable});\nstd::cout << txt << \"\\n\";\n

    The above prints:

    L^2MT^-3\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-of-derived-units","title":"Symbols of derived units","text":""},{"location":"users_guide/framework_basics/text_output/#unit_symbol_formatting","title":"unit_symbol_formatting","text":"

    unit_symbol_formatting is a data type describing the configuration of the symbol generation algorithm. It contains three orthogonal fields, each with a default value.

    enum class unit_symbol_solidus : std::int8_t {\n  one_denominator,  // m/s;   kg m\u207b\u00b9 s\u207b\u00b9\n  always,           // m/s;   kg/(m s)\n  never,            // m s\u207b\u00b9; kg m\u207b\u00b9 s\u207b\u00b9\n  default_denominator = one_denominator\n};\n\nenum class unit_symbol_separator : std::int8_t {\n  space,          // kg m\u00b2/s\u00b2\n  half_high_dot,  // kg\u22c5m\u00b2/s\u00b2  (valid only for utf8 encoding)\n  default_separator = space\n};\n\nstruct unit_symbol_formatting {\n  text_encoding encoding = text_encoding::default_encoding;\n  unit_symbol_solidus solidus = unit_symbol_solidus::default_denominator;\n  unit_symbol_separator separator = unit_symbol_separator::default_separator;\n};\n

    unit_symbol_solidus impacts how the division of unit symbols is being presented in the text output. By default, the '/' will be printed if only one unit component is in the denominator. Otherwise, the exponent syntax will be used.

    unit_symbol_separator specifies how multiple multiplied units should be separated from each other. By default, the space (' ') will be used as a separator.

    "},{"location":"users_guide/framework_basics/text_output/#unit_symbol","title":"unit_symbol()","text":"

    Returns a std::string_view with the symbol of a unit for the provided configuration:

    template<unit_symbol_formatting fmt = unit_symbol_formatting{}, typename CharT = char, Unit U>\n[[nodiscard]] consteval std::string_view unit_symbol(U);\n

    For example:

    static_assert(unit_symbol<{.solidus = unit_symbol_solidus::never,\n                           .separator = unit_symbol_separator::half_high_dot}>(kg * m / s2) == \"kg\u22c5m\u22c5s\u207b\u00b2\");\n
    "},{"location":"users_guide/framework_basics/text_output/#unit_symbol_to","title":"unit_symbol_to()","text":"

    Inserts the generated unit symbol into the output text iterator at runtime.

    template<typename CharT = char, std::output_iterator<CharT> Out, Unit U>\nconstexpr Out unit_symbol_to(Out out, U u, unit_symbol_formatting fmt = unit_symbol_formatting{});\n

    For example:

    std::string txt;\nunit_symbol_to(std::back_inserter(txt), kg * m / s2,\n               {.solidus = unit_symbol_solidus::never, .separator = unit_symbol_separator::half_high_dot});\nstd::cout << txt << \"\\n\";\n

    The above prints:

    kg\u22c5m\u22c5s\u207b\u00b2\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-of-scaled-units","title":"Symbols of scaled units","text":"

    In most cases scaled units are hidden behind named units. However, there are a few real-life where a user directly faces a scaled unit. For example:

    constexpr Unit auto L_per_100km = L / (mag<100> * km);\n

    The above is a derived unit of litre divided by a scaled unit of 100 kilometers. As we can see a scaled unit has a magnitude and a reference unit. To denote the scope of such a unit, we enclose it in [...]. For example, the following:

    std::cout << 6.7 * L_per_100km << \"\\n\";\n

    prints:

    6.7 L/[100 km]\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-of-common-units","title":"Symbols of common units","text":"

    Some common units expressed with a specialization of the common_unit class template need special printing rules for their symbols. As they represent a minimum set of equivalent common units resulting from the addition or subtraction of multiple quantities, we print all of them as a scaled version of the source unit. For example, the following:

    std::cout << 1 * km + 1 * mi << \"\\n\";\nstd::cout << 1 * nmi + 1 * mi << \"\\n\";\nstd::cout << 1 * km / h + 1 * m / s << \"\\n\";\nstd::cout << 1 * rad + 1 * deg << \"\\n\";\n

    prints:

    40771 EQUIV{[1/25146 mi], [1/15625 km]}\n108167 EQUIV{[1/50292 mi], [1/57875 nmi]}\n23 EQUIV{[1/5 km/h], [1/18 m/s]}\n183.142 EQUIV{[1/\u03c0\u00b0], [1/180 rad]}\n

    Thanks to the above, it might be easier for the user to reason about the magnitude of the resulting unit and its impact on the value stored in the quantity.

    "},{"location":"users_guide/framework_basics/text_output/#space_before_unit_symbol-customization-point","title":"space_before_unit_symbol customization point","text":"

    The SI Brochure says:

    SI Brochure

    The numerical value always precedes the unit and a space is always used to separate the unit from the number. ... The only exceptions to this rule are for the unit symbols for degree, minute and second for plane angle, \u00b0, \u2032 and \u2033, respectively, for which no space is left between the numerical value and the unit symbol.

    There are more units with such properties. For example, percent (%) and per mille(\u2030).

    To support the above and other similar cases, the library exposes space_before_unit_symbol customization point. By default, its value is true for all the units, so the space between a number and a unit will be inserted in the output text. To change this behavior, we have to provide a partial specialization for a specific unit:

    template<>\nconstexpr bool space_before_unit_symbol<non_si::degree> = false;\n

    Note

    The above works only for the default formatting or for the format strings that use %? placement field (std::format(\"{}\", q) is equivalent to std::format(\"{:%N%?%U}\", q)).

    In case a user provides custom format specification (e.g., std::format(\"{:%N %U}\", q)), the library will always obey this specification for all the units (no matter what the actual value of the space_before_unit_symbol customization point is) and the separating space will always be used in this case.

    "},{"location":"users_guide/framework_basics/text_output/#output-streams","title":"Output streams","text":"

    Tip

    The output streaming support is opt-in and can be enabled by including the <mp-units/ostream.h> header file.

    The easiest way to print a dimension, unit, or quantity is to provide its object to the output stream:

    const QuantityOf<isq::speed> auto v1 = avg_speed(220. * km, 2 * h);\nconst QuantityOf<isq::speed> auto v2 = avg_speed(140. * mi, 2 * h);\nstd::cout << v1 << '\\n';            // 110 km/h\nstd::cout << v2 << '\\n';            // 70 mi/h\nstd::cout << v2.unit << '\\n';       // mi/h\nstd::cout << v2.dimension << '\\n';  // LT\u207b\u00b9\n

    The text output will always print the value using the default formatting for this entity.

    Important: Don't assume a unit

    Remember that when we deal with a quantity of an \"unknown\" (e.g., auto) type, it is a good practice to always convert the unit to the expected one before passing it to the text output:

    std::cout << v1.in(km / h) << '\\n';       // 110 km/h\nstd::cout << v1.force_in(m / s) << '\\n';  // 30.5556 m/s\n
    "},{"location":"users_guide/framework_basics/text_output/#output-stream-formatting","title":"Output stream formatting","text":"

    Only basic formatting can be applied to output streams. It includes control over width, fill, and alignment.

    The numerical value of the quantity will be printed according to the current stream state and standard manipulators may be used to customize that (assuming that the underlying representation type respects them).

    std::cout << \"|\" << std::setw(10) << 123 * m << \"|\\n\";                       // |     123 m|\nstd::cout << \"|\" << std::setw(10) << std::left << 123 * m << \"|\\n\";          // |123 m     |\nstd::cout << \"|\" << std::setw(10) << std::setfill('*') << 123 * m << \"|\\n\";  // |123 m*****|\n

    Note

    To have more control over the formatting of the quantity that is printed with the output stream just use std::cout << std::format(...).

    "},{"location":"users_guide/framework_basics/text_output/#text-formatting","title":"Text formatting","text":"

    The library provides custom formatters for std::format facility, which allows fine-grained control over what and how it is being printed in the text output.

    Tip

    The text formatting facility support is opt-in and can be enabled by including the <mp-units/format.h> header file.

    "},{"location":"users_guide/framework_basics/text_output/#controlling-width-fill-and-alignment","title":"Controlling width, fill, and alignment","text":"

    Formatting grammar for all the entities provides control over width, fill, and alignment. The C++ standard grammar tokens fill-and-align and width are being used. They treat the entity as a contiguous text to be aligned. For example, here are a few examples of the quantity numerical value and symbol formatting:

    std::println(\"|{:0}|\", 123 * m);     // |123 m|\nstd::println(\"|{:10}|\", 123 * m);    // |     123 m|\nstd::println(\"|{:<10}|\", 123 * m);   // |123 m     |\nstd::println(\"|{:>10}|\", 123 * m);   // |     123 m|\nstd::println(\"|{:^10}|\", 123 * m);   // |  123 m   |\nstd::println(\"|{:*<10}|\", 123 * m);  // |123 m*****|\nstd::println(\"|{:*>10}|\", 123 * m);  // |*****123 m|\nstd::println(\"|{:*^10}|\", 123 * m);  // |**123 m***|\n

    It is important to note that in the second line above, the quantity text is aligned to the right by default, which is consistent with the formatting of numeric types. Units and dimensions behave as text and, thus, are aligned to the left by default.

    Note

    std::println is a C++23 facility. In case we do not have access to C++23, we can obtain the same output with:

    std::cout << std::format(\"<format-string>\\n\", <format-args>);\n
    "},{"location":"users_guide/framework_basics/text_output/#dimension-formatting","title":"Dimension formatting","text":"
    dimension-format-spec = [fill-and-align], [width], [dimension-spec];\ndimension-spec        = [character-set];\ncharacter-set         = 'U' | 'P';\n

    In the above grammar:

    • fill-and-align and width tokens are defined in the format.string.std chapter of the C++ standard specification,
    • character-set token specifies the symbol text encoding:
      • U (default) uses the UTF-8 symbols defined by [@ISO80000] (e.g., LT\u207b\u00b2),
      • P forces non-standard portable output (e.g., LT^-2).

    Dimension symbols of some quantities are specified to use Unicode signs by the ISQ (e.g., \u0398 symbol for the thermodynamic temperature dimension). The library follows this by default. From the engineering point of view, sometimes Unicode text might not be the best solution, as terminals of many (especially embedded) devices can output only letters from the basic literal character set. In such a case, the dimension symbol can be forced to be printed using such characters thanks to character-set token:

    std::println(\"{}\", isq::dim_thermodynamic_temperature);   // \u0398\nstd::println(\"{:P}\", isq::dim_thermodynamic_temperature); // O\nstd::println(\"{}\", isq::power.dimension);                 // L\u00b2MT\u207b\u00b3\nstd::println(\"{:P}\", isq::power.dimension);               // L^2MT^-3\n
    "},{"location":"users_guide/framework_basics/text_output/#unit-formatting","title":"Unit formatting","text":"
    unit-format-spec      = [fill-and-align], [width], [unit-spec];\nunit-spec             = [character-set], [unit-symbol-solidus], [unit-symbol-separator], [L]\n                      | [character-set], [unit-symbol-separator], [unit-symbol-solidus], [L]\n                      | [unit-symbol-solidus], [character-set], [unit-symbol-separator], [L]\n                      | [unit-symbol-solidus], [unit-symbol-separator], [character-set], [L]\n                      | [unit-symbol-separator], [character-set], [unit-symbol-solidus], [L]\n                      | [unit-symbol-separator], [unit-symbol-solidus], [character-set], [L];\nunit-symbol-solidus   = '1' | 'a' | 'n';\nunit-symbol-separator = 's' | 'd';\n

    In the above grammar:

    • fill-and-align and width tokens are defined in the format.string.std chapter of the C++ standard specification,
    • unit-symbol-solidus token specifies how the division of units should look like:
      • '1' (default) outputs / only when there is only one unit in the denominator, otherwise negative exponents are printed (e.g., m/s, kg m\u207b\u00b9 s\u207b\u00b9)
      • 'a' always uses solidus (e.g., m/s, kg/(m s))
      • 'n' never prints solidus, which means that negative exponents are always used (e.g., m s\u207b\u00b9, kg m\u207b\u00b9 s\u207b\u00b9)
    • unit-symbol-separator token specifies how multiplied unit symbols should be separated:
      • 's' (default) uses space as a separator (e.g., kg m\u00b2/s\u00b2)
      • 'd' uses half-high dot (\u22c5) as a separator (e.g., kg\u22c5m\u00b2/s\u00b2) (requires the UTF-8 encoding)
    • 'L' is reserved for possible future localization use in case the C++ standard library gets access to the ICU-like database.

    Note

    The above grammar intended that the elements of unit-spec can appear in any order as they have unique characters. Users shouldn't have to remember the order of those tokens to control the formatting of a unit symbol.

    Unit symbols of some quantities are specified to use Unicode signs by the SI (e.g., \u03a9 symbol for the resistance quantity). The library follows this by default. From the engineering point of view, Unicode text might not be the best solution sometimes, as terminals of many (especially embedded) devices can output only letters from the basic literal character set. In such a case, the unit symbol can be forced to be printed using such characters thanks to character-set token:

    std::println(\"{}\", si::ohm);      // \u03a9\nstd::println(\"{:P}\", si::ohm);    // ohm\nstd::println(\"{}\", us);           // \u00b5s\nstd::println(\"{:P}\", us);         // us\nstd::println(\"{}\", m / s2);       // m/s\u00b2\nstd::println(\"{:P}\", m / s2);     // m/s^2\n

    Additionally, both ISO 80000 and SI leave some freedom on how to print unit symbols. This is why two additional tokens were introduced.

    unit-symbol-solidus specifies how the division of units should look like. By default, / will be used only when the denominator contains only one unit. However, with the 'a' or 'n' options, we can force the facility to print the / character always (even when there are more units in the denominator), or never, in which case a parenthesis will be added to enclose all denominator units.

    std::println(\"{}\", m / s);          // m/s\nstd::println(\"{}\", kg / m / s2);    // kg m\u207b\u00b9 s\u207b\u00b2\nstd::println(\"{:a}\", m / s);        // m/s\nstd::println(\"{:a}\", kg / m / s2);  // kg/(m s\u00b2)\nstd::println(\"{:n}\", m / s);        // m s\u207b\u00b9\nstd::println(\"{:n}\", kg / m / s2);  // kg m\u207b\u00b9 s\u207b\u00b2\n

    The unit-symbol-separator token allows us to obtain the following outputs:

    std::println(\"{}\", kg * m2 / s2);    // kg m\u00b2/s\u00b2\nstd::println(\"{:d}\", kg * m2 / s2);  // kg\u22c5m\u00b2/s\u00b2\n

    Note

    'd' requires the UTF-8 encoding to be set.

    "},{"location":"users_guide/framework_basics/text_output/#quantity-formatting","title":"Quantity formatting","text":"
    quantity-format-spec        = [fill-and-align], [width], [quantity-specs], [defaults-specs];\nquantity-specs              = conversion-spec;\n                            | quantity-specs, conversion-spec;\n                            | quantity-specs, literal-char;\nliteral-char                = ? any character other than '{', '}', or '%' ?;\nconversion-spec             = '%', placement-type;\nplacement-type              = subentity-id | '?' | '%';\ndefaults-specs              = ':', default-spec-list;\ndefault-spec-list           = default-spec;\n                            | default-spec-list, default-spec;\ndefault-spec                = subentity-id, '[' format-spec ']';\nsubentity-id                = 'N' | 'U' | 'D';\nformat-spec                 = ? as specified by the formatter for the argument type ?;\n

    In the above grammar:

    • fill-and-align and width tokens are defined in the format.string.std chapter of the C++ standard specification,
    • placement-type token specifies which entity should be put and where:
      • 'N' inserts a default-formatted numerical value of the quantity,
      • 'U' inserts a default-formatted unit of the quantity,
      • 'D' inserts a default-formatted dimension of the quantity,
      • '?' inserts an optional separator between the number and a unit based on the value of space_before_unit_symbol for this unit,
      • '%' just inserts '%' character.
    • defaults-specs token allows overwriting defaults for the underlying formatters with the custom format string. Each override starts with a subentity identifier ('N', 'U', or 'D') followed by the format string enclosed in square brackets.
    "},{"location":"users_guide/framework_basics/text_output/#default-formatting","title":"Default formatting","text":"

    To format quantity values, the formatting facility uses quantity-format-spec. If left empty, the default formatting is applied. The same default formatting is also applied to the output streams. This is why the following code lines produce the same output:

    std::cout << \"Distance: \" << 123 * km << \"\\n\";\nstd::cout << std::format(\"Distance: {}\\n\", 123 * km);\nstd::cout << std::format(\"Distance: {:%N%?%U}\\n\", 123 * km);\n

    Note

    For some quantities, the {:%N %U} format may provide a different output than the default one, as some units have space_before_unit_symbol customization point explicitly set to false (e.g., % and \u00b0).

    "},{"location":"users_guide/framework_basics/text_output/#quantity-numerical-value-unit-symbol-or-both","title":"Quantity numerical value, unit symbol, or both?","text":"

    Thanks to the grammar provided above, the user can easily decide to either:

    • print a whole quantity:

      std::println(\"Speed: {}\", 120 * km / h);\n
      Speed: 120 km/h\n
    • provide custom quantity formatting:

      std::println(\"Speed: {:%N in %U}\", 120 * km / h);\n
      Speed: 120 in km/h\n
    • provide custom formatting for components:

      std::println(\"Speed: {::N[.2f]U[n]}\", 100. * km / (3 * h));\n
      Speed: 33.33 km h\u207b\u00b9\n
    • print only specific components (numerical value, unit, or dimension):

      std::println(\"Speed:\\n- number: {0:%N}\\n- unit: {0:%U}\\n- dimension: {0:%D}\", 120 * km / h);\n
      Speed:\n- number: 120\n- unit: km/h\n- dimension: LT\u207b\u00b9\n
    "},{"location":"users_guide/framework_basics/text_output/#formatting-of-the-quantity-numerical-value","title":"Formatting of the quantity numerical value","text":"

    The representation type used as a numerical value of a quantity must provide its own formatter specialization. It will be called by the quantity formatter with the format-spec provided by the user in the N defaults specification.

    In case we use C++ fundamental arithmetic types with our quantities the standard formatter specified in format.string.std will be used. The rest of this chapter assumes that it is the case and provides some usage examples.

    sign token allows us to specify how the value's sign is being printed:

    std::println(\"{0},{0::N[+]},{0::N[-]},{0::N[ ]}\", 1 * m);   // 1 m,+1 m,1 m, 1 m\nstd::println(\"{0},{0::N[+]},{0::N[-]},{0::N[ ]}\", -1 * m);  // -1 m,-1 m,-1 m,-1 m\n

    where:

    • + indicates that a sign should be used for both non-negative and negative numbers,
    • - indicates that a sign should be used for negative numbers and negative zero only (this is the default behavior),
    • <space> indicates that a leading space should be used for non-negative numbers other than negative zero, and a minus sign for negative numbers and negative zero.

    precision token is allowed only for floating-point representation types:

    std::println(\"{::N[.0]}\", 1.2345 * m);   // 1 m\nstd::println(\"{::N[.1]}\", 1.2345 * m);   // 1 m\nstd::println(\"{::N[.2]}\", 1.2345 * m);   // 1.2 m\nstd::println(\"{::N[.3]}\", 1.2345 * m);   // 1.23 m\nstd::println(\"{::N[.0f]}\", 1.2345 * m);  // 1 m\nstd::println(\"{::N[.1f]}\", 1.2345 * m);  // 1.2 m\nstd::println(\"{::N[.2f]}\", 1.2345 * m);  // 1.23 m\n

    type specifies how a value of the representation type is being printed. For integral types:

    std::println(\"{::N[b]}\", 42 * m);    // 101010 m\nstd::println(\"{::N[B]}\", 42 * m);    // 101010 m\nstd::println(\"{::N[d]}\", 42 * m);    // 42 m\nstd::println(\"{::N[o]}\", 42 * m);    // 52 m\nstd::println(\"{::N[x]}\", 42 * m);    // 2a m\nstd::println(\"{::N[X]}\", 42 * m);    // 2A m\n

    The above can be printed in an alternate version thanks to the # token:

    std::println(\"{::N[#b]}\", 42 * m);   // 0b101010 m\nstd::println(\"{::N[#B]}\", 42 * m);   // 0B101010 m\nstd::println(\"{::N[#o]}\", 42 * m);   // 052 m\nstd::println(\"{::N[#x]}\", 42 * m);   // 0x2a m\nstd::println(\"{::N[#X]}\", 42 * m);   // 0X2A m\n

    For floating-point values, the type token works as follows:

    std::println(\"{::N[a]}\",   1.2345678 * m);      // 1.3c0ca2a5b1d5dp+0 m\nstd::println(\"{::N[.3a]}\", 1.2345678 * m);      // 1.3c1p+0 m\nstd::println(\"{::N[A]}\",   1.2345678 * m);      // 1.3C0CA2A5B1D5DP+0 m\nstd::println(\"{::N[.3A]}\", 1.2345678 * m);      // 1.3C1P+0 m\nstd::println(\"{::N[e]}\",   1.2345678 * m);      // 1.234568e+00 m\nstd::println(\"{::N[.3e]}\", 1.2345678 * m);      // 1.235e+00 m\nstd::println(\"{::N[E]}\",   1.2345678 * m);      // 1.234568E+00 m\nstd::println(\"{::N[.3E]}\", 1.2345678 * m);      // 1.235E+00 m\nstd::println(\"{::N[g]}\",   1.2345678 * m);      // 1.23457 m\nstd::println(\"{::N[g]}\",   1.2345678e8 * m);    // 1.23457e+08 m\nstd::println(\"{::N[.3g]}\", 1.2345678 * m);      // 1.23 m\nstd::println(\"{::N[.3g]}\", 1.2345678e8 * m);    // 1.23e+08 m\nstd::println(\"{::N[G]}\",   1.2345678 * m);      // 1.23457 m\nstd::println(\"{::N[G]}\",   1.2345678e8 * m);    // 1.23457E+08 m\nstd::println(\"{::N[.3G]}\", 1.2345678 * m);      // 1.23 m\nstd::println(\"{::N[.3G]}\", 1.2345678e8 * m);    // 1.23E+08 m\n
    "},{"location":"users_guide/framework_basics/the_affine_space/","title":"The Affine Space","text":"

    The affine space has two types of entities:

    • Point - a position specified with coordinate values (e.g., location, address, etc.)
    • Displacement vector - the difference between two points (e.g., shift, offset, displacement, duration, etc.)

    In the following subchapters, we will often refer to displacement vectors simply as vectors for brevity.

    Note

    The displacement vector described here is specific to the affine space theory and is not the same thing as the quantity of a vector character that we discussed in the \"Scalars, vectors, and tensors\" chapter (although, in some cases, those terms may overlap).

    "},{"location":"users_guide/framework_basics/the_affine_space/#operations-in-the-affine-space","title":"Operations in the affine space","text":"

    Here are the primary operations one can do in the affine space:

    • vector + vector -> vector
    • vector - vector -> vector
    • -vector -> vector
    • vector * scalar -> vector
    • scalar * vector -> vector
    • vector / scalar -> vector
    • point - point -> vector
    • point + vector -> point
    • vector + point -> point
    • point - vector -> point

    Important

    It is not possible to:

    • add two points,
    • subtract a point from a vector,
    • multiply nor divide points with anything else.
    "},{"location":"users_guide/framework_basics/the_affine_space/#points-are-more-common-than-most-of-us-imagine","title":"Points are more common than most of us imagine","text":"

    Point abstractions should be used more often in the C++ software. They are not only about temperature or time. Points are everywhere around us and should become more popular in the products we implement. They can be used to implement:

    • temperature points,
    • timestamps,
    • daily mass readouts from the scale,
    • altitudes of mountain peaks on a map,
    • current path length measured by the car's odometer,
    • today's price of instruments on the market,
    • and many more.

    Improving the affine space's Points intuition will allow us to write better and safer software.

    "},{"location":"users_guide/framework_basics/the_affine_space/#displacement-vector-is-modeled-by-quantity","title":"Displacement vector is modeled by quantity","text":"

    Up until now, each time we used a quantity in our code, we were modeling some kind of a difference between two things:

    • the distance between two points,
    • duration between two time points,
    • the difference in speed (even if relative to zero).

    As we already know, a quantity type provides all operations required for a displacement vector abstraction in the affine space. It can be constructed with:

    • the multiply syntax (works for most of the units),
    • delta<Reference> construction helper (e.g., delta<isq::height[m]>(42), delta<deg_C>(3)),
    • two-parameter constructor taking a number and a quantity reference/unit.

    Note

    The multiply syntax support is disabled for units that provide a point origin in their definition (i.e., units of temperature like K, deg_C, and deg_F).

    "},{"location":"users_guide/framework_basics/the_affine_space/#point-is-modeled-by-quantity_point-and-pointorigin","title":"Point is modeled by quantity_point and PointOrigin","text":"

    In the mp-units library, the Point abstraction is modelled by:

    • PointOrigin concept that specifies measurement origin, and
    • quantity_point class template that specifies a Point relative to a specific predefined origin.
    "},{"location":"users_guide/framework_basics/the_affine_space/#quantity_point","title":"quantity_point","text":"

    The quantity_point class template specifies an absolute quantity measured from a predefined origin:

    template<Reference auto R,\n         PointOriginFor<get_quantity_spec(R)> auto PO = default_point_origin(R),\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity_point;\n

    As we can see above, the quantity_point class template exposes one additional parameter compared to quantity. The PO parameter satisfies a PointOriginFor concept and specifies the origin of our measurement scale.

    Each quantity_point internally stores a quantity object, which represents a displacement vector from the predefined origin. Thanks to this, an instantiation of a quantity_point can be considered as a model of a vector space from such an origin.

    Forcing the user to manually predefine an origin for every domain may be cumbersome and discourage users from using such abstractions at all. This is why, by default, the PO template parameter is initialized with the default_point_origin(R) that provides the quantity points' scale zeroth point using the following rules:

    • if the measurement unit of a quantity specifies its point origin in its definition (e.g., degree Celsius), then this origin is being used,
    • otherwise, an instantiation of zeroth_point_origin<QuantitySpec> is being used which provides a well-established zeroth point for a specific quantity type.

    Quantity points with default point origins may be constructed with the point construction helper or forcing an explicit conversion from the quantity:

    // quantity_point qp1 = 42 * m;           // Compile-time error\n// quantity_point qp2 = 42 * K;           // Compile-time error\n// quantity_point qp3 = delta<deg_C>(42); // Compile-time error\nquantity_point qp4(42 * m);\nquantity_point qp5(42 * K);\nquantity_point qp6(delta<deg_C>(42));\nquantity_point qp7 = point<m>(42);\nquantity_point qp8 = point<K>(42);\nquantity_point qp9 = point<deg_C>(42);\n

    Tip

    The quantity_point definition can be found in the mp-units/quantity_point.h header file.

    "},{"location":"users_guide/framework_basics/the_affine_space/#zeroth_point_originquantityspec","title":"zeroth_point_origin<QuantitySpec>","text":"

    zeroth_point_origin<QuantitySpec> is meant to be used in cases where the specific domain has a well-established, non-controversial, and unique zeroth point on the measurement scale. This saves the user from the need to write a boilerplate code that would predefine such a type for this domain.

    quantity_point<isq::distance[si::metre]> qp1(100 * m);\nquantity_point<isq::distance[si::metre]> qp2 = point<m>(120);\n\nassert(qp1.quantity_from_zero() == 100 * m);\nassert(qp2.quantity_from_zero() == 120 * m);\nassert(qp2.quantity_from(qp1) == 20 * m);\nassert(qp1.quantity_from(qp2) == -20 * m);\n\nassert(qp2 - qp1 == 20 * m);\nassert(qp1 - qp2 == -20 * m);\n\n// auto res = qp1 + qp2;   // Compile-time error\n

    In the above code 100 * m and 120 * m still create two quantities that serve as displacement vectors here. Quantity point objects can be explicitly constructed from such quantities only when their origin is an instantiation of the zeroth_point_origin<QuantitySpec>.

    It is really important to understand that even though we can use .quantity_from_zero() to obtain the displacement vector of a point from the origin, the point by itself does not represent or have any associated physical value. It is just a point in some space. The same point can be expressed with different displacement vectors from different origins.

    It is also worth mentioning that simplicity comes with a safety cost here. For some users, it might be surprising that the usage of zeroth_point_origin<QuantitySpec> makes various quantity point objects compatible as long as quantity types used in the origin and reference are compatible:

    quantity_point<si::metre> qp1{isq::distance(100 * m)};\nquantity_point<si::metre> qp2 = point<isq::height[m]>(120);\n\nassert(qp2.quantity_from(qp1) == 20 * m);\nassert(qp1.quantity_from(qp2) == -20 * m);\nassert(qp2 - qp1 == 20 * m);\nassert(qp1 - qp2 == -20 * m);\n
    "},{"location":"users_guide/framework_basics/the_affine_space/#absolute-point-origin","title":"Absolute point origin","text":"

    In cases where we want to implement an isolated independent space in which points are not compatible with other spaces, even of the same quantity type, we should manually predefine an absolute point origin.

    inline constexpr struct origin final : absolute_point_origin<isq::distance> {} origin;\n\n// quantity_point<si::metre, origin> qp1{100 * m};        // Compile-time error\n// quantity_point<si::metre, origin> qp2{delta<m>(120)};  // Compile-time error\nquantity_point<si::metre, origin> qp1 = origin + 100 * m;\nquantity_point<si::metre, origin> qp2 = 120 * m + origin;\n\n// assert(qp1.quantity_from_zero() == 100 * m);   // Compile-time error\n// assert(qp2.quantity_from_zero() == 120 * m);   // Compile-time error\nassert(qp1.quantity_from(origin) == 100 * m);\nassert(qp2.quantity_from(origin) == 120 * m);\nassert(qp2.quantity_from(qp1) == 20 * m);\nassert(qp1.quantity_from(qp2) == -20 * m);\n\nassert(qp1 - origin == 100 * m);\nassert(qp2 - origin == 120 * m);\nassert(qp2 - qp1 == 20 * m);\nassert(qp1 - qp2 == -20 * m);\n\nassert(origin - qp1 == -100 * m);\nassert(origin - qp2 == -120 * m);\n\n// assert(origin - origin == 0 * m);   // Compile-time error\n

    We can't construct a quantity point directly from the quantity anymore when a custom, named origin is used. To prevent potential safety and maintenance issues, we always need to explicitly provide both a compatible origin and a quantity measured from it to construct a quantity point.

    Said otherwise, a quantity point defined in terms of a specific origin is the result of adding the origin and the displacement vector measured from it to the point we create.

    Info

    A rationale for this longer construction syntax can be found in the Why can't I create a quantity by passing a number to a constructor? chapter.

    Similarly to creation of a quantity, if someone does not like the operator-based syntax to create a quantity_point, the same results can be achieved with a two-parameter constructor:

    quantity_point qp1{100 * m, origin};\n

    Again, CTAD always helps to use precisely the type we need in a current case.

    Additionally, if a quantity point is defined in terms of a custom, named origin, then we can't use a quantity_from_zero() member function anymore. This is to prevent surprises, as our origin may not necessarily be perceived as an absolute zero in the domain we model. Also, as we will learn soon, we can define several related origins in one space, and then it gets harder to understand which one is the \"zero\" one. This is why, to be specific and always correct about the points we use, a quantity_from(QP) member function can be used (where QP can either be an origin or another quantity point).

    Finally, please note that it is not allowed to subtract two point origins defined in terms of absolute_point_origin (e.g., origin - origin) as those do not contain information about the unit, so we cannot determine a resulting quantity type.

    "},{"location":"users_guide/framework_basics/the_affine_space/#modeling-independent-spaces-in-one-domain","title":"Modeling independent spaces in one domain","text":"

    Absolute point origins are also perfect for establishing independent spaces even if the same quantity type and unit is being used:

    inline constexpr struct origin1 final : absolute_point_origin<isq::distance> {} origin1;\ninline constexpr struct origin2 final : absolute_point_origin<isq::distance> {} origin2;\n\nquantity_point qp1 = origin1 + 100 * m;\nquantity_point qp2 = origin2 + 120 * m;\n\nassert(qp1.quantity_from(origin1) == 100 * m);\nassert(qp2.quantity_from(origin2) == 120 * m);\n\nassert(qp1 - origin1 == 100 * m);\nassert(qp2 - origin2 == 120 * m);\nassert(origin1 - qp1 == -100 * m);\nassert(origin2 - qp2 == -120 * m);\n\n// assert(qp2 - qp1 == 20 * m);                    // Compile-time error\n// assert(qp1 - origin2 == 100 * m);               // Compile-time error\n// assert(qp2 - origin1 == 120 * m);               // Compile-time error\n// assert(qp2.quantity_from(qp1) == 20 * m);       // Compile-time error\n// assert(qp1.quantity_from(origin2) == 100 * m);  // Compile-time error\n// assert(qp2.quantity_from(origin1) == 120 * m);  // Compile-time error\n
    "},{"location":"users_guide/framework_basics/the_affine_space/#relative-point-origin","title":"Relative Point origin","text":"

    We often do not have only one ultimate \"zero\" point when we measure things. Often, we have one common scale, but we measure various quantities relative to different points and expect those points to be compatible. There are many examples here, but probably the most common are temperatures, timestamps, and altitudes.

    For such cases, relative point origins should be used:

    inline constexpr struct A final : absolute_point_origin<isq::distance> {} A;\ninline constexpr struct B final : relative_point_origin<A + 10 * m> {} B;\ninline constexpr struct C final : relative_point_origin<B + 10 * m> {} C;\ninline constexpr struct D final : relative_point_origin<A + 30 * m> {} D;\n\nquantity_point qp1 = C + 100 * m;\nquantity_point qp2 = D + 120 * m;\n\nassert(qp1.quantity_ref_from(qp1.point_origin) == 100 * m);\nassert(qp2.quantity_ref_from(qp2.point_origin) == 120 * m);\n\nassert(qp2.quantity_from(qp1) == 30 * m);\nassert(qp1.quantity_from(qp2) == -30 * m);\nassert(qp2 - qp1 == 30 * m);\nassert(qp1 - qp2 == -30 * m);\n\nassert(qp1.quantity_from(A) == 120 * m);\nassert(qp1.quantity_from(B) == 110 * m);\nassert(qp1.quantity_from(C) == 100 * m);\nassert(qp1.quantity_from(D) == 90 * m);\nassert(qp1 - A == 120 * m);\nassert(qp1 - B == 110 * m);\nassert(qp1 - C == 100 * m);\nassert(qp1 - D == 90 * m);\n\nassert(qp2.quantity_from(A) == 150 * m);\nassert(qp2.quantity_from(B) == 140 * m);\nassert(qp2.quantity_from(C) == 130 * m);\nassert(qp2.quantity_from(D) == 120 * m);\nassert(qp2 - A == 150 * m);\nassert(qp2 - B == 140 * m);\nassert(qp2 - C == 130 * m);\nassert(qp2 - D == 120 * m);\n\nassert(B - A == 10 * m);\nassert(C - A == 20 * m);\nassert(D - A == 30 * m);\nassert(D - C == 10 * m);\n\nassert(B - B == 0 * m);\n// assert(A - A == 0 * m);  // Compile-time error\n

    Note

    Even though we can't subtract two absolute point origins from each other, it is possible to subtract relative ones or relative and absolute ones.

    "},{"location":"users_guide/framework_basics/the_affine_space/#converting-between-different-representations-of-the-same-point","title":"Converting between different representations of the same point","text":"

    As we might represent the same point with displacement vectors from various origins, the library provides facilities to convert the same point to the quantity_point class templates expressed in terms of different origins.

    For this purpose, we can use either:

    • A converting constructor:

      quantity_point<si::metre, C> qp2C = qp2;\nassert(qp2C.quantity_ref_from(qp2C.point_origin) == 130 * m);\n
    • A dedicated conversion interface:

      quantity_point qp2B = qp2.point_for(B);\nquantity_point qp2A = qp2.point_for(A);\n\nassert(qp2B.quantity_ref_from(qp2B.point_origin) == 140 * m);\nassert(qp2A.quantity_ref_from(qp2A.point_origin) == 150 * m);\n

    It is important to understand that all such translations still describe exactly the same point (e.g., all of them compare equal):

    assert(qp2 == qp2C);\nassert(qp2 == qp2B);\nassert(qp2 == qp2A);\n

    Important

    It is only allowed to convert between various origins defined in terms of the same absolute_point_origin. Even if it is possible to express the same point as a displacement vector from another absolute_point_origin, the library will not provide such a conversion. A custom user-defined conversion function will be needed to add such a functionality.

    Said another way, in the library, there is no way to spell how two distinct absolute_point_origin types relate to each other.

    "},{"location":"users_guide/framework_basics/the_affine_space/#temperature-support","title":"Temperature support","text":"

    Support for temperature quantity points is probably one of the most common examples of relative point origins in action that we use in daily life.

    The SI definition in the library provides a few predefined point origins for this purpose:

    namespace si {\n\ninline constexpr struct absolute_zero final : absolute_point_origin<isq::thermodynamic_temperature> {} absolute_zero;\ninline constexpr auto zeroth_kelvin = absolute_zero;\n\ninline constexpr struct ice_point final : relative_point_origin<point<milli<kelvin>>(273'150)}> {} ice_point;\ninline constexpr auto zeroth_degree_Celsius = ice_point;\n\n}\n\nnamespace usc {\n\ninline constexpr struct zeroth_degree_Fahrenheit final :\n  relative_point_origin<point<mag_ratio<5, 9> * si::degree_Celsius>(-32)> {} zeroth_degree_Fahrenheit;\n\n}\n

    The above is a great example of how point origins can be stacked on top of each other:

    • usc::zeroth_degree_Fahrenheit is defined relative to si::zeroth_degree_Celsius
    • si::zeroth_degree_Celsius is defined relative to si::zeroth_kelvin.

    Note

    Notice that while stacking point origins, we can use different representation types and units for origins and a point. In the above example, the relative point origin for degree Celsius is defined in terms of si::kelvin, while the quantity point for it will use si::degree_Celsius as a unit.

    The temperature point origins defined above are provided explicitly in the respective units' definitions:

    namespace si {\n\ninline constexpr struct kelvin final :\n    named_unit<\"K\", kind_of<isq::thermodynamic_temperature>, zeroth_kelvin> {} kelvin;\ninline constexpr struct degree_Celsius final :\n    named_unit<{u8\"\u2103\", \"`C\"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;\n\n}\n\nnamespace usc {\n\ninline constexpr struct degree_Fahrenheit final :\n    named_unit<{u8\"\u2109\", \"`F\"}, mag_ratio<5, 9> * si::degree_Celsius,\n               zeroth_degree_Fahrenheit> {} degree_Fahrenheit;\n\n}\n

    As it was described above, default_point_origin(R) returns a zeroth_point_origin<QuantitySpec> when a unit does not provide any origin in its definition. As of today, the units of temperature are the only ones in the entire mp-units library that provide such origins.

    Now, let's see how we can benefit from the above definitions. We have quite a few alternatives to choose from here. Depending on our needs or tastes, we can:

    • be explicit about the unit and origin:

      quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q1 = si::zeroth_degree_Celsius + delta<deg_C>(20.5);\nquantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q2{delta<deg_C>(20.5), si::zeroth_degree_Celsius};\nquantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q3{delta<deg_C>(20.5)};\nquantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q4 = point<deg_C>(20.5);\n
    • specify a unit and use its zeroth point origin implicitly:

      quantity_point<si::degree_Celsius> q5 = si::zeroth_degree_Celsius + delta<deg_C>(20.5);\nquantity_point<si::degree_Celsius> q6{delta<deg_C>(20.5), si::zeroth_degree_Celsius};\nquantity_point<si::degree_Celsius> q7{delta<deg_C>(20.5)};\nquantity_point<si::degree_Celsius> q8 = point<deg_C>(20.5);\n
    • benefit from CTAD:

      quantity_point q9 = si::zeroth_degree_Celsius + delta<deg_C>(20.5);\nquantity_point q10{delta<deg_C>(20.5), si::zeroth_degree_Celsius};\nquantity_point q11{delta<deg_C>(20.5)};\nquantity_point q12 = point<deg_C>(20.5);\n

    In all of the above cases, we end up with the quantity_point of the same type and value.

    To play a bit more with temperatures, we can implement a simple room AC temperature controller in the following way:

    constexpr struct room_reference_temp final : relative_point_origin<point<deg_C>(21)> {} room_reference_temp;\nusing room_temp = quantity_point<isq::Celsius_temperature[deg_C], room_reference_temp>;\n\nconstexpr auto step_delta = delta<isq::Celsius_temperature<deg_C>>(0.5);\nconstexpr int number_of_steps = 6;\n\nroom_temp room_ref{};\nroom_temp room_low = room_ref - number_of_steps * step_delta;\nroom_temp room_high = room_ref + number_of_steps * step_delta;\n\nstd::println(\"Room reference temperature: {} ({}, {::N[.2f]})\\n\",\n             room_ref.quantity_from_zero(),\n             room_ref.in(usc::degree_Fahrenheit).quantity_from_zero(),\n             room_ref.in(si::kelvin).quantity_from_zero());\n\nstd::println(\"| {:<18} | {:^18} | {:^18} | {:^18} |\",\n             \"Temperature delta\", \"Room reference\", \"Ice point\", \"Absolute zero\");\nstd::println(\"|{0:=^20}|{0:=^20}|{0:=^20}|{0:=^20}|\", \"\");\n\nauto print_temp = [&](std::string_view label, auto v) {\n  std::println(\"| {:<14} | {:^18} | {:^18} | {:^18:N[.2f]} |\", label,\n               v - room_reference_temp, (v - si::ice_point).in(deg_C), (v - si::absolute_zero).in(deg_C));\n};\n\nprint_temp(\"Lowest\", room_low);\nprint_temp(\"Default\", room_ref);\nprint_temp(\"Highest\", room_high);\n

    The above prints:

    Room reference temperature: 21 \u2103 (69.8 \u2109, 294.15 K)\n\n| Temperature delta  |   Room reference   |     Ice point      |   Absolute zero    |\n|====================|====================|====================|====================|\n| Lowest             |       -3 \u2103        |       18 \u2103        |     291.15 \u2103      |\n| Default            |        0 \u2103        |       21 \u2103        |     294.15 \u2103      |\n| Highest            |        3 \u2103        |       24 \u2103        |     297.15 \u2103      |\n
    "},{"location":"users_guide/framework_basics/the_affine_space/#no-text-output-for-points","title":"No text output for Points","text":"

    The library does not provide a text output for quantity points. The quantity stored inside is just an implementation detail of this type. It is a vector from a specific origin. Without the knowledge of the origin, the vector by itself is useless as we can't determine which point it describes.

    In the current library design, point origin does not provide any text in its definition. Even if we could add such information to the point's definition, we would not know how to output it in the text. There may be many ways to do it. For example, should we prepend or append the origin part to the quantity text?

    For example, the text output of 42 m for a quantity point may mean many things. It may be an offset from the mountain top, sea level, or maybe the center of Mars. Printing 42 m AMSL for altitudes above mean sea level is a much better solution, but the library does not have enough information to print it that way by itself.

    "},{"location":"users_guide/framework_basics/the_affine_space/#the-affine-space-is-about-type-safety","title":"The affine space is about type-safety","text":"

    The following operations are not allowed in the affine space:

    • adding two quantity_point objects
      • It is physically impossible to add positions of home and Denver airports.
    • subtracting a quantity_point from a quantity
      • What would it mean to subtract the DEN airport location from the distance to it?
    • multiplying/dividing a quantity_point with a scalar
      • What is the position of 2 * DEN airport location?
    • multiplying/dividing a quantity_point with a quantity
      • What would multiplying the distance with the DEN airport location mean?
    • multiplying/dividing two quantity_point objects
      • What would multiplying home and DEN airport location mean?
    • mixing quantity_points of different quantity kinds
      • It is physically impossible to subtract time from length.
    • mixing quantity_points of inconvertible quantities
      • What does subtracting a distance point to DEN airport from the Mount Everest base camp altitude mean?
    • mixing quantity_points of convertible quantities but with unrelated origins
      • How do we subtract a point on our trip to CppCon measured relatively to our home location from a point measured relative to the center of the Solar System?

    Important: The affine space improves safety

    The usage of quantity_point and affine space types, in general, improves expressiveness and type-safety of the code we write.

    "},{"location":"users_guide/framework_basics/value_conversions/","title":"Value Conversions","text":""},{"location":"users_guide/framework_basics/value_conversions/#value-preserving-conversions","title":"Value-preserving conversions","text":"
    auto q1 = 5 * km;\nstd::cout << q1.in(m) << '\\n';\nquantity<si::metre, int> q2 = q1;\n

    The second line above converts the current quantity to the one expressed in meters and prints its contents. The third line converts the quantity expressed in kilometers into the one measured in meters.

    In case a user would like to perform an opposite transformation:

    auto q1 = 5 * m;\nstd::cout << q1.in(km) << '\\n';\nquantity<si::kilo<si::metre>, int> q2 = q1;\n

    Both conversions will fail to compile.

    There are two ways to make the above work. The first solution is to use a floating-point representation type:

    auto q1 = 5. * m;\nstd::cout << q1.in(km) << '\\n';\nquantity<si::kilo<si::metre>> q2 = q1;\n

    or

    auto q1 = 5 * m;\nstd::cout << value_cast<double>(q1).in(km) << '\\n';\nquantity<si::kilo<si::metre>> q2 = q1;  // double by default\n

    Important

    The mp-units library follows std::chrono::duration logic and treats floating-point types as value-preserving.

    "},{"location":"users_guide/framework_basics/value_conversions/#value-truncating-conversions","title":"Value-truncating conversions","text":"

    The second solution is to force a truncating conversion:

    auto q1 = 5 * m;\nstd::cout << value_cast<km>(q1) << '\\n';\nquantity<si::kilo<si::metre>, int> q2 = q1.force_in(km);\n

    This explicit cast makes it clear that something unsafe is going on. It is easy to spot in code reviews or while chasing a bug in the source code.

    Note

    q.force_in(U) is just a shortcut to run value_cast<U>(q). There is no difference in behavior between those two interfaces. q.force_in(U) was added for consistency with q.in(U) and q.force_numerical_value_in(U).

    Another place where this cast is useful is when a user wants to convert a quantity with a floating-point representation to the one using an integral one. Again, this is a truncating conversion, so an explicit cast is needed:

    quantity<si::metre, int> q3 = value_cast<int>(3.14 * m);\n

    Info

    It is often OK to use an integral as a representation type, but in general, floating-point types provide better precision and are privileged in the library as they are considered to be value-preserving.

    In some cases, a unit and a representation type should be changed simultaneously. Moreover, sometimes, the order of doing those operations matters. In such cases, the library provides the value_cast<U, Rep>(q) which always returns the most precise result:

    C++23C++20Portable
    inline constexpr struct dim_currency final : base_dimension<\"$\"> {} dim_currency;\ninline constexpr struct currency final : quantity_spec<dim_currency> {} currency;\n\ninline constexpr struct us_dollar final : named_unit<\"USD\", kind_of<currency>> {} us_dollar;\ninline constexpr struct scaled_us_dollar final : named_unit<\"USD_s\", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;\n\nnamespace unit_symbols {\n\ninline constexpr auto USD = us_dollar;\ninline constexpr auto USD_s = scaled_us_dollar;\n\n}  // namespace unit_symbols\n\nusing Price = quantity_point<currency[us_dollar]>;\nusing Scaled = quantity_point<currency[scaled_us_dollar], zeroth_point_origin<currency>, std::int64_t>;\n
    inline constexpr struct dim_currency final : base_dimension<\"$\"> {} dim_currency;\ninline constexpr struct currency final : quantity_spec<currency, dim_currency> {} currency;\n\ninline constexpr struct us_dollar final : named_unit<\"USD\", kind_of<currency>> {} us_dollar;\ninline constexpr struct scaled_us_dollar final : named_unit<\"USD_s\", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;\n\nnamespace unit_symbols {\n\ninline constexpr auto USD = us_dollar;\ninline constexpr auto USD_s = scaled_us_dollar;\n\n}  // namespace unit_symbols\n\nusing Price = quantity_point<currency[us_dollar]>;\nusing Scaled = quantity_point<currency[scaled_us_dollar], zeroth_point_origin<currency>, std::int64_t>;\n
    inline constexpr struct dim_currency final : base_dimension<\"$\"> {} dim_currency;\nQUANTITY_SPEC(currency, dim_currency);\n\ninline constexpr struct us_dollar final : named_unit<\"USD\", kind_of<currency>> {} us_dollar;\ninline constexpr struct scaled_us_dollar final : named_unit<\"USD_s\", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;\n\nnamespace unit_symbols {\n\ninline constexpr auto USD = us_dollar;\ninline constexpr auto USD_s = scaled_us_dollar;\n\n}  // namespace unit_symbols\n\nusing Price = quantity_point<currency[us_dollar]>;\nusing Scaled = quantity_point<currency[scaled_us_dollar], zeroth_point_origin<currency>, std::int64_t>;\n
    using namespace unit_symbols;\nPrice price{12.95 * USD};\nScaled spx = value_cast<USD_s, std::int64_t>(price);\n

    As a shortcut, instead of providing a unit and a representation type to value_cast, you may also provide a Quantity type directly, from which unit and representation type are taken. However, value_cast<Quantity>, still only allows for changes in unit and representation type, but not changing the type of the quantity. For that, you will have to use a quantity_cast instead.

    Overloads are also provided for instances of quantity_point. All variants of value_cast<...>(q) that apply to instances of quantity have a corresponding version applicable to quantity_point, where the point_origin remains untouched, and the cast changes how the \"offset\" from the origin is represented. Specifically, for any quantity_point instance qp, all of the following equivalences hold:

    static_assert(value_cast<Rep>(qp) == quantity_point{value_cast<Rep>(qp.quantity_from(qp.point_origin)), qp.point_origin});\nstatic_assert(value_cast<U>(qp) == quantity_point{value_cast<U>(qp.quantity_from(qp.point_origin)), qp.point_origin});\nstatic_assert(value_cast<U, Rep>(qp) == quantity_point{value_cast<U, Rep>(qp.quantity_from(qp.point_origin)), qp.point_origin});\nstatic_assert(value_cast<Q>(qp) == quantity_point{value_cast<Q>(qp.quantity_from(qp.point_origin)), qp.point_origin});\n

    Furthermore, there is one additional overload value_cast<ToQP>(qp). This overload permits to additionally replace the point_origin with another compatible one, while still representing the same point in the affine space. Thus, it is roughly equivalent to value_cast<ToQP::unit, ToQP::rep>(qp).point_for(ToQP::point_origin). In contrast to a separate value_cast followed by point_for (or vice-versa), the combined value_cast tries to choose the order of the individual conversion steps in a way to avoid both overflow and unnecessary loss of precision. Overflow is a risk because the change of origin point may require an addition of a potentially large offset (the difference between the origin points), which may well be outside the range of one or both quantity types.

    "},{"location":"users_guide/framework_basics/value_conversions/#value-conversions-summary","title":"Value conversions summary","text":"

    The table below provides all the value conversion functions that may be run on x being the instance of either quantity or quantity_point:

    Forcing Representation Unit Member function Non-member function No Same u x.in(u) No T Same x.in<T>() No T u x.in<T>(u) Yes Same u x.force_in(u) value_cast<u>(x) Yes T Same x.force_in<T>() value_cast<T>(x) Yes T u x.force_in<T>(u) value_cast<u, T>(x) or value_cast<T, u>(x)"},{"location":"users_guide/systems/strong_angular_system/","title":"Strong Angular System","text":""},{"location":"users_guide/systems/strong_angular_system/#some-background-information","title":"Some background information","text":"

    As per today's SI, both radian and steradian are dimensionless. This forces the convention to set the angle 1 radian equal to the number 1 within equations (similar to what natural units system does for c constant).

    Following Wikipedia:

    Wikipedia: Radian - Dimensional analysis

    Giacomo Prando says \"the current state of affairs leads inevitably to ghostly appearances and disappearances of the radian in the dimensional analysis of physical equations.\" For example, a mass hanging by a string from a pulley will rise or drop by \\(y=r\u03b8\\) centimeters, where \\(r\\) is the radius of the pulley in centimeters and \\(\u03b8\\) is the angle the pulley turns in radians. When multiplying \\(r\\) by \\(\u03b8\\) the unit of radians disappears from the result. Similarly in the formula for the angular velocity of a rolling wheel, \\(\u03c9=v/r\\), radians appear in the units of \\(\u03c9\\) but not on the right hand side. Anthony French calls this phenomenon \"a perennial problem in the teaching of mechanics\". Oberhofer says that the typical advice of ignoring radians during dimensional analysis and adding or removing radians in units according to convention and contextual knowledge is \"pedagogically unsatisfying\".

    At least a dozen scientists have made proposals to treat the radian as a base unit of measure defining its own dimension of \"angle\", as early as 1936 and as recently as 2022. This would bring the advantages of a physics-based, consistent, and logically-robust unit system, with unambiguous units for all physical quantities. At the same time the only notable changes for typical end-users would be: improved units for the quantities torque, angular momentum, and moment of inertia.

    Paul Quincey in his proposal \"Angles in the SI: a detailed proposal for solving the problem\" states:

    Paul Quincey: Angles in the SI: a detailed proposal for solving the problem

    The familiar units assigned to some angular quantities are based on equations that have adopted the radian convention, and so are missing rads that would be present if the complete equation is used. The physically-correct units are those with the rads reinstated. Numerical values would not change, and the physical meanings of all quantities would also be unaffected.

    He proposes the following changes:

    • The radian would become either a new base unit or a 'complementary' unit
    • The steradian would become a derived unit equal to \\(1\\:rad^2\\)
    • The SI units for

      • Torque would change from \\(N\\:m\\) (\\(=J\\)) to \\(J\\:rad^{-1}\\)
      • Angular momentum would change from \\(J\\:s\\) to \\(J\\:s\\:rad^{-1}\\) (i.e. \\(J/(rad/s)\\))
      • Moment of inertia would change from \\(kg\\:m^2\\) to \\(kg\\:m^2\\:rad^{-2}\\) (i.e. \\(J/(rad/s)^2\\))
    • The option to omit the radian from the SI units for angle, angular velocity, angular frequency, angular acceleration, and angular wavenumber would be removed, the only correct SI units being \\(rad\\), \\(rad/s\\), \\(rad/s\\), \\(rad/s^2\\) and \\(rad/m\\) respectively.

    Paul Quincey summarizes that with the above in action:

    Paul Quincey: Angles in the SI: a detailed proposal for solving the problem

    However, the physical clarity this would build into the SI should be recognised very quickly. The units would tell us that \\(torque \\times angle = energy\\), and \\(angular\\:momentum \\times angle = action\\), for example, in the same way that they do for \\(force \\times distance = energy\\), \\(linear\\:momentum \\times distance = action\\), and \\(radiant\\:intensity \\times solid\\:angle = radiant\\:flux\\). Dimensional analysis could be used to its full extent. Software involving angular quantities would be rationalised. Arguments about the correct units for frequency and angular frequency, and the meaning of the unit \\(Hz\\), could be left behind. The explanation of these changes would be considerably easier and more rewarding than explaining how a kilogram-sized mass can be measured in terms of the Planck constant.

    "},{"location":"users_guide/systems/strong_angular_system/#angular-quantities-in-the-si","title":"Angular quantities in the SI","text":"

    Even though the SI somehow ignores the dimensionality of angle:

    SI Brochure

    Plane and solid angles, when expressed in radians and steradians respectively, are in effect also treated within the SI as quantities with the unit one. The symbols \\(rad\\) and \\(sr\\) are written explicitly where appropriate, in order to emphasize that, for radians or steradians, the quantity being considered is, or involves the plane angle or solid angle respectively. For steradians it emphasizes the distinction between units of flux and intensity in radiometry and photometry for example. However, it is a long-established practice in mathematics and across all areas of science to make use of \\(rad = 1\\) and \\(sr = 1\\). For historical reasons the radian and steradian are treated as derived units.

    It also explicitly states:

    SI Brochure

    The SI unit of frequency is hertz, the SI unit of angular velocity and angular frequency is radian per second, and the SI unit of activity is becquerel, implying counts per second. Although it is formally correct to write all three of these units as the reciprocal second, the use of the different names emphasizes the different nature of the quantities concerned. It is especially important to carefully distinguish frequencies from angular frequencies, because by definition their numerical values differ by a factor of \\(2\\pi\\). Ignoring this fact may cause an error of \\(2\\pi\\). Note that in some countries, frequency values are conventionally expressed using \u201ccycle/s\u201d or \u201ccps\u201d instead of the SI unit \\(Hz\\), although \u201ccycle\u201d and \u201ccps\u201d are not units in the SI. Note also that it is common, although not recommended, to use the term frequency for quantities expressed in \\(rad/s\\). Because of this, it is recommended that quantities called \u201cfrequency\u201d, \u201cangular frequency\u201d, and \u201cangular velocity\u201d always be given explicit units of \\(Hz\\) or \\(rad/s\\) and not \\(s^{-1}\\).

    "},{"location":"users_guide/systems/strong_angular_system/#strong-angular-extensions-in-the-library","title":"Strong Angular extensions in the library","text":"

    The mp-units library strives to define physically-correct quantities and their units to provide maximum help to its users. As treating angle as a dimensional quantity can lead to many \"trivial\" mistakes in dimensional analysis and calculation, it was decided to provide additional experimental systems of quantities and units that follow the above approach and treat angle as a base quantity with a base unit of radian and solid angle as its derived quantity.

    As those (at least for now) are not a part of SI, the plain angle and solid angle definitions can be found in a dedicated angular system. Those definitions are also used in the isq_angle system of quantities to make the recipes for angle-based quantities like torque or angular velocity physically correct:

    using namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\nusing mp_units::angular::unit_symbols::deg;\nusing mp_units::angular::unit_symbols::rad;\n\nconst quantity lever = isq_angle::position_vector(20 * cm);\nconst quantity force = isq_angle::force(500 * N);\nconst quantity angle = isq_angle::angular_measure(90. * deg);\nconst quantity torque = isq_angle::torque(lever * force * angular::sin(angle) / (1 * isq_angle::cotes_angle));\n\nstd::cout << \"Applying a perpendicular force of \" << force << \" to a \" << lever << \" long lever results in \"\n          << torque.in(N * m / rad) << \" of torque.\\n\";\n

    The above program prints:

    Applying a perpendicular force of 500 N to a 20 cm long lever results in 100 N m/rad of torque.\n

    Note

    cotes_angle is a constant which represents an angle with the value of exactly 1 radian. You can find more information about this constant in Quincey.

    Try it on Compiler Explorer

    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/","title":"Interoperability with Other Libraries","text":"

    mp-units makes it easy to cooperate with similar entities of other libraries. No matter if we want to provide interoperability with a simple home-grown strongly typed wrapper type (e.g., Meter, Timestamp, ...) or with a feature-rich quantities and units library, we have to provide specializations of:

    • a quantity_like_traits for external quantity-like type,
    • a quantity_point_like_traits for external quantity_point-like type.
    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/#specifying-a-conversion-kind","title":"Specifying a conversion kind","text":"

    Before we delve into the template specialization details, let's first decide if we want the conversions to happen implicitly or if explicit ones would be a better choice. Or maybe the conversion should be implicit in one direction only (e.g., into mp-units abstractions) while the explicit conversions in the other direction should be preferred?

    There is no one unified answer to the above questions. Everything depends on the use case.

    Typically, the implicit conversions are allowed in cases where:

    • both abstractions mean exactly the same, and interchanging them in the code should not change its logic,
    • there is no significant runtime overhead introduced by such a conversion (e.g., no need for dynamic allocation or copying of huge internal buffers),
    • the target type of the conversion provides the same or better safety to the users,
    • we prefer the simplicity of implicit conversions over safety during the (hopefully short) transition period of refactoring our code base from the usage of one library to the other.

    In all other scenarios, we should probably enforce explicit conversions.

    The kinds of inter-library conversions can be easily configured in partial specializations of conversion traits in the mp-units library. Conversion traits should provide a static data member convertible to bool. If the value is true, then the conversion is explicit. Otherwise, if the value is false, implicit conversions will be allowed. The names of the flags are as follows:

    • explicit_import to describe conversion from the external entity to the one in this library (import case),
    • explicit_export to describe conversion from the entity in this library to the external one (export case).
    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/#quantities-conversions","title":"Quantities conversions","text":"

    For example, let's assume that some company has its own Meter strong-type wrapper:

    struct Meter {\n  int value;\n};\n

    As every usage of Meter is at least as good and safe as the usage of quantity<si::metre, int>, and as there is no significant runtime performance penalty, we would like to allow the conversion to mp_units::quantity to happen implicitly.

    On the other hand, the quantity type is much safer than the Meter, and that is why we would prefer to see the opposite conversions stated explicitly in our code.

    To enable such interoperability, we must define a partial specialization of the quantity_like_traits<T> type trait. Such specialization should provide:

    • reference static data member that provides the quantity reference (e.g., unit),
    • rep type that specifies the underlying storage type,
    • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity type should happen explicitly (if true),
    • explicit_export static data member convertible to bool that specifies that the conversion from a quantity type to T should happen explicitly (if true),
    • to_numerical_value(T) static member function returning a quantity's raw value of rep type,
    • from_numerical_value(rep) static member function returning T.

    For example, for our Meter type, we could provide the following:

    template<>\nstruct mp_units::quantity_like_traits<Meter> {\n  static constexpr auto reference = si::metre;\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = true;\n  using rep = decltype(Meter::value);\n  static constexpr rep to_numerical_value(Meter m) { return m.value; }\n  static constexpr Meter from_numerical_value(rep v) { return Meter{v}; }\n};\n

    After that, we can check that the QuantityLike concept is satisfied:

    static_assert(mp_units::QuantityLike<Meter>);\n

    and we can write the following:

    void print(Meter m) { std::cout << m.value << \" m\\n\"; }\n\nint main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si::unit_symbols;\n\n  Meter height{42};\n\n  // implicit conversions\n  quantity h1 = height;\n  quantity<isq::height[m], int> h2 = height;\n\n  std::cout << h1 << \"\\n\";\n  std::cout << h2 << \"\\n\";\n\n  // explicit conversions\n  print(Meter(h1));\n  print(Meter(h2));\n}\n

    Note

    No matter if we decide to use implicit or explicit conversions, the mp-units will not allow unsafe operations to happen.

    If we extend the above example with unsafe conversions, the code will not compile, and we will have to fix the issues first before the conversion may be performed:

    UnsafeFixed
    quantity<isq::height[m]> h3 = height;\nquantity<isq::height[mm], int> h4 = height;\nquantity<isq::height[km], int> h5 = height;  // Compile-time error (1)\n\nstd::cout << h3 << \"\\n\";\nstd::cout << h4 << \"\\n\";\nstd::cout << h5 << \"\\n\";\n\nprint(Meter(h3));                            // Compile-time error (2)\nprint(Meter(h4));                            // Compile-time error (3)\nprint(Meter(h5));\n
    1. Truncation of value while converting from meters to kilometers.
    2. Conversion of double to int is not value-preserving.
    3. Truncation of value while converting from millimeters to meters.
    quantity<isq::height[m]> h3 = height;\nquantity<isq::height[mm], int> h4 = height;\nquantity<isq::height[km], int> h5 = quantity{height}.force_in(km);\n\nstd::cout << h3 << \"\\n\";\nstd::cout << h4 << \"\\n\";\nstd::cout << h5 << \"\\n\";\n\nprint(Meter(value_cast<int>(h3)));\nprint(Meter(h4.force_in(m)));\nprint(Meter(h5));\n
    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/#quantity-points-conversions","title":"Quantity points conversions","text":"

    To play with quantity point conversions, let's assume that we have a Timestamp strong type in our codebase, and we would like to start using mp-units to work with this abstraction.

    struct Timestamp {\n  int seconds;\n};\n

    As we described in The Affine Space chapter, timestamps should be modeled as quantity points rather than regular quantities.

    To allow the conversion between our custom Timestamp type and the quantity_point class template we need to provide the following in the partial specialization of the quantity_point_like_traits<T> type trait:

    • reference static data member that provides the quantity point reference (e.g., unit),
    • point_origin static data member that specifies the absolute point, which is the beginning of our measurement scale for our points,
    • rep type that specifies the underlying storage type,
    • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity type should happen explicitly (if true),
    • explicit_export static data member convertible to bool that specifies that the conversion from a quantity type to T should happen explicitly (if true),
    • to_numerical_value(T) static member function returning a raw value of the quantity being the offset of the point from the origin,
    • from_numerical_value(rep) static member function returning T.

    For example, for our Timestamp type, we could provide the following:

    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = true;\n  using rep = decltype(Timestamp::seconds);\n  static constexpr rep to_numerical_value(Timestamp ts) { return ts.seconds; }\n  static constexpr Timestamp from_numerical_value(rep v) { return Timestamp(v); }\n};\n

    After that, we can check that the QuantityPointLike concept is satisfied:

    static_assert(mp_units::QuantityPointLike<Timestamp>);\n

    and we can write the following:

    void print(Timestamp ts) { std::cout << ts.seconds << \" s\\n\"; }\n\nint main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si::unit_symbols;\n\n  Timestamp ts{42};\n\n  // implicit conversion\n  quantity_point qp = ts;\n\n  std::cout << qp.quantity_from_zero() << \"\\n\";\n\n  // explicit conversion\n  print(Timestamp(qp));\n}\n
    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/#interoperability-with-the-c-standard-library","title":"Interoperability with the C++ Standard Library","text":"

    In the C++ standard library, we have two types that handle quantities and model the affine space. Those are:

    • std::chrono::duration - specifies quantities of time,
    • std::chrono::time_point - specifies quantity points of time.

    The mp-units library comes with built-in interoperability with those types. It is enough to include the mp-units/systems/si/chrono.h file to benefit from it. This file provides:

    • partial specializations of quantity_like_traits and quantity_point_like_traits that provide support for implicit conversions between std and mp_units types in both directions,
    • chrono_point_origin<Clock> point origin for std clocks,
    • to_chrono_duration and to_chrono_time_point dedicated conversion functions that result in types exactly representing mp-units abstractions.

    Important

    Only a quantity_point that uses chrono_point_origin<Clock> as its origin can be converted to the std::chrono abstractions:

    inline constexpr struct ts_origin final : relative_point_origin<chrono_point_origin<system_clock> + 1 * h> {} ts_origin;\ninline constexpr struct my_origin final : absolute_point_origin<isq::time> {} my_origin;\n\nquantity_point qp1 = sys_seconds{1s};\nauto tp1 = to_chrono_time_point(qp1);  // OK\n\nquantity_point qp2 = chrono_point_origin<system_clock> + 1 * s;\nauto tp2 = to_chrono_time_point(qp2);  // OK\n\nquantity_point qp3 = ts_origin + 1 * s;\nauto tp3 = to_chrono_time_point(qp3);  // OK\n\nquantity_point qp4 = my_origin + 1 * s;\nauto tp4 = to_chrono_time_point(qp4);  // Compile-time Error (1)\n\nquantity_point qp5{1 * s};\nauto tp5 = to_chrono_time_point(qp5);  // Compile-time Error (2)\n
    1. my_origin is not defined in terms of chrono_point_origin<Clock>.
    2. zeroth_point_origin is not defined in terms of chrono_point_origin<Clock>.

    Here is an example of how interoperability described in this chapter can be used in practice:

    using namespace std::chrono;\n\nsys_seconds ts_now = floor<seconds>(system_clock::now());\n\nquantity_point start_time = ts_now;\nquantity speed = 925. * km / h;\nquantity distance = 8111. * km;\nquantity flight_time = distance / speed;\nquantity_point exp_end_time = start_time + flight_time;\n\nsys_seconds ts_end = value_cast<int>(exp_end_time.in(s));\n\nauto curr_time = zoned_time(current_zone(), ts_now);\nauto mst_time = zoned_time(\"America/Denver\", ts_end);\n\nstd::cout << \"Takeoff: \" << curr_time << \"\\n\";\nstd::cout << \"Landing: \" << mst_time << \"\\n\";\n

    The above may print the following output:

    Takeoff: 2023-11-18 13:20:54 UTC\nLanding: 2023-11-18 15:07:01 MST\n
    "},{"location":"users_guide/use_cases/wide_compatibility/","title":"Wide Compatibility","text":"

    The mp-units allows us to implement nice and terse code targeting a specific C++ version and configuration. Such code is easy to write and understand but might not be portable to some older environments.

    However, sometimes, we want to develop code that can be compiled on a wide range of various compilers and configurations. This is why the library also exposes and uses special preprocessor macros that can be used to ensure the wide compatibility of our code.

    Note

    Those macros are used in our short example applications as those are meant to be built on all of the supported compilers. Some still do not support std::format, C++ modules, or C++ versions newer than C++20.

    "},{"location":"users_guide/use_cases/wide_compatibility/#various-compatibility-options","title":"Various compatibility options","text":"

    Depending on your compiler's conformance, you can choose to use any of the below styles to write your code using mp-units:

    C++23C++20C++20 with header filesC++20 with header files + libfmtWide Compatibility
    #include <format>\n#include <iostream>\nimport mp_units;\n\n// ...\n\ninline constexpr struct horizontal_length final : quantity_spec<isq::length> {} horizontal_length;\n\n// ...\n\nstd::cout << std::format(...) << \"\\n\";\n
    #include <format>\n#include <iostream>\nimport mp_units;\n\n// ...\n\ninline constexpr struct horizontal_length final : quantity_spec<horizontal_length, isq::length> {} horizontal_length;\n\n// ...\n\nstd::cout << std::format(...) << \"\\n\";\n
    #include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <format>\n#include <iostream>\n\n// ...\n\ninline constexpr struct horizontal_length final : quantity_spec<horizontal_length, isq::length> {} horizontal_length;\n\n// ...\n\nstd::cout << std::format(...) << \"\\n\";\n
    #include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <fmt/format.h>\n#include <iostream>\n\n// ...\n\ninline constexpr struct horizontal_length final : quantity_spec<horizontal_length, isq::length> {} horizontal_length;\n\n// ...\n\nstd::cout << fmt::format(...) << \"\\n\";\n
    #include <iostream>\n#include <mp-units/ext/format.h>\n#ifdef MP_UNITS_MODULES\n#include <mp-units/compat_macros.h>\nimport mp_units;\n#else\n#include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#endif\n\n// ...\n\nQUANTITY_SPEC(horizontal_length, isq::length);\n\n// ...\n\nstd::cout << MP_UNITS_STD_FMT::format(...) << \"\\n\";\n

    Tip

    Depending on your preferences, you can either write:

    • terse code directly targeting your specific compiler's abilities,
    • verbose code using preprocessor branches and macros that provide the widest compatibility across various compilers.
    "},{"location":"users_guide/use_cases/wide_compatibility/#compatibility-macros","title":"Compatibility macros","text":"

    This chapter describes only the most essential tools the mp-units users need. All the compatibility macros can be found in the mp-units/compat_macros.h header file.

    Tip

    The mp-units/compat_macros.h header file is implicitly included when we use \"legacy\" headers in our translation units. However, it has to be explicitly included when we use C++20 modules, as those do not propagate preprocessor macros.

    "},{"location":"users_guide/use_cases/wide_compatibility/#QUANTITY_SPEC","title":"QUANTITY_SPEC(name, ...)","text":"

    Quantity specification definitions benefit from an explicit object parameter added in C++23 to remove the need for CRTP idiom, which significantly simplifies the code.

    This macro benefits from the new C++23 feature if available. Otherwise, it uses the CRTP idiom under the hood.

    "},{"location":"users_guide/use_cases/wide_compatibility/#mp_units_std_fmt","title":"MP_UNITS_STD_FMT","text":"

    Some of the supported compilers do not support std::format and related tools. Also, despite using a conformant compiler, some projects still choose to use fmtlib as their primary formatting facility (e.g., to benefit from additional features provided with the library).

    This macro resolves to either the std or fmt namespace, depending on the value of MP_UNITS_API_STD_FORMAT CMake option.

    To include the header files of the underlying text formatting framework, the following include should be used:

    #include <mp-units/ext/format.h>\n
    "},{"location":"users_guide/use_cases/wide_compatibility/#contracts","title":"Contracts","text":"

    The mp-units library internally does contract checking by default. It can be disabled with a Conan or CMake option. However, when enabled, it can use either gsl-lite or ms-gsl. To write a code that is independent from the underlying framework, the following preprocessor macros are exposed:

    • MP_UNITS_EXPECTS(expr)
    • MP_UNITS_EXPECTS_DEBUG(expr)
    • MP_UNITS_ASSERT(expr)
    • MP_UNITS_ASSERT_DEBUG(expr)

    Their meaning is consistent with respective gsl-lite.

    Also, to include the header files of the underlying framework, the following include should be used:

    #include <mp-units/ext/contracts.h>\n
    "},{"location":"users_guide/use_cases/working_with_legacy_interfaces/","title":"Working with Legacy interfaces","text":"

    In case we are working with a legacy/unsafe interface, we may need to extract the numerical value of a quantity and pass it to some third-party legacy unsafe interfaces.

    In such situations we can use .numerical_value_in(Unit) member function:

    void legacy_check_speed_limit(int speed_in_km_per_h);\n
    legacy_check_speed_limit((180 * km / (2 * h)).numerical_value_in(km / h));\n

    Such a getter will explicitly enforce the usage of a correct unit required by the underlying interface, which reduces a significant number of safety-related issues.

    The above code will not compile in case value truncation may happen. To solve the issue, we need to either use a value-preserving representation type or force the truncating conversion with .force_numerical_value_in(Unit):

    legacy_check_speed_limit((140 * mi / (2 * h)).force_numerical_value_in(km / h));\n

    The getters mentioned above always return by value as a quantity value conversion may be required to adjust it to the target unit. In case a user needs a reference to the underlying storage .numerical_value_ref_in(Unit) should be used:

    void legacy_set_speed_limit(int* speed_in_km_per_h) { *speed_in_km_per_h = 100; }\n
    quantity<km / h, int> speed_limit;\nlegacy_set_speed_limit(&speed_limit.numerical_value_ref_in(km / h));\n

    This member function again requires a target unit to enforce safety. This overload does not participate in overload resolution if the provided unit has a different scaling factor than the current one.

    "},{"location":"blog/archive/2024/","title":"2024","text":""},{"location":"blog/archive/2023/","title":"2023","text":""},{"location":"blog/category/metrology/","title":"Metrology","text":""},{"location":"blog/category/releases/","title":"Releases","text":""},{"location":"blog/category/wg21-updates/","title":"WG21 Updates","text":""},{"location":"blog/page/2/","title":"Blog","text":""},{"location":"blog/archive/2024/page/2/","title":"2024","text":""},{"location":"users_guide/examples/tags_index/","title":"Tags Index","text":"

    Note

    mp-units usage example applications are meant to be built on all of the supported compilers. This is why they benefit from the Wide Compatibility mode.

    Tip

    All usage examples in this chapter are categorized with appropriate tags to simplify navigation and search of relevant code. You can either read all the examples one-by-one in the order provided by the documentation authors or, thanks to the tagging system, jump straight to the example that is the most interesting for you.

    "},{"location":"users_guide/examples/tags_index/#affine-space","title":"Affine Space","text":"
    • hw_voltage
    "},{"location":"users_guide/examples/tags_index/#cgs-system","title":"CGS System","text":"
    • avg_speed
    "},{"location":"users_guide/examples/tags_index/#embedded","title":"Embedded","text":"
    • hw_voltage
    "},{"location":"users_guide/examples/tags_index/#international-system","title":"International System","text":"
    • avg_speed
    • hello_units
    "},{"location":"users_guide/examples/tags_index/#physical-constants","title":"Physical Constants","text":"
    • si_constants
    "},{"location":"users_guide/examples/tags_index/#text-formatting","title":"Text Formatting","text":"
    • hello_units
    • hw_voltage
    • si_constants
    "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to mp-units!","text":"

    mp-units is a compile-time enabled feature-rich Modern C++ modular/header-only library that provides compile-time dimensional analysis and unit/quantity manipulation. Its key strengths include safety, performance, and developer experience.

    The library source code is hosted on GitHub with a permissive MIT license.

    Supported compilers

    This library tries to provide the best user experience possible with the C++ language. To achieve that, it extensively uses the latest C++ language features.

    Even though the library benefits from the latest C++ versions (if available), C++20 is enough to compile and use all of the library's functionality.

    Please refer to C++ compiler support chapter for more details.

    C++ modulesHeader files
    #include <iostream>\n#include <print>\nimport mp_units;\n\nusing namespace mp_units;\n\ninline constexpr struct smoot final : named_unit<\"smoot\", mag<67> * usc::inch> {} smoot;\n\nint main()\n{\n  constexpr quantity dist = 364.4 * smoot;\n  std::println(\"Harvard Bridge length = {::N[.1f]} ({::N[.1f]}, {::N[.2f]}) \u00b1 1 \u03b5ar\",\n               dist, dist.in(usc::foot), dist.in(si::metre));\n}\n
    #include <mp-units/format.h>\n#include <mp-units/systems/si.h>\n#include <mp-units/systems/usc.h>\n#include <print>\n\nusing namespace mp_units;\n\ninline constexpr struct smoot final : named_unit<\"smoot\", mag<67> * usc::inch> {} smoot;\n\nint main()\n{\n  constexpr quantity dist = 364.4 * smoot;\n  std::println(\"Harvard Bridge length = {::N[.1f]} ({::N[.1f]}, {::N[.2f]}) \u00b1 1 \u03b5ar\",\n               dist, dist.in(usc::foot), dist.in(si::metre));\n}\n

    Output:

    Harvard Bridge length = 364.4 smoot (2034.6 ft, 620.14 m) \u00b1 1 \u03b5ar\n

    Try it on Compiler Explorer

    What is smoot?

    The smoot (/\u02c8smu\u02d0t/) is a nonstandard unit of length created as part of an MIT fraternity prank. It is named after Oliver R. Smoot, a fraternity pledge to Lambda Chi Alpha, who, in October 1958, lay on the Harvard Bridge (between Boston and Cambridge, Massachusetts) and was used by his fraternity brothers to measure the length of the bridge.

    One smoot equals Oliver Smoot's height at the time of the prank (five feet and seven inches). The bridge's length was measured to be 364.4 smoots plus or minus one ear, with the \"plus or minus\" intended to express the measurement uncertainty.

    Oliver Smoot graduated from MIT with the class of 1962, became a lawyer, and later became chairman of the American National Standards Institute (ANSI) and president of the International Organization for Standardization (ISO).

    More on the smoot unit of length can be found at https://en.wikipedia.org/wiki/Smoot.

    Important: Help needed!

    The mp-units library might be the subject of ISO standardization for C++29. More on this can be found in the following ISO C++ proposals:

    • P1935: A C++ Approach to Physical Units,
    • P2980: A motivation, scope, and plan for a physical quantities and units library,
    • P3045: Quantities and units library.

    We are actively looking for parties interested in field-trialing the library.

    "},{"location":"api_reference/","title":"API Reference - mp-units","text":""},{"location":"release_notes/","title":"Release Notes","text":""},{"location":"release_notes/#mp-units","title":"mp-units","text":""},{"location":"release_notes/#2.5.0","title":"2.5.0 WIP","text":""},{"location":"release_notes/#2.4.0","title":"2.4.0 November 5, 2024","text":"
    • (!) feat: phase_velocity and group_velocity aliases removed from ISQ by ISO
    • feat: iec::bit using-declared in iec::unit_symbols
    • feat: common unit symbols now use EQUIV{u1, u2, ...} syntax
    • feat: scaled_unit symbol printing improved ([] around the entire unit, small magnitude values do not use a power of 10 anymore)
    • feat: scaled_unit does not have a priority over derived_unit anymore
    • feat: fractional exponents support added to mag_power
    • feat: tag types are now required to be empty
    • feat: magnitude text now obeys formatting parameters and knows how to print constants
    • feat: added support for printing powers of magnitude constants
    • feat: SymbolicConstant concept added
    • feat: common_unit selection algorithm improved to make rev + rad return rad
    • feat: litre text symbol changed from l to L to avoid ambiguity with 1
    • feat: alternative litre unit symbol L added to prevent ambiguities with 1
    • feat: Unicode unit symbols
    • feat: \u03c0 added as an alias for pi
    • feat: expr_pow extended to remove redundancy in callers
    • feat: DerivedDimensionExpr, DerivedQuantitySpecExpr and DerivedUnitExpr removed
    • feat: MagnitudeSpecExpr and PowerVBase removed and some functions renamed to limit possible ambiguity in overload resolution
    • feat: std::is_object constraint applied to value_type_t
    • feat: quantity_values are now defined on top of std::chrono::duration_values
    • feat: from now on truncation is not allowed in compound assignment
    • feat(example): currency example now uses chrono::time_point and has better interfaces
    • feat(example): treat_as_floating_point specializations for examples' types removed
    • (!) refactor: all iec quantity specifications are now deprecated and moved to isq
    • (!) refactor: mag_constant now takes a symbol and a value and the class deriving from it must be final
    • (!) refactor: op==(U1, U2) now checks for the same type (old behavior available as equivalent(U1, U2)) + convertible now verifies associated quantity_spec as well
    • (!) refactor: ascii -> portable, unicode -> utf8, 'A' -> 'P'
    • (!) refactor: char_traits removed from fixed_string
    • (!) refactor: convertibility traits and concepts refactored to use bool flags instead of wrappers
    • (!) refactor: tag types should not expose their members
    • refactor: convertible(U1, U2) implementation simplified
    • refactor: abs moved to constexpr_math.h
    • refactor: unit_symbol_impl simplified
    • refactor: unit_symbol_formatting moved to a dedicated header file
    • refactor: shorten_T removed
    • refactor: magnitude interface cleanup
    • refactor: derived_from_the_same_base_dimension no longer needed
    • refactor: one_of usage removed from the fixed_string deduction guides
    • refactor: quantity.h is not needed in constants.h (unit.h is enough)
    • refactor: SameDimension concept is not needed and can be inlined in DimensionOf
    • refactor: framework Unicode symbols are now spelled using their codes
    • refactor: QuantitySpecWithNoSpecifiers removed and kind_of definition simplified
    • refactor: treat_as_floating_point simplified and extended to use std::chrono::treat_as_floating_point_v
    • refactor: wrapped_type_t reuses std::indirectly_readable_traits
    • refactor: expr_fractions takes direct OneType type now instead of a trait
    • refactor: Unicode symbols description reworked based on the latest SG16 recommendations
    • refactor: Mutable concept applied to quantity and quantity_point
    • refactor: explicit cleanup for deduction guides of quantity and quantity_point
    • refactor: point_origin_interface::op- cleanup
    • refactor: QuantityLikeImpl refactored to conform to API Reference by @JohelEGP
    • refactor: get_complexity refactored to be 0-based and not account for a number of arguments in a list
    • refactor: get_complexity refactored to returned maximum complexity of an element (instead of the sum of elements)
    • refactor(test): derived_quantity refactored to child_quantity
    • fix: missing are_ingredients_convertible overloads added
    • fix: constraints for magnitude added for scaled_unit and fixed common_unit instantiating it incorrectly
    • fix: according to ISO 80000-16 % should always be prefixed with space
    • fix: extraneous space in unit symbol having only denominators removed
    • fix: explicit cast added to less for magnitudes to fix clang-arm64 conversion error
    • fix: common_unit handling fixed for some corner cases
    • fix: math functions constraints fixed
    • fix: operator*(M, U u) fixed for U being scaled_unit
    • fix: subsumption of QuantityKindSpec fixed
    • fix: ValuePreservingTo fixed to apply std::remove_cvref_t on FromRep
    • fix: compound assignment operations on quantities now behave the same as on the underlying representation types
    • fix: QuantityConvertibleTo used in quantity_point compound assignment
    • fix: convertible_kinds implementation fixed
    • fix(tests): freestanding build fixed
    • test: unit tests for the inverse of mag_constant added
    • test: Unicode symbols used in unit tests
    • test: more std::chrono tests added
    • test: commutativity tests added to ISQ
    • test: one kind_of test added to reference tests
    • test: pow<0> and pow<1> tests added for dimensions
    • test: more get_common_quantity_spec tests added
    • build: setting of some test_package CMake options enabled only for a cxx_modules build
    • ci: sudo apt update added for documentation.yml in hope that it will resolve missing system packages issue
    • ci: MSVC added to the CI
    • ci: the latest not-released docs will use \"HEAD\" as a version from now on
    • ci: documentation action should run on a new tag as well
    • ci: documentation action limited only to commits that change documentation files
    • ci: non-documentation actions should not run on tags
    • docs: missing systems added to the \"Project Structure\" chapter
    • docs: graphs of ISQ kind hierarchies improved
    • docs: category of the ISO meeting reports changed to \"WG21 Updates\"
    • docs: blog comments support added
    • docs: Part 1-5 articles of the ISQ series added
    • docs: \"Symbols of scaled units\" chapter added + minor updates to scaled and common units chapters
    • docs: conan profile updated to present gcc-14 instead of gcc-12 which is no longer supported
    • docs: \"Why derived units order is not preserved from the multiplication?\" chapter added to FAQ
    • docs: \"Many shades of the same unit\" extended with a note about the derived units order
    • docs: API Reference now presents index.html instead of full.hml
    "},{"location":"release_notes/#2.3.0","title":"2.3.0 September 27, 2024","text":"
    • (!) feat: delta and absolute construction helpers
    • (!) feat: Conan and CMake options changed as requested at ConanCenter code review
    • (!) feat: unit_can_be_prefixed removed - from now on all named units can be prefixed
    • feat: formatting functions for units and dimensions marked as constexpr to enable compile-time text formatting
    • feat: qp1.quantity_from(qp2) added
    • feat: non-member swap added for fixed_string
    • feat: simplified inplace_vector added
    • feat: text output for angular units improved (space before symbol removed)
    • feat: representation type template parameter added to value conversion functions
    • feat: all tags in the expression templates are now ordered by the type names
    • feat: convertibility of a quantity with a unit one with the raw value added
    • feat: import std; support added
    • feat: compute values for rational magnitude powers (thanks @chiphogg)
    • feat: value_cast<Representation, Unit>() complementary conversion function added
    • feat: hw_voltage example added
    • feat: MP_UNITS_IMPORT_STD and MP_UNITS_MODULES handled properly in test_package.cpp
    • feat: complex quantity character added
    • feat: iec::var unit added
    • feat: type_list_unique added
    • feat: for_each on std::tuple added
    • feat: common_unit support added
    • (!) refactor: quantity_point_like_traits now use numerical value instead of the quantity
    • (!) refactor: iec80000 system renamed to iec
    • (!) refactor: mag_pi is now mag<pi>
    • (!) refactor: common_XXX() functions renamed to get_common_XXX()
    • refactor: error messages-related improvements
    • refactor: [[nodiscard]] and consteval set for some magnitude-related functions
    • refactor: degree Celsius and Fahrenheit symbols text now use Unicode codepoints
    • refactor: dimension_symbol and units_symbol refactored to use inplace_vector
    • refactor: unit_symbol and dimension_symbol refactored for readability and consteval
    • refactor: binary operators of quantity and quantity_point are now hidden friends
    • refactor: tag types-related operators are now hidden friends as well
    • refactor: ValuePreservingTo concept added
    • refactor: perfect forwarding interfaces improved
    • refactor: Representation concepts now requires WeaklyRegular instead of std::regular
    • refactor: quantity_point default-constructibility removed from the quantity_from constraints
    • refactor: has_common_type_v simplified
    • refactor: is_power_of_quantity_spec and is_power_of_dim variable templates converted to concepts
    • refactor: unnecessary custom versions of is_specialization_of removed
    • refactor: is_derived_from_specialization_of_v added and applied to remove custom traits
    • fix: signatures of capacity functions of fixed_string fixed
    • fix: MP_UNITS_API_NO_CRTP handling fixed
    • fix: MP_UNITS_HOSTED branch added to core.h
    • fix: mkdocs dependencies fixed in the gitpod dockerfile
    • fix: MP_UNITS_API_CONTRACTS should have a priority over headers availability
    • fix: si.h and angular.h now properly include hacks.h to define MP_UNITS_HOSTED before its usage
    • fix: quantity scaling between different prefixes improved
    • fix: conversion operator to std::chrono types fixed
    • fix: std::format does not always use Char* as iterators
    • fix: complex_power & co fixed
    • fix: __cpp_deleted_function workaround for clang-19 added
    • test: conversion to chrono unit tests added
    • test: unit tests for creating a quantity from a volatile variable
    • test: increase tolerance for certain math tests to two epsilon by @burnpanck
    • test: std::complex-based quantities tests added
    • build: conanfile bumped to use catch2/3.7.0
    • build: conanfile bumped to use fmt/11.0.1
    • build: minimum required CMake version bumped to 3.25
    • build: duplicated cmake_minimum_required commands removed
    • build: minimum conan version set to 2.0.15
    • build: MSVC compilation enabled for the library part by @czjhoppe
    • build: Dockerfile updated for gitpod
    • build: generate() in test_package now correctly propagates project's options
    • build: target_include_directories is not needed anymore
    • build: target_compile_features now uses CMAKE_CXX_STANDARD
    • build: package_type is dynamically set in conanfile.py depending if we build modules or not
    • build(conan): packaging improved
    • ci: added test for upstream clang on macos-14, as an example for an arm64 platform by @burnpanck
    • style: pre-commit updated to clang-format-18.1.8
    • docs: \"Strong Angular System\" chapter added
    • docs: docs updated to use new spelling for Conan options
    • docs: \"Interface Introduction\" chapter updated
    • docs: St. Louis 2024 report added
    • docs: \"Radians and degrees support\" added
    • docs: Rework of \"Getting Started\" section
    • docs: \"API Reference\" support added (thanks @JohelEGP)
    • docs: \"Generating API reference\" chapter added
    • docs: minor formatting and spelling improvements to the \"CONTRIBUTING.md\"
    • docs: \"Unit symbols\" chapter added
    • docs: examples line numbers fixed
    • docs: \"Text Formatting\" tag removed from avg_speed example
    • docs: line breaks in mermaid graphs fixed
    • docs: 2.3.0 release added
    "},{"location":"release_notes/#2.2.1","title":"2.2.1 July 3, 2024","text":"
    • (!) feat: Conan and CMake options refactored
    "},{"location":"release_notes/#2.2.0","title":"2.2.0 June 14, 2024","text":"
    • (!) feat: C++ modules support added by @JohelEGP
    • (!) feat: New formatting specification implemented
    • (!) feat: has_unit_symbol support removed
    • (!) feat: ABI concerns resolved with introduction of u8 strings for symbols
    • (!) feat: API-related Conan, CMake, and preprocessor options redesigned
    • (!) feat: core.h removed
    • (!) feat: from now on units, dimensions, quantity specifications, and point origins have to be marked as final
    • feat: implicit point origins support added
    • feat: unit default point origin support added
    • feat: fma, isfinite, isinf, and isnan math function added by @NAThompson
    • feat: fma for quantity points added
    • feat: quantity_point support added for quantity_cast and value_cast
    • feat: value_cast<Unit, Representation> added
    • feat: value_cast<Quantity>(q), value_cast<Quantity>(qp) and value_cast<QuantityPoint>(qp) added by @burnpanck
    • feat: interconvertible(QuantitySpec, QuantitySpec) added
    • feat: qp.quantity_from_zero() added
    • feat: value_type type trait added
    • feat: do not print space between a number and percent or per_mille
    • feat: ppm parts per million added by @nebkat
    • feat: atan2 2-argument arctangent added by @nebkat
    • feat: fmod floating-point division remainder added by @nebkat
    • feat: remainder IEEE division remainder added by @nebkat
    • feat: std::format support added
    • feat: unit text output support added
    • feat: formatting error messages improved
    • feat: improve types readability by eliminating extraneous () in references, prefixes, and kind_of
    • feat: dimension and unit text output added
    • feat: some light and radiation ISQ quantities added
    • feat: allow configuring GSL library use
    • feat: freestanding support added
    • (!) refactor: zero_Fahrenheit renamed to zeroth_degree_Fahrenheit
    • (!) refactor: SI-related trigonometric functions moved to the si subnamespace
    • (!) refactor: math.h header file broke up to smaller pieces
    • (!) refactor: fixed_string interface refactored
    • (!) refactor: ReferenceOf does not take a dimension anymore
    • (!) refactor: 'o' replaced with '1' as a modifier for unit_symbol_solidus::one_denominator
    • (!) refactor: get_kind() now returns kind_of
    • (!) refactor: FMT macros moved to compat_macros.h
    • (!) refactor: fixed_string refactored to reflect the latest changes to P3094R2
    • (!) refactor: basic_symbol_text renamed to symbol_text
    • (!) refactor: ratio hidden as an implementation detail behind mag_ratio
    • (!) refactor: framework.h introduced
    • (!) refactor: type list tools made an implementation detail of the library
    • (!) refactor: header files with the entire system definitions moved up in the directory tree
    • (!) refactor: absolute_point_origin does not use CRTP anymore
    • refactor: system's units do not inherit from one another anymore
    • refactor: all units made final
    • refactor: math functions constraints refactored
    • refactor: si_quantities.h added to improve compile-times
    • refactor: validate_ascii_string refactored to is_basic_literal_character_set
    • refactor: underlying_type split to wrapped_type and value_type and used in code
    • refactor: code refactored to comply with clang-tidy
    • refactor: remove dependency on <ranges> header and switch to use an iterator-based copy algorithm
    • refactor: terminate replaced with abort and a header file added
    • refactor: most std::remove_const_t removed and some replaced with the GCC-specific workaround
    • refactor: not needed remove_reference_t and remove_cvref_t removed
    • refactor: binary operators of quantity and quantity_point are now hidden friends
    • fix: QuantityLike conversions required Q::rep instead of using one provided by quantity_like_traits
    • fix: QuantitySpec[Unit] replaced with make_reference in value_cast
    • fix: ice_point is now defined with the integral offset from absolute_zero
    • fix: performance regression in sudo_cast fixed
    • fix: explicit object parameter support fixed
    • fix: missing version header file added to hacks.h
    • fix: quantity_cast to accept lvalue references (thanks @burnpanck)
    • fix: value_cast with lvalue references to quantity_point (thanks @burnpanck)
    • docs: project blog and first posts added
    • docs: project documentation layout refactored
    • docs: \"Interoperability with Other Libraries\" chapter added
    • docs: \"Framework Basics\" chapters updated and cleaned up
    • docs: smoot unit example added to the main page
    • docs: \"Code Example\" chapter renamed to \"Look and Feel\" and reordered in TOC to be after \"Quick Start\"
    • docs: \"Quick Start\" chapter reworked to be simpler and include quantity points
    • docs: \"Quantity points\" chapter extended
    • docs: \"The Affine Space\" chapter updated to reflect the recent design changes
    • docs: \"Working with Legacy interfaces\" chapter added
    • docs: \"Text Output\" chapter updated
    • docs: mkdocs social plugin enabled
    • docs: project logo and custom color scheme added
    • docs: minimum compiler requirements updated
    • docs: unit symbols admonition extended in the \"Quick Start\" chapter
    • docs: Cairo dependency described in the MkDocs section
    • docs: \"hello units\" example updated with dimensions output
    • docs: \"Text Output\" chapter updated with the recent formatting changes
    • docs: formatting grammar language changed to EBNF
    • docs: \"Project structure\" chapter expanded
    • docs: CITATION.cff updated
    • (!) build: Conan and CMake options refactored
    • (!) build: MP_UNITS_AS_SYSTEM_HEADERS renamed to MP_UNITS_BUILD_AS_SYSTEM_HEADERS
    • (!) build: MP_UNITS_BUILD_LA and MP_UNITS_IWYU CMake options now have _DEV_ in the name
    • build: gsl-lite updated to 0.41.0
    • build: catch2 updated to 3.5.1
    • build: fmt updated to 10.2.1
    • build: gitpod environment updated
    • build: check_cxx_feature_supported added
    • build: IWYU path handling fixed
    • build: IWYU enabled on GCC
    • build: CMAKE_EXPORT_COMPILE_COMMANDS flag enabled for the developer's build
    • build(conan): generate() now set cache_variables
    • build(conan): can_run check added before running tests
    • ci: Conan and CMake CI now use different cache names
    • ci: gcc-14 added
    • ci: clang-tidy CI added
    "},{"location":"release_notes/#2.1.1","title":"2.1.1 May 16, 2024","text":"
    • fix: unit tests compilation on gcc-14 fixed
    • fix: explicit this parameter support fixed
    "},{"location":"release_notes/#2.1.0","title":"2.1.0 December 9, 2023","text":"
    • (!) feat: inverse() support added for dimensions, quantity_spec, units, and references (1 / s will now create quantity and not a Unit)
    • (!) feat: quantity_point does not provide zero() anymore
    • (!) feat: quantity_spec and its kind should not compare equal
    • (!) feat: mutating interface removed from fixed_string
    • (!) feat: common_type with a raw value is not needed anymore as for a long time now raw values are not convertible to the dimensionless quantities
    • (!) feat: symbol_text definition simplified
    • (!) feat: users are now allowed to inherit their own types from absolute point origins
    • (!) feat: interoperability with other libraries redesigned
    • feat: basic_fixed_string(const CharT*, std::integral_constant<std::size_t, N>) constructor added
    • feat: isq::activity added and becquerel definition updated to benefit from it
    • feat: gray and sievert now have correct associated quantity kinds
    • feat: UnitCompatibleWith concept added and applied to in(U) and force_in(U) functions
    • feat: quantities can now be multiplied and divided by units (no parenthesis needed anymore)
    • feat: Magnitude / Unit operator added
    • feat: equality for dimensions now will allow derived classes as well (but not from derived_dimension)
    • feat: zero_Fahrenheit point origin added
    • feat: equivalent point origins handling improved
    • feat(example): unit symbols added to the currency example
    • (!) refactor: unit_symbol<fmt>(U) signature refactored and the resulting text can now also be used at runtime
    • (!) refactor: make_xxx factory functions replaced with two-parameter constructors
    • (!) refactor: unit_symbol changed to consteval
    • refactor: in(U) and force_in(U) now return auto to provide better diagnostics on clang
    • refactor: quantity operators constraints refactored
    • refactor: more type members added to fixed_string definition
    • refactor: unit_symbol_formatting enums now use std::int8_t as a representation type
    • fix: symbols of named dimensionless units with the ratio = 1 were not printed
    • fix: iterator is now properly updated for all cases in unit_symbol
    • fix: Fahrenheit conversion ratio was inverted
    • fix: CommonlyInvocableQuantities was overconstrained for the current library design
    • fix: are_ingredients_convertible now mandates explicit conversion for To dimensionless quantities
    • fix: quantity_point::point_for(PO) constraints fixed
    • fix(example): latitude and longitude fixed to include 0 for N and E respectively
    • ci: clang-17 enabled
    • ci: apple-clang-15 enabled
    • ci: Added C++23 builds to the CI matrix
    • docs: \"Getting Started\" chapters updated
    • docs: \"Basic Concepts\" and \"Interface Introduction\" chapters updated
    • docs: \"Design Overview\" chapter added and \"Concepts\" chapter reworked
    • docs: \"Output stream formatting\" chapter updated
    • docs: \"Default formatting\" chapter updated
    • docs: \"Derived unit symbols generation\" chapter added
    • docs: outdated affine space chapter updated
    • docs: CameCase concept identifiers FAQ added
    • docs: gravitational_potential_energy equation fixed on a graph
    • docs: YouTube video link updated to the C++ on Sea 2023
    • docs: ISO papers reference added to docs and README
    • docs: a representation type in a dimensionless quantity FAQ fixed
    • docs: titles added to some important admonitions
    • docs: \"Terms and Definitions\" slightly updated
    • docs: \"canonical unit\" added to glossary and its documentation in code was updated
    • docs: Design overview graph updated
    "},{"location":"release_notes/#2.0.0","title":"2.0.0 September 24, 2023","text":"
    • units namespace renamed to mp_units (#317)
    • header files in the <mp-units/...> rather then in <units/...> (#317)
    • the downcasting facility is removed (#383, #211, #32)
    • unified and simplified quantity creation (#274)
    • determining the best way to create a quantity (#413)
    • V2 quantity_point (#414)
    • introduction of quantity_spec to store not only dimension but also additional information about quantities (#405)
    • quantity now takes reference object, which aggregates quantity_spec and a unit and a representation type
    • units, prefixes, dimensions, quantity specifications, and references are passed as NTTPs to templates and provide arithmetic operations and comparison
    • expression templates consistently used in all derived types to increase the readability (#351, #166)
    • derived dimensions are now factors of only base dimensions (#281)
    • convertibility of derived quantities (#427)
    • dimensions, quantity specifications, units, and references are now composable, significantly reducing the number of definitions and resulting types
    • heavily simplified unit systems definitions (no need to define unnamed derived units, systems-specific dimensions, aliases for quantities, concepts, UDLs, ... anymore)
    • improved definition of all systems
    • support for ISO 80000 Part 3-6 quantities
    • faster than lightspeed constants (#169)
    • extensions to quantity formatting with fmt
    • quantity_kind removed
    • improved casting of unit with .in(Unit), .force_in(Unit) for quantity and quantity_spec
    • numerical value accessor safety improved with .numerical_value_in(Unit) and .force_numerical_value_in(Unit)
    • quantity can no longer be constructed with a raw value (#434)
    • Implicit construction of quantities from a value (#410)
    • quantity_point can no longer be constructed with just a quantity and an explicit PointOrigin is always needed
    • ceil and floor are dangerous (#432)
    • quecto, ronto, ronna, quetta new SI prefixes support
    • comparison against zero added (#487)
    • documentation rewritten from scratch
    • many smaller changes not possible to address with the previous design (#205, #210, #134)
    "},{"location":"release_notes/#0.8.0","title":"0.8.0 June 14, 2023","text":"
    • (!) refactor: common_quantity, common_quantity_for, common_quantity_point, common_quantity_kind, and common_quantity_point_kind removed
    • (!) refactor: named_derived_unit removed as it was not used
    • (!) refactor: derived_unit renamed to derived_scaled_unit
    • (!) refactor: unit renamed to derived_unit
    • (!) refactor: U::is_named removed from the unit types and replaced with NamedUnit concept
    • (!) refactor: PrefixFamily support removed
    • (!) refactor: mi(naut) renamed to nmi
    • (!) refactor: knot unit helper renamed to kn in FPS
    • (!) refactor: knot text symbol changed from \"knot\" to \"kn\"
    • refactor: quantity op+() and op-() reimplemented in terms of reference rather then quantity types
    • refactor(example): glide_computer now use dimensionless quantities with ranged_representation as rep
    • feat: HEP system support added (thanks @RalphSteinhagen)
    • feat: floor(), ceil(), and round() support added (thanks @hofbi)
    • feat: std::format support for compliant compilers added
    • feat: conversion helpers from mp-units to std::chrono types added
    • feat: math functions can now be safely used with user-defined types
    • feat: conversion from quantity_point to std::chrono::time_point added
    • feat: nautical_mile_per_hour and knot added to si::international system
    • (!) fix: add quantity_point::origin, like std::chrono::time_point::clock
    • fix: enable any prefixes for most of the named units (beside those that use prefixes already)
    • fix: hectare definition fixed to be a prefixed version of are + other units
    • fix: account for different dimensions in quantity_point_cast's constraint
    • fix: output stream operator now properly handles state
    • fix: fmt algorithms were overconstrained with forward_iterator
    • fix: CTAD for aliases fixed
    • fix: derived_ratio calculation
    • fix: fill_t assignment operator fixed
    • fix: improve downcast mode off
    • fix: radioactivity header compilation fixed
    • fix: si::hep::dim_momentum duplicated definition fixed
    • fix: fps can now coexist with international system
    • fix: public headers fixed to be standalone
    • test: standalone public headers tests added
    • (!) build: CMake generator in Conan is no longer obtained from an environment variable
    • (!) build: Required Conan version bumped to 1.48
    • (!) build: Conan 1.48 does not set CMAKE_BUILD_TYPE in the conan_toolchain.cmake anymore
    • build: AppleClang 13 support added (thanks @fdischner)
    • build: most of the conanfile.py refactored to be Conan 2.0 ready
    • build: validate() replaced with configure() to raise errors during conan install in Conan 1.X
    • build: minimum Conan version changed to 1.40
    • build: linear-algebra Conan repo is no needed anymore
    • build: Gitpod support added
    • build: clang-format-15 support added
    • build: export config to local build (#322)
    • build: fix export name of mp-units-system
    • build: fmt updated to 8.0.1
    • build: gsl-lite updated to 0.40.0
    • build: catch2 updated to 2.13.9
    • build: doxygen updated to 1.9.4
    • build: linear_algebra/0.7.0 switched to wg21-linear_algebra/0.7.2
    • ci: VS2022, gcc-11, clang-13, clang-14, and AppleClang 13 support added
    • ci: pre-commit support added (thanks @hofbi)
    • docs: Project documentation updated
    • docs: CITATION.cff file added
    • docs: CONTRIBUTING.md updated
    "},{"location":"release_notes/#0.7.0","title":"0.7.0 May 11, 2021","text":"
    • (!) refactor: ScalableNumber renamed to Representation
    • (!) refactor: output stream operators moved to the units/quantity_io.h header file
    • (!) refactor: Refactored the library file tree
    • (!) refactor: quantity::count() renamed to quantity::number()
    • (!) refactor: data system renamed to isq::iec80000 (quantity names renamed too)
    • (!) refactor: *deduced_unit renamed to *derived_unit
    • (!) refactor: got rid of a noble_derived_unit
    • refactor: quantity (kind) point updated to reflect latest changes to quantity
    • refactor: basic concepts, quantity and quantity_cast refactored
    • refactor: abs() definition refactored to be more explicit about the return type
    • feat: quantity (point) kind support added (thanks @johelegp)
    • feat: quantity references support added (thanks @johelegp)
    • feat: quantity aliases support addded
    • feat: interoperability with std::chrono::duration and other units libraries
    • feat: CTAD for dimensionless quantity added
    • feat: modulation_rate support added (thanks @go2sh)
    • feat: SI prefixes for isq::iec80000 support added (thanks @go2sh)
    • feat: a possibility to disable quantity UDLs support with UNITS_NO_LITERALS preprocessor define
    • feat: a support to define ISQ derived dimensions in terms of different number or order of components
    • perf: preconditions check do not influence the runtime performance of a Release build
    • perf: quantity_cast() generates less assembly instructions
    • perf: temporary string creation removed from quantity::op<<()
    • perf: value initialization for quantity value removed (left with a default initialization)
    • perf: limited the equivalent trait usage
    • perf: limited the C++ Standard Library headers usage
    • perf: rvalue references support added for constructors and getters
    • (!) fix: exp() has sense only for dimensionless quantities
    • (!) fix: dim_torque now properly divides by an angle (instead of multiply) + default unit name change
    • fix: quantity's operators fixed to behave like the underlying types do
    • fix: quantity_cast() fixed to work correctly with representation types not convertible from std::intmax_t
    • fix: ambiguous case for empty type list resolved
    • fix: downcasting facility for non-default-constructible types
    • fix: restore user-warnings within the library implementation
    • fix: the text symbol of foot_pound_force and foot_pound_force_per_second
    • fix: quantity modulo arithmetics fixed
    • (!) build: Conan testing version is now hosted on Artifactory
    • (!) build: Linear Algebra is now hosted on its Artifactory
    • (!) build: BUILD_DOCS CMake option renamed to UNITS_BUILD_DOCS
    • build: doxygen updated to 1.8.20
    • build: catch2 updated to 2.13.4
    • build: fmt updated to 7.1.3
    • build: ms-gsl replaced with gsl-lite/0.38.0
    • build: Conan generator switched to cmake_find_package_multi
    • build: Conan CMakeToolchain support added
    • build: CMake scripts cleanup
    • build: ccache support added
    • ci: CI switched from Travis CI to GitHub Actions
    "},{"location":"release_notes/#0.6.0","title":"0.6.0 September 13, 2020","text":"
    • feat: quantity_point support added (thanks @johelegp)
    • feat: Added angle as SI base dimension (thanks @kwikius)
    • feat: si::angular_velocity support added (thanks @mikeford3)
    • feat: FPS system added (thanks @mikeford3)
    • feat: Added support for mathematical function exp(quantity)
    • feat: Localization support for text output added (thanks @rbrugo)
    • feat: Added STL random number distribution wrappers (thanks @yasamoka)
    • (!) refactor: Refactored and cleaned up the library file tree
    • (!) refactor: q_* UDL renamed to _q_*
    • (!) refactor: UDLs with \"per\" in name renamed from *p* to *_per_*
    • (!) refactor: ratio changed to the NTTP kind
    • (!) refactor: exp and Exp renamed to exponent and Exponent
    • (!) refactor: Scalar concept renamed to ScalableNumber
    • (!) refactor: Dimensionless quantities redesigned to be of a quantity type
    • refactor: math.h function signatures refactored to use a Quantity concept (thanks @kwikius)
    • refactor: [[nodiscard]] added to many functions
    • fix: si::day unit symbol fixed to d (thanks @komputerwiz)
    • fix: si::mole unit symbol fixed to mol (thanks @mikeford3)
    • (!) build: gcc-9 is no longer supported (at least gcc-10 is required)
    • build: Visual Studio 16.7 support added
    • build: linear_algebra updated to 0.7.0/stable
    • build: fmt updated to 7.0.3
    • build: range-v3 updated to 0.11.0
    • build: catch2 updated to 2.13.0
    • build: doxygen updated to 1.8.18
    • build: ms-gsl 3.1.0 dependency added
    • build: Removed the dependency on a git submodule with common CMake scripts
    "},{"location":"release_notes/#0.5.0","title":"0.5.0 May 17, 2020","text":"
    • Major refactoring and rewrite of the library
    • Units are now independent from dimensions
    • Dimensions now depend on units (base or coherent units are provided in a class template)
    • Quantity gets a Dimension template parameter again (as unit does not provide information about its dimension anymore)
    • Spaceship operator support added
    • Added official CGS system support
    • Added official data information system support
    • Repository file tree cleanup
    • ratio refactored to contain Exp template parameter (thanks a lot @oschonrock!)
    • SI fundamental constants added
    • q_ prefix applied to all the UDLs (thanks @kwikius)
    • unknown_unit renamed to unknown_coherent_unit
    • Project documentation greatly extended and switched to Sphinx
    • A few more usage examples added
    • ASCII-only output support added (thanks @yasamoka)
    • Representation values formatting extended (thanks @rbrugo)
    • Output streams formatting support added
    • Linear algebra from std::experimental::math support added
    • Named SI units and their dimensions added (thanks @rbrugo
    • libfmt updated to 6.2.0
    • Added absolute functions and epsilon to math.h (thanks @mikeford3)
    • Added a lot of prefixes to named units and introduced alias_unit (thanks @yasamoka)
    • Linking with Conan targets only when they exists (#98)
    • All physical dimensions and units put into physical namespace
    • CMake improvements
    • Velocity renamed to speed

    Many thanks to GitHub users @oschonrock, @kwikius, and @i-ky for their support in drafting a new library design.

    "},{"location":"release_notes/#0.4.0","title":"0.4.0 Nov 17, 2019","text":"
    • Support for derived dimensions in exp added
    • Added pow() and sqrt() operations on quantities
    • units removed from a std::experimental namespace
    • Downcasting facility refactored so the user does not have to write the boilerplate code anymore
    • From now on base dimensions should inherit from base_dimension class template
    • Added unit symbols definitions to base_dimension and derived units
    • Added support for operator<< on quantity
    • fmt support added
    • Derived unit factory helpers refactored
    • Refactored the way prefixed units are defined
    "},{"location":"release_notes/#0.3.1","title":"0.3.1 Sep 18, 2019","text":"
    • cmcstl2 dependency changed to range-v3 0.9.1
    "},{"location":"release_notes/#0.3.0","title":"0.3.0 Sep 16, 2019","text":"
    • The design as described on CppCon 2019 talk (https://youtu.be/0YW6yxkdhlU)
    • Applied the feedback from the Cologne evening session
    • upcasting_traits renamed to downcasting_traits
    • Dimension template parameter removed from quantity
    • units moved to a std::experimental namespace
    • Leading underscore prefix removed from UDLs
    • Added a few more derived dimensions
    • meter renamed to metre
    • Missing operator* added
    • Predefined dimensions moved to a dedicated directory
    • dimension_ prefix removed from names of derived dimensions
    • cmcstl2 library updated to 2019.09.19
    • base_dimension is a value provided as const& to the exp type
    • integrated with Compiler Explorer
    • gsl-lite dependency removed
    • Fractional dimension exponents support added
    • QuantityOf concept introduced
    • quantity_cast<U, Rep>() support added
    "},{"location":"release_notes/#0.2.0","title":"0.2.0 July 18, 2019","text":"
    • The design as described on C++Now 2019 talk (https://youtu.be/wKchCktZPHU)
    • Added C++20 features supported by gcc-9.1 (std::remove_cvref_t, down with typename, std::type_identity)
    • Compile-time performance optimizations (type_list, common_ratio, ratio, conditional_t)
    "},{"location":"release_notes/#0.1.0","title":"0.1.0 May 18, 2019","text":"
    • Initial library release
    • Begin semantic versioning
    • The last version to work with gcc-8
    "},{"location":"appendix/glossary/","title":"Glossary","text":""},{"location":"appendix/glossary/#iso-definitions","title":"ISO definitions","text":"

    Note

    The ISO terms provided below are only a few of many defined in the ISO/IEC Guide 99.

    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 reference.
    • A reference can be a measurement unit, a measurement procedure, a reference material, or a combination of such.
    • A quantity as defined here is a scalar. However, a vector or a tensor, the components of which are quantities, is also considered to be a quantity.
    • The concept \u2019quantity\u2019 may be generically divided into, e.g. \u2018physical quantity\u2019, \u2018chemical quantity\u2019, and \u2018biological quantity\u2019, or \u2018base quantity\u2019 and \u2018derived quantity\u2019.
    • Examples of quantities are: length, radius, wavelength, energy, electric charge, etc.
    kind of quantity, kind
    • Aspect common to mutually comparable quantities.
    • The division of the concept \u2018quantity\u2019 into several kinds is to some extent arbitrary, for example:
      • the quantities diameter, circumference, and wavelength are generally considered to be quantities of the same kind, namely, of the kind of quantity called length,
      • the quantities heat, kinetic energy, and potential energy are generally considered to be quantities of the same kind, namely of the kind of quantity called energy.
    • Quantities of the same kind within a given system of quantities have the same quantity dimension. However, quantities of the same dimension are not necessarily of the same kind.
      • For example, the quantities moment of force and energy are, by convention, not regarded as being of the same kind, although they have the same dimension. Similarly for heat capacity and entropy, as well as for number of entities, relative permeability, and mass fraction.
    system of quantities
    • Set of quantities together with a set of non-contradictory equations relating those quantities.
    • Examples of systems of quantities are: the International System of Quantities, the Imperial System, etc.
    base quantity
    • 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 others.
    • Base quantities are referred to as being mutually independent since a base quantity cannot be expressed as a product of powers of the other base quantities.
    • \u2018Number of entities\u2019 can be regarded as a base quantity in any system of quantities.
    derived quantity
    • Quantity, in a system of quantities, defined in terms of the base quantities of that system.
    International System of Quantities, ISQ
    • System of quantities based on the seven base quantities: length, mass, time, electric current, thermodynamic temperature, amount of substance, and luminous intensity.
    • This system of quantities is published in the ISO 80000 and IEC 80000 series Quantities and units.
    • The International System of Units (SI) is based on the ISQ.
    quantity dimension, dimension of a quantity, dimension
    • Expression of the dependence of a quantity on the base quantities of a system of quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factor.
      • e.g. in the ISQ, the quantity dimension of force is denoted by \\(\\textsf{dim }F = \\mathsf{LMT}^{\u20132}\\).
    • A power of a factor is the factor raised to an exponent. Each factor is the dimension of a base quantity.
    • In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character.
    • In a given system of quantities:
      • quantities of the same kind have the same quantity dimension,
      • quantities of different quantity dimensions are always of different kinds,
      • quantities having the same quantity dimension are not necessarily of the same kind.
    • Symbols representing the dimensions of the base quantities in the ISQ are:

      Base quantity Symbol for dimension length \\(\\mathsf{L}\\) mass \\(\\mathsf{M}\\) time \\(\\mathsf{T}\\) electric current \\(\\mathsf{I}\\) thermodynamic temperature \\(\\mathsf{\u0398}\\) amount of substance \\(\\mathsf{N}\\) luminous intensity \\(\\mathsf{J}\\)

      Thus, the dimension of a quantity \\(Q\\) is denoted by \\(\\textsf{dim }Q = \\mathsf{L}^\u03b1\\mathsf{M}^\u03b2\\mathsf{T}^\u03b3\\mathsf{I}^\u03b4\\mathsf{\u0398}^\u03b5\\mathsf{N}^\u03b6\\mathsf{J}^\u03b7\\) where the exponents, named dimensional exponents, are positive, negative, or zero.

    quantity of dimension one, dimensionless quantity
    • quantity for which all the exponents of the factors corresponding to the base quantities in its quantity dimension are zero.
    • The term \u201cdimensionless quantity\u201d is commonly used and is kept here for historical reasons. It stems from the fact that all exponents are zero in the symbolic representation of the dimension for such quantities. The term \u201cquantity of dimension one\u201d reflects the convention in which the symbolic representation of the dimension for such quantities is the symbol \\(1\\).
    • The measurement units and values of quantities of dimension one are numbers, but such quantities convey more information than a number.
    • Some quantities of dimension one are defined as the ratios of two quantities of the same kind.
    • Numbers of entities are quantities of dimension one.
    measurement unit, unit of measurement, unit
    • Real scalar quantity, defined and adopted by convention, with which any other quantity of the same kind can be compared to express the ratio of the two quantities as a number.
    • Measurement units are designated by conventionally assigned names and symbols.
    • Measurement units of quantities of the same quantity dimension may be designated by the same name and symbol even when the quantities are not of the same kind.
      • For example, joule per kelvin and J/K are respectively the name and symbol of both a measurement unit of heat capacity and a measurement unit of entropy, which are generally not considered to be quantities of the same kind.
    • However, in some cases special measurement unit names are restricted to be used with quantities of specific kind only.
      • For example, the measurement unit \u2018second to the power minus one\u2019 (\\(\\mathsf{1/s}\\)) is called hertz (\\(\\mathsf{Hz}\\)) when used for frequencies and becquerel (\\(\\mathsf{Bq}\\)) when used for activities of radionuclides. As another example, the joule (\\(\\mathsf{J}\\)) is used as a unit of energy, but never as a unit of moment of force, e.g. the newton metre (\\(\\mathsf{N\u00b7m}\\)).
    • Measurement units of quantities of dimension one are numbers. In some cases, these measurement units are given special names, e.g. radian, steradian, and decibel, or are expressed by quotients such as millimole per mole equal to \\(10^{\u22123}\\) and microgram per kilogram equal to \\(10^{\u22129}\\).
    base unit
    • Measurement unit that is adopted by convention for a base quantity.
    • In each coherent system of units, there is only one base unit for each base quantity.
      • e.g. in the SI, the metre is the base unit of length. In the CGS systems, the centimetre is the base unit of length.
    • A base unit may also serve for a derived quantity of the same quantity dimension.
    • For number of entities, the number one, symbol \\(1\\), can be regarded as a base unit in any system of units.
    derived unit
    • Measurement unit for a derived quantity.
    • For example, the metre per second, symbol m/s, and the centimetre per second, symbol cm/s, are derived units of speed in the SI. The kilometre per hour, symbol km/h, is a measurement unit of speed outside the SI but accepted for use with the SI. The knot, equal to one nautical mile per hour, is a measurement unit of speed outside the SI.
    coherent derived unit
    • Derived 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.
    • A power of a base unit is the base unit raised to an exponent.
    • Coherence can be determined only with respect to a particular system of quantities and a given set of base units.
      • For example, if the metre, the second, and the mole are base units, the metre per second is the coherent derived unit of velocity when velocity is defined by the quantity equation \\(v = \\mathsf{d}r/\\mathsf{d}t\\), and the mole per cubic metre is the coherent derived unit of amount-of-substance concentration when amount-of-substance concentration is defined by the quantity equation \\(c = n/V\\). The kilometre per hour and the knot, given as examples of derived units, are not coherent derived units in such a system of quantities.
    • A derived unit can be coherent with respect to one system of quantities but not to another.
      • For example, the centimetre per second is the coherent derived unit of speed in a CGS system of units but is not a coherent derived unit in the SI.
    • The coherent derived unit for every derived quantity of dimension one in a given system of units is the number one, symbol \\(1\\). The name and symbol of the measurement unit one are generally not indicated.
    system of units
    • Set of base units and derived units, together with their multiples and submultiples, defined in accordance with given rules, for a given system of quantities.
    coherent system of units
    • System of units, based on a given system of quantities, in which the measurement unit for each derived quantity is a coherent derived unit.
    • A system of units can be coherent only with respect to a system of quantities and the adopted base units.
    • For a coherent system of units, numerical value equations have the same form, including numerical factors, as the corresponding quantity equations.
    off-system measurement unit, off-system unit
    • Measurement unit that does not belong to a given system of units.
    • For example, the electronvolt (about \\(1.602\\;18 \u00d7 10^{\u201319}\\;\\mathsf{J}\\)) is an off-system measurement unit of energy with respect to the SI. Day, hour, minute are off-system measurement units of time with respect to the SI.
    International System of Units, SI
    • System of units, based on the International System of Quantities, their names and symbols, including a series of prefixes and their names and symbols, together with rules for their use, adopted by the General Conference on Weights and Measures (CGPM).
    quantity value, value of a quantity, value
    • Number and reference together expressing magnitude of a quantity.
      • For example, length of a given rod: \\(5.34\\;\\mathsf{m}\\) or \\(534\\;\\mathsf{cm}\\).
    • The number can be complex.
    • A quantity value can be presented in more than one way.
    • In the case of vector or tensor quantities, each component has a quantity value.
      • For example, force acting on a given particle, e.g. in Cartesian components \\((F_x; F_y; F_z) = (\u221231.5; 43.2; 17.0)\\;\\mathsf{N}\\).
    numerical quantity value, numerical value of a quantity, numerical value
    • Number in the expression of a quantity value, other than any number serving as the reference
      • For example, in an amount-of-substance fraction equal to \\(3\\;\\mathsf{mmol/mol}\\), the numerical quantity value is \\(3\\) and the unit is \\(\\mathsf{mmol/mol}\\). The unit \\(\\mathsf{mmol/mol}\\) is numerically equal to \\(0.001\\), but this number \\(0.001\\) is not part of the numerical quantity value, which remains \\(3\\).
    quantity equation
    • Mathematical relation between quantities in a given system of quantities, independent of measure\u00adment units.
    • For example, \\(T = (1/2) mv^2\\) where \\(T\\) is the kinetic energy and \\(v\\) the speed of a specified particle of mass \\(m\\).
    unit equation
    • Mathematical relation between base units, coher\u00adent derived units or other measurement units.
    • For example, \\(\\mathsf{J} := \\mathsf{kg}\\:\\mathsf{m}^2/\\mathsf{s}^2\\), where, \\(\\mathsf{J}\\), \\(\\mathsf{kg}\\), \\(\\mathsf{m}\\), and \\(\\mathsf{s}\\) are the symbols for the joule, kilogram, metre, and second, respectively. (The symbol \\(:=\\) denotes \u201cis by definition equal to\u201d as given in the ISO 80000 and IEC 80000 series.). \\(1\\;\\mathsf{km/h} = (1/3.6)\\;\\mathsf{m/s}\\).
    numerical value equation, numerical quantity value equation
    • Mathematical relation between numerical quantity values, based on a given quantity equation and specified measurement units.
    • For example, in the quantity equation for kinetic energy of a particle, \\(T = (1/2) mv^2\\), if \\(m = 2\\;\\mathsf{kg}\\) and \\(v = 3\\;\\mathsf{m/s}\\), then \\({T} = (1/2)\\:\u00d7\\:2\\:\u00d7\\:3^2\\) is a numerical value equation giving the numerical value \\(9\\) of \\(T\\) in joules.
    "},{"location":"appendix/glossary/#other-definitions","title":"Other definitions","text":"

    Info

    The below terms extend the official ISO glossary and are commonly referred to by the mp-units library.

    base dimension
    • A dimension of a base quantity.
    derived dimension
    • A dimension of a derived quantity.
    • Implemented as an symbolic expression being the result of the dimension equation on base dimensions.
    dimension equation
    • Mathematical relation between dimensions in a given system of quantities, independent of measure\u00adment units.
    quantity kind hierarchy, quantity hierarchy
    • Quantities of the same kind form a hierarchy that determines their:
      • convertibility (e.g. every width is a length, but width should not be convertible to height)
      • common quantity type (e.g. width + height -> length)
    quantity character, character of a quantity, character
    • Scalars, vectors and tensors are mathematical objects that can be used to denote certain physical quantities and their values. They are as such independent of the particular choice of a coordinate system, whereas each scalar component of a vector or a tensor and each component vector and component tensor depend on that choice.
    • A vector is a tensor of the first order and a scalar is a tensor of order zero.
    • For vectors and tensors, the components are quantities that can be expressed as a product of a number and a unit.
    • Vectors and tensors can also be expressed as a numerical value vector or tensor, respectively, multiplied by a unit.
    • Quantities of different characters support different set of operations.
      • For example, a quantity can be multiplied by another one only if any of them has scalar character. Vectors and tensors can't be multiplied or divided, but they support additional operations like dot and cross products, which are not available for scalars.
    • The term \u2019character\u2019 was borrowed from the below quote:

    ISO 80000-1_2009

    In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character.

    quantity specification, quantity_spec
    • An entity storing all the information about a specific quantity:
      • location in a quantity hierarchy
      • quantity equation
      • dimension of a quantity
      • quantity kind
      • quantity character
      • additional constraints (e.g. non-negative)
    • Dimension of a quantity is not enough to specify all the properties of a quantity.
    unit with an associated quantity, associated unit
    • Unit that is used to measure quantities of a specific kind in a given system of units.
    quantity reference, reference
    • According to its definition, quantity can be expressed by means of a number and a reference
    • In the mp-units library, a reference describes all the required meta-information associated with a specific quantity (quantity specification and unit).
    canonical representation of a unit, canonical unit
    • A canonical representation of a unit consists of:
      • a reference unit being the result of extraction of all the intermediate derived units,
      • a magnitude being a product of all the prefixes and magnitudes of extracted scaled units.
    • All units having the same canonical unit are deemed equal.
    • All units having the same reference unit are convertible (their magnitude may differ and is used during conversion).
    reference unit

    See canonical representation of a unit

    absolute quantity point origin, absolute point origin
    • An explicit point on an axis of values of a specific quantity type that serves as an absolute reference point for all quantity points which definitions are (explicitly or implicitly) based on it.
    • For example, mean sea level is commonly used as an absolute reference point to measure altitudes.
    relative quantity point origin, relative point origin
    • An explicit, known at compile-time, point on an axis of values of a specific quantity type serving as a reference for other quantities.
    • For example, an ice point is a quantity point with a value of \\(273.15\\;\\mathsf{K}\\) that is used as the zero point of a degree Celsius scale.
    quantity point origin, point origin
    • Either an absolute point origin or a relative point origin.
    quantity point, absolute quantity
    • An absolute quantity with respect to an origin.
    • For example, timestamp (as opposed to duration), altitude (as opposed to height), absolute temperature (as opposed to temperature difference).
    "},{"location":"appendix/references/","title":"References","text":"ISO80000

    ISO 80000-1:2009(E) \"Quantities and units \u2014 Part 1: General\", International Organization for Standardization.

    Quincey \"Angles in the SI: a detailed proposal for solving the problem, Quincey, Paul (1 October 2021). SIBrochure

    The International System of Units (SI), International Bureau of Weights and Measures (20 May 2019), ISBN 978-92-822-2272-0.

    "},{"location":"blog/","title":"Blog","text":""},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/","title":"What's new in mp-units 2.0?","text":"

    After a year of hard work, we've just released mp-units 2.0.0. It can be obtained from GitHub and Conan.

    The list of the most significant changes introduced by the new version can be found in our Release Notes. We will also describe some of them in this post.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#why-20-if-10-was-never-released","title":"Why 2.0 if 1.0 was never released?","text":"

    Version 2 of the mp-units project is a huge change and a new quality for the users. We did not want to pretend that 2.0 is an evolutionary upgrade of the previous version of the project. It feels like a different product.

    We could start a new repo named \"mp-units-v2\" similarly to range-v3 but we decided not to go this path. We kept the same repo and made the scope of the changes and potential breakage explicit with a drastic bump in the project version.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#what-has-changed","title":"What has changed?","text":"

    The answer is \"nearly everything\". The whole library and its documentation were rewritten nearly from scratch.

    Here are the significant changes that the users can observe:

    • Repository name

      If you didn't notice, the repository name was changed from \"mpusz/units\" to \"mpusz/mp-units\".

    • Header files content and layout

      Previously, all the header files resided in the include/units directory. Now, they can be found in include/mp-units. The project file tree was significantly changed as well. Many files were moved to different subdirectories or renamed.

    • Namespace

      Previously, all the definitions were provided in the units namespace, and now they are in the mp_units one.

    • Abstractions, interfaces, definitions

      The interfaces of all of the types were refactored. We got unit symbols and a new way to construct a quantity and quantity_point. The readability of the generated types was improved thanks to the introduction of expression templates. Nearly all of the template arguments are now passed by values thanks to class NTTP extensions in C++20. As a result, unit definitions are much easier and terser. Also, the V2 has a powerful ability to model systems of quantities and provides definitions for many ISQ quantities.

    • Conan 2.0

      Also, now we support Conan 2.0, which provides an updated way of handling dependencies.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#what-is-gone","title":"What is gone?","text":"

    Some cornerstones of the initial design did not prove in practice and were removed while we moved to version 2.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#the-downcasting-facility","title":"The downcasting facility","text":"

    The first and the most important of such features was removing the downcasting facility. This feature is still a powerful metaprogramming technique that allows users to map long class template instantiations to nicely named, short, and easy-to-understand user's strong types.

    Such mapping works perfectly fine for 1-to-1 relationships. However, we often deal with N-to-1 connections in the quantities and units domain. Here are only a few such examples:

    • work and torque have the same dimension \\(L^2MT^{-2}\\),
    • becquerel and hertz have the same definition of \\(s^{-1}\\),
    • litre and cubic decimetre have the same factor.

    In the above examples, multiple entities \"wanted\" to register different names for identical class template instantiations, resulting in compile-time errors. We had to invent some hacks and workarounds to make it work, but we were never satisfied with the outcome.

    Additionally, this facility could easily lead to ODR violations or provide different results depending on which header files were included in the translation units. This was too vulnerable to be considered a good practice here.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#no-udls-anymore","title":"No UDLs anymore","text":"

    Over the years, we have learned that UDLs are not a good solution. More information on this subject can be found in the Why don't we use UDLs to create quantities? chapter.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#no-construction-of-a-quantity-from-a-raw-value","title":"No construction of a quantity from a raw value","text":"

    To improve safety, we no longer allow the construction of quantities from raw values. In the new design, we always need to explicitly specify a unit to create a quantity:

    quantity q1 = 42 * m;\nquantity<si::metre> = 2 * km;\nquantity q3(42, si::metre);\n

    The previous approach was reported to be error-prone under maintenance. More on this subject can be found in the Why can't I create a quantity by passing a number to a constructor? chapter.

    "},{"location":"blog/2023/09/24/whats-new-in-mp-units-20/#new-look-and-feel","title":"New look and feel","text":"

    Here is a concise example showing you the new look and feel of the library:

    #include <mp-units/format.h>\n#include <mp-units/systems/isq/isq.h>\n#include <mp-units/systems/si/si.h>\n#include <format>\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nquantity<isq::speed[m / s]> avg_speed(quantity<si::metre> d,\n                                      quantity<si::second> t)\n{ return d / t; }\n\nint main()\n{\n  auto speed = avg_speed(220 * km, 2 * h);\n  std::println(\"{}\", speed);  // 30.5556 m/s\n}\n

    All of the changes we provided, although breaking ones, resulted in much better, easier, and safer abstractions. These offer a new quantity on the market and hopefully will be appreciated by our users.

    Please check our new documentation to learn about the latest version of the project and find out how to benefit from all the new cool stuff we have here.

    "},{"location":"blog/2023/12/09/mp-units-210-released/","title":"mp-units 2.1.0 released!","text":"

    A new product version can be obtained from GitHub and Conan.

    The list of the most significant changes introduced by the new version can be found in our Release Notes. We will also describe the most important of them in this post.

    "},{"location":"blog/2023/12/09/mp-units-210-released/#no-more-parenthesis-while-creating-quantities-with-derived-units","title":"No more parenthesis while creating quantities with derived units","text":"

    The V2 design introduced a way to create a quantity by multiplying a raw value and a unit:

    quantity q1 = 42 * m;\n

    However, this meant that when we wanted to create a quantity having a derived unit, we had to put parenthesis around the unit equation or create a custom value of the named unit:

    quantity q2 = 60 * (km / h);\n\nconstexpr auto kmph = km / h;\nquantity q3 = 60 * kmph;\n\nquantity q4 = 50 * (1 / s);\n

    With the new version, we removed this restriction, and now we can type:

    quantity q5 = 60 * km / h;\nquantity q6 = 50 / s;\n

    As a side effect, we introduced a breaking change . We can't use the following definition of hertz anymore:

    inline constexpr struct hertz : named_unit<\"Hz\", 1 / second, kind_of<isq::frequency>> {} hertz;\n

    and have to type either:

    inline constexpr struct hertz : named_unit<\"Hz\", one / second, kind_of<isq::frequency>> {} hertz;\n

    or

    inline constexpr struct hertz : named_unit<\"Hz\", inverse(second), kind_of<isq::frequency>> {} hertz;\n

    To be consistent, we applied the same change to the dimensions and quantity specifications definitions. Now, to define a frequency we have to type:

    C++23C++20Portable
    inline constexpr struct frequency : quantity_spec<inverse(period_duration)> {} frequency;\n
    inline constexpr struct frequency : quantity_spec<frequency, inverse(period_duration)> {} frequency;\n
    QUANTITY_SPEC(frequency, inverse(period_duration));\n
    "},{"location":"blog/2023/12/09/mp-units-210-released/#make_xxx-factory-functions-replaced-with-two-parameter-constructors","title":"make_xxx factory functions replaced with two-parameter constructors","text":"

    In the initial version of the V2 framework, if someone did not like the multiply syntax to create a quantity we provided the make_quantity() factory function. A similar approach was used for quantity_point creation.

    This version removes those ( breaking change ) and introduces two parameter constructors:

    quantity q(42, si::metre);\nquantity_point qp(q, mean_sea_level);\n

    The above change encourages a better design and results in a terser code.

    "},{"location":"blog/2023/12/09/mp-units-210-released/#improved-definitions-of-becquerel-gray-and-sievert","title":"Improved definitions of becquerel, gray, and sievert","text":"

    In the initial V2 version, we lacked the definitions of the atomic and nuclear physics quantities, which resulted in simplified and unsafe definitions of becquerel, gray, and sievert units. We still do not model most of the quantities from this domain, but we've added the ones that are necessary for the definition of those units.

    Thanks to the above, the following expressions will not compile:

    quantity q1 = 1 * Hz + 1 * Bq;\nquantity<si::sievert> q2 = 42 * Gy;\n
    "},{"location":"blog/2023/12/09/mp-units-210-released/#compatibility-with-other-libraries-redesigned","title":"Compatibility with other libraries redesigned","text":"

    Another significant improvement in this version was redesigning the way we provide compatibility with other similar libraries. The interfaces of quantity_like_traits and quantity_point_like_traits were changed and extended to provide conversion not only from but also to entities from other libraries ( breaking change ).

    We've also introduced an innovative approach that allows us to specify if such conversions should happen implicitly or if they need to be forced explicitly.

    More on this subject can be found in the Interoperability with Other Libraries chapter.

    "},{"location":"blog/2023/12/09/mp-units-210-released/#point-origins-can-now-be-derived-from-each-other","title":"Point origins can now be derived from each other","text":"

    Previously, each class derived from absolute_point_origin was considered a unique independent point origin. On the other hand, it was OK to derive multiple classes from the same relative_point_origin, and those were specifying the same point in the domain. We found this confusing and limiting. This is why, in this version, the absolute_point_origin uses a CRTP idiom to be able to detect between points that should be considered different from the ones that should be equivalent.

    If we derive from the same instantiation of absolute_point_origin we end up with an equivalent point origin. This change allows us to provide different names for the same temperature points:

    inline constexpr struct absolute_zero : absolute_point_origin<absolute_zero, isq::thermodynamic_temperature> {} absolute_zero;\ninline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin;\n\ninline constexpr struct ice_point : relative_point_origin<absolute_zero + 273.15 * kelvin> {} ice_point;\ninline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius;\n

    Please note that this is a breaking change as well.

    "},{"location":"blog/2023/12/09/mp-units-210-released/#unit-symbol-text-can-now-be-properly-used-at-runtime","title":"Unit symbol text can now be properly used at runtime","text":"

    The interface of the previous definition of unit_symbol function allowed the use of the returned buffer only at compile-time. This was too limiting as users often want to use unit symbols at runtime (e.g., print them to the console). The new version redesigned the interface of this function ( breaking change ) to return a buffer that can be properly used at both compilation and runtime:

    std::string_view unit1 = unit_symbol(m / s);\nstd::cout << unit1 << \"\\n\";     // m/s\nstd::string_view unit2 = unit_symbol<{.solidus = unit_symbol_solidus::never}>(m / s);\nstd::cout << unit2 << \"\\n\";     // m s\u207b\u00b9\n
    "},{"location":"blog/2024/06/14/mp-units-220-released/","title":"mp-units 2.2.0 released!","text":"

    A new product version can be obtained from GitHub and Conan.

    Among other features, this release provides long-awaited support for C++20 modules, redesigns and enhances text output formatting, and greatly simplifies quantity point usage. This post describes those and a few other smaller interesting improvements, while a much longer list of the most significant changes introduced by the new version can be found in our Release Notes.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#c20-modules-and-project-structure-cleanup","title":"C++20 modules and project structure cleanup","text":"

    GitHub Issue #7 was our oldest open issue before this release. Not anymore. After 4.5 years, we finally closed it, even though the C++ modules' support is still really limited.

    Info

    To benefit from C++ modules, we need at least:

    • CMake 3.29.3
    • Ninja 1.11
    • clang-17

    In the upcoming months, hopefully, the situation will improve with the bug fixes in CMake, gcc-14, and MSVC.

    Note

    More requirements for C++ modules support can be found in the CMake's documentation.

    To enable the compilation and distribution of C++ modules, a cxx_modules Conan or MP_UNITS_BUILD_CXX_MODULES CMake option has to be enabled.

    With the above, the following C++ modules will be provided:

    flowchart TD\n    mp_units --- mp_units.systems --- mp_units.core
    C++ Module CMake Target Contents mp_units.core mp-units::core Core library framework and systems-independent utilities mp_units.systems mp-units::systems All the systems of quantities and units mp_units mp-units::mp-units Core + Systems

    The easiest way to use them is just to import mp_units; at the beginning of your translation unit (see the Quick Start chapter for some usage examples).

    Note

    C++20 modules support still have some issues when imported from the installed CMake target. See the following GitLab issue for more details.

    In this release, we also highly limited the number of CMake targets ( breaking change ). Now, they correspond exactly to the C++ modules they provide. This means that many smaller partial targets were removed. We also merged text output targets with the core library's definition.

    The table below specifies where we can now find the contents of previously available CMake targets:

    Before Now mp-units::utility mp-units::core mp-units::core-io mp-units::core mp-units::core-fmt mp-units::core mp-units::{system_name} mp-units::systems

    While we were enabling C++ modules, we also had to refactor our header files slightly ( breaking change ). Some had to be split into smaller pieces (e.g., math.h), while others had to be moved to a different subdirectory (e.g., chrono.h).

    In version 2.2, the following headers have a new location or contents:

    Header File C++ Module Contents mp-units/math.h mp_units.core System-independent functions only mp-units/systems/si/math.h mp_units.systems Trigonometric functions using si::radian mp-units/systems/angular/math.h mp_units.systems Trigonometric functions using angular::radian mp-units/systems/si/chrono.h mp_units.systems std::chrono compatibility traits

    Benefiting from this opportunity, we also cleaned up core and systems definitions ( breaking change ).

    Regarding the library's core, we removed core.h and exposed only one header framework.h that provides all of the library's framework so the user does not have to enumerate files like unit.h, quantity.h, and quantity_point.h anymore. Those headers are not gone, they were put to the mp-units/framework subheader. So they are still there if you really need them.

    Regarding the changes in systems definitions, we moved the wrapper header files with the entire system definition to the mp-units/systems subdirectory. Additionally, they now also include framework.h, so a system include is enough to use the library. Thanks to that our users don't have to write tedious code like the below anymore:

    NowBefore
    #include <mp-units/systems/cgs.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n\n// ...\n
    #include <mp-units/quantity_point.h>\n#include <mp-units/systems/cgs/cgs.h>\n#include <mp-units/systems/international/international.h>\n#include <mp-units/systems/isq/isq.h>\n#include <mp-units/systems/si/si.h>\n\n// ...\n

    Additionally, we merged all of the compatibility-related macros into one header file mp-units/compat_macros.h. This header file should be explicitly included before importing C++ modules if we want to benefit from the Wide Compatibility tools.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#better-control-over-the-librarys-api","title":"Better control over the library's API","text":"

    With this release, nearly all of the Conan and CMake build options were refactored with the intent of providing better control over the library's API.

    Previously, the library used the latest available feature set supported by a specific compiler. For example, quantity_spec definitions would use CRTP on an older compiler or provide a simpler API on a newer one thanks to the C++23 this deduction feature. This could lead to surprising results where the same code written by the user would compile fine on one compiler but not the other.

    From this release, all API extensions have their corresponding configuration options in Conan and CMake. With this, a user has full control over the API exposed by the library. Those options expose three values:

    • True - The feature is always enabled (the configuration error will happen if the compiler does not support this feature)
    • False - The feature is disabled, and an older alternative is always used.
    • Auto - The feature is automatically enabled if the compiler supports it (old behavior).

    Additionally, some CMake options were renamed to better express the impact on our users ( breaking change ). For example, now CMake options include:

    • MP_UNITS_API_* - options affecting the library's API,
    • MP_UNITS_BUILD_* - options affecting the build process,
    • MP_UNITS_DEV_* - options primarily useful for the project developers or people who want to compile our unit tests and examples.
    "},{"location":"blog/2024/06/14/mp-units-220-released/#configurable-contracts-checking","title":"Configurable contracts checking","text":"

    Before this release, the library always depended on gsl-lite to perform runtime contract and asserts checking. In this release we introduced new options to control if contract checking should be based on gsl-lite, ms-gsl, or may be completely disabled.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#freestanding-support","title":"Freestanding support","text":"

    From this release it is possible to configure the library in the freestanding mode. This limits the functionality and interface of the library to be compatible with the freestanding implementations.

    Info

    To learn more, please refer to the Build options chapter.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#simplified-quantity-point-support","title":"Simplified quantity point support","text":"

    This release significantly simplifies the usage of quantity points and affine space abstractions in general.

    Previously, the user always had to define an explicit point origin even if the domain being modeled does not have such an explicit origin. Now, in such cases, we can benefit from the implicit point origins. For example:

    NowBefore
    quantity_point price_usd{100 * USD};\n
    constexpr struct zero final : absolute_point_origin<currency> {} zero;\n\nquantity_point price_usd = zero + 100 * USD;\n

    As we can see above, the new design allows direct-initializing quantity_point class template from a quantity, but only if the former one is defined in terms of the implicit point origin. Otherwise, an explicit origin still always has to be provided during initialization.

    Also, we introduced the possibility of specifying a default point origin in the unit definition. With that, we could provide proper temperature scales without forcing the user to always use the origins explicitly. Also, a new member function, .quantity_from_zero(), was introduced that always returns the quantity from the unit's specific point origin or from the implicit point origin otherwise.

    NowBefore
    quantity_point temp{20 * deg_C};\nstd::cout << \"Temperature: \" << temp << \" (\"\n          << temp.in(deg_F).quantity_from_zero() << \", \"\n          << temp.in(K).quantity_from_zero() << \")\\n\";\n
    quantity_point temp = si::zeroth_degree_Celsius + 20 * deg_C;\nstd::cout << \"Temperature: \" << temp << \" (\"\n          << temp.in(deg_F).quantity_from(usc::zeroth_degree_Fahrenheit) << \", \"\n          << temp.in(K).quantity_from(si::zeroth_kelvin) << \")\\n\";\n

    More information about the new design can be found in The Affine Space chapter.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#unified-temperature-point-origins-names","title":"Unified temperature point origins names","text":"

    By omission, we had the following temperature point origins in the library:

    • si::zero_kelvin (for si::kelvin),
    • si::zeroth_degree_Celsius (for si::degree_Celsius),
    • usc::zero_Fahrenheit (for usc::degree_Fahrenheit).

    With this release, the last one was renamed to usc::zeroth_degree_Fahrenheit to be consistently named with its corresponding unit and with the si::zeroth_degree_Celsius ( breaking change ).

    "},{"location":"blog/2024/06/14/mp-units-220-released/#changes-to-units-definitions","title":"Changes to units definitions","text":"

    There were several known issues when units were deriving from each other (e.g., #512 and #537). We could either highly complicate the framework to allow these which could result in much longer compilation times or disallow inheriting from units at all. We chose the second option.

    With this release all of of the units must be marked as final. To enforce this we have changed the definition of the Unit<T> concept, which now requires type T to be final ( breaking change ).

    WG21 Study Group 16 (Unicode) raised concerns about potential ABI issues when different translation units are compiled with different ordinary literal encodings. Those issues were resolved with a change to units definitions ( breaking change ). It affects only units that specify both Unicode and ASCII symbols. The new design requires the Unicode symbol to be provided as a UTF-8 literal.

    This also means that the basic_symbol_text has fixed character types for both symbols. This is why it was renamed to symbol_text ( breaking change ).

    NowBefore
    inline constexpr struct ohm final : named_unit<symbol_text{u8\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n
    inline constexpr struct ohm : named_unit<basic_symbol_text{\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n

    Note

    On C++20-compliant compilers it should be enough to type the following in the unit's definition:

    inline constexpr struct ohm final : named_unit<{u8\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n
    "},{"location":"blog/2024/06/14/mp-units-220-released/#changes-to-dimension-quantity-specification-and-point-origins-definitions","title":"Changes to dimension, quantity specification, and point origins definitions","text":"

    Similarly to units, now also all dimensions, quantity specifications, and point origins have to be marked final ( breaking change ).

    NowBefore
    inline constexpr struct dim_length final : base_dimension<\"L\"> {} dim_length;\ninline constexpr struct length final : quantity_spec<dim_length> {} length;\n\ninline constexpr struct absolute_zero final : absolute_point_origin<isq::thermodynamic_temperature> {} absolute_zero;\ninline constexpr auto zeroth_kelvin  = absolute_zero;\ninline constexpr struct kelvin final : named_unit<\"K\", kind_of<isq::thermodynamic_temperature>, zeroth_kelvin> {} kelvin;\n\ninline constexpr struct ice_point final : relative_point_origin<quantity_point{273'150 * milli<kelvin>}> {} ice_point;\ninline constexpr auto zeroth_degree_Celsius = ice_point;\ninline constexpr struct degree_Celsius final : named_unit<symbol_text{u8\"\u2103\", \"`C\"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;\n
    inline constexpr struct dim_length : base_dimension<\"L\"> {} dim_length;\ninline constexpr struct length : quantity_spec<dim_length> {} length;\n\ninline constexpr struct absolute_zero : absolute_point_origin<absolute_zero, isq::thermodynamic_temperature> {} absolute_zero;\ninline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin;\ninline constexpr struct kelvin : named_unit<\"K\", kind_of<isq::thermodynamic_temperature>, zeroth_kelvin> {} kelvin;\n\ninline constexpr struct ice_point : relative_point_origin<quantity_point{273'150 * milli<kelvin>}> {} ice_point;\ninline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius;\ninline constexpr struct degree_Celsius : named_unit<symbol_text{u8\"\u2103\", \"`C\"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;\n

    Please also note, that the absolute_point_origin does not use CRTP idiom anymore ( breaking change ).

    "},{"location":"blog/2024/06/14/mp-units-220-released/#improved-text-output","title":"Improved text output","text":"

    With this release, we can print not only whole quantities but also just their units or dimensions. Also, we fixed the std::format support so users can now enjoy full C++20 compatibility and don't have to use fmtlib anymore.

    We have also changed the grammar for quantities formatting ( breaking change ). It introduces the composition of underlying formatters that finally allows us to properly format user-defined representation types (assuming they have std::format support). Additionally, thanks to a new %? token, we can provide a custom format string that will properly print quantity of any unit.

    Here is a small preview of what is now available:

    using namespace mp_units::si::unit_symbols;\nusing namespace mp_units::international::unit_symbols;\nquantity q = (90. * km / h).in(mph);\n\nstd::cout << \"Number: \" << q.numerical_value_in(mph) << \"\\n\";\nstd::cout << \"Unit: \" << q.unit << \"\\n\";\nstd::cout << \"Dimension: \" << q.dimension << \"\\n\";\nstd::println(\"{::N[.2f]}\", q);\nstd::println(\"{:.4f} in {} of {}\", q.numerical_value_in(mph), q.unit, q.dimension);\nstd::println(\"{:%N in %U of %D:N[.4f]}\", q);\n
    Number: 55.9234\nUnit: mi/h\nDimension: LT\u207b\u00b9\n55.92 mi/h\n55.9234 in mi/h of LT\u207b\u00b9\n55.9234 in mi/h of LT\u207b\u00b9\n

    More on this subject can be found in the updated Text Output chapter.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#improved-casts","title":"Improved casts","text":"

    We added a new conversion function. value_cast<Unit, Representation> forces the conversion of both a unit and representation type in one step and always ensures that the best precision is provided.

    Also, we have finally added proper implementations of value_cast and quantity_cast for quantity points.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#even-better-error-messages","title":"Even better error messages","text":"

    This release made a few small refactorings that, without changing the user-facing API, allowed us to improve the readability of the generated types that can be observed in the compilation errors.

    Example 1 (clang):

    NowBefore
    error: no matching function for call to 'time_to_goal'\n   26 |   const quantity ttg = time_to_goal(half_marathon_distance, pace);\n      |                        ^~~~~~~~~~~~\nnote: candidate template ignored: constraints not satisfied [with distance:auto = quantity<kilo_<metre>{}, double>,\n                                                                  speed:auto = quantity<derived_unit<second, per<kilo_<metre>>>{}, double>]\n   13 | QuantityOf<isq::time> auto time_to_goal(QuantityOf<isq::length> auto distance,\n      |                            ^\nnote: because 'QuantityOf<quantity<derived_unit<si::second, per<si::kilo_<si::metre> > >{{{}}}>, isq::speed>' evaluated to false\n   14 |                                         QuantityOf<isq::speed> auto speed)\n      |                                         ^\nnote: because 'QuantitySpecOf<std::remove_const_t<decltype(quantity<derived_unit<second, per<kilo_<metre> > >{{{}}}, double>::quantity_spec)>, struct speed{{{}}}>' evaluated to false\n   61 | concept QuantityOf = Quantity<Q> && QuantitySpecOf<std::remove_const_t<decltype(Q::quantity_spec)>, QS>;\n      |                                     ^\nnote: because 'implicitly_convertible(kind_of_<derived_quantity_spec<isq::time, per<isq::length> > >{}, struct speed{{{}}})' evaluated to false\n  147 |   QuantitySpec<T> && QuantitySpec<decltype(QS)> && implicitly_convertible(T{}, QS) &&\n      |                                                                         ^\n1 error generated.\nCompiler returned: 1\n
    error: no matching function for call to 'time_to_goal'\n   26 |   const quantity ttg = time_to_goal(half_marathon_distance, pace);\n      |                        ^~~~~~~~~~~~\nnote: candidate template ignored: constraints not satisfied [with distance:auto = quantity<kilo_<metre{{}}>{}, double>,\n                                                                  speed:auto = quantity<derived_unit<second, per<kilo_<metre{{}}>>>{}, double>]\n   13 | QuantityOf<isq::time> auto time_to_goal(QuantityOf<isq::length> auto distance,\n      |                            ^\nnote: because 'QuantityOf<quantity<derived_unit<si::second, per<si::kilo_<si::metre{{}}> > >{{{}}}>, isq::speed>' evaluated to false\n   14 |                                         QuantityOf<isq::speed> auto speed)\n      |                                         ^\nnote: because 'QuantitySpecOf<std::remove_const_t<decltype(quantity<derived_unit<second, per<kilo_<metre{{}}> > >{{{}}}, double>::quantity_spec)>, struct speed{{{}}}>' evaluated to false\n   61 | concept QuantityOf = Quantity<Q> && QuantitySpecOf<std::remove_const_t<decltype(Q::quantity_spec)>, QS>;\n      |                                     ^\nnote: because 'implicitly_convertible(kind_of_<derived_quantity_spec<isq::time, per<isq::length> >{{}, {{}}}>{}, struct speed{{{}}})' evaluated to false\n  147 |   QuantitySpec<T> && QuantitySpec<decltype(QS)> && implicitly_convertible(T{}, QS) &&\n      |                                                                         ^\n1 error generated.\nCompiler returned: 1\n

    Example 2 (gcc):

    NowBefore
    error: no matching function for call to 'Box::Box(quantity<reference<isq::height, si::metre>(), int>, quantity<reference<horizontal_length, si::metre>(), int>,\n                                                  quantity<reference<isq::width, si::metre>(), int>)'\n   27 | Box my_box(isq::height(1 * m), horizontal_length(2 * m), isq::width(3 * m));\n      |                                                                           ^\nnote: candidate: 'Box::Box(quantity<reference<horizontal_length, si::metre>()>, quantity<reference<isq::width, si::metre>()>,\n                                          quantity<reference<isq::height, si::metre>()>)'\n   19 |   Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n      |   ^~~\nnote:   no known conversion for argument 1 from 'quantity<reference<isq::height, si::metre>(),int>'\n        to 'quantity<reference<horizontal_length, si::metre>(),double>'\n   19 |   Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n
    error: no matching function for call to 'Box::Box(quantity<reference<isq::height(), si::metre()>(), int>, quantity<reference<horizontal_length(), si::metre()>(), int>,\n                                                  quantity<reference<isq::width(), si::metre()>(), int>)'\n   27 | Box my_box(isq::height(1 * m), horizontal_length(2 * m), isq::width(3 * m));\n      |                                                                           ^\nnote: candidate: 'Box::Box(quantity<reference<horizontal_length(), si::metre()>()>, quantity<reference<isq::width(), si::metre()>()>,\n                                          quantity<reference<isq::height(), si::metre()>()>)'\n   19 |   Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n      |   ^~~\nnote:   no known conversion for argument 1 from 'quantity<reference<isq::height(), si::metre()>(),int>'\n        to 'quantity<reference<horizontal_length(), si::metre()>(),double>'\n   19 |   Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n
    "},{"location":"blog/2024/06/14/mp-units-220-released/#mathh-header-changes","title":"math.h header changes","text":"

    This release provided lots of changes to the mp_units/math.h header file.

    First, we got several outstanding contributions:

    • fma, isfinite, isinf, and isnan math functions were added by @NAThompson,
    • ppm, atan2, fmod, and remainder were added by @nebkat.

    Thanks!

    Additionally, we changed the namespace for trigonometric functions using SI units. Now they are inside of the mp_units::si subnamespace and not in mp_units::isq like it was the case before ( breaking change ).

    Also, the header itself was split into smaller pieces that improve C++20 modules definitions.

    "},{"location":"blog/2024/06/14/mp-units-220-released/#ratio-made-an-implementation-detail-of-the-library","title":"ratio made an implementation detail of the library","text":"

    We decided not to expose ratio and associated interfaces in the public part of the library ( breaking change ). Standardization of it could be problematic as we have std::ratio already.

    Alternatively, as in the public interface it was always only used with mag, we introduced a new helper called mag_ratio to provide the magnitude of the unit defined in terms of a rational conversion factor. Here is a comparison of the code with previous and current definitions:

    NowBefore
    inline constexpr struct yard final : named_unit<\"yd\", mag_ratio<9'144, 10'000> * si::metre> {} yard;\ninline constexpr struct foot final : named_unit<\"ft\", mag_ratio<1, 3> * yard> {} foot;\n
    inline constexpr struct yard : named_unit<\"yd\", mag<ratio{9'144, 10'000}> * si::metre> {} yard;\ninline constexpr struct foot : named_unit<\"ft\", mag<ratio{1, 3}> * yard> {} foot;\n
    "},{"location":"blog/2024/09/27/mp-units-230-released/","title":"mp-units 2.3.0 released!","text":"

    A new product version can be obtained from GitHub and Conan.

    This release fine-tunes many key features of the library. This post describes the most interesting improvements, while a much longer list of the changes introduced by the new version can be found in our Release Notes.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#cmake-and-conan-options-changed","title":"CMake and Conan options changed","text":"

    During the review on the ConanCenter, we got feedback that we should improve the handling of options for which value is automatically determined based on the current configuration. Instead of explicitly setting the auto value, we defer the choice between True/False until the configuration stage and set it there once all the settings are known. auto value for such option was removed ( breaking change ).

    If you didn't set any value at the command line for such options, everything stays the same for you. However, some changes are needed if you explicitly used auto like below:

    conan install . -o 'mp-units:std_format=auto' -s compiler.cppstd=23 -b missing\n

    Now you have to either skip such an option to keep automatic deduction:

    conan install . -s compiler.cppstd=23 -b missing\n

    or set it explicitly to True or False to force a specific configuration:

    conan install . -o 'mp-units:std_format=True' -s compiler.cppstd=23 -b missing\n
    "},{"location":"blog/2024/09/27/mp-units-230-released/#msvc-compiler-support","title":"MSVC compiler support","text":"

    The MSVC compiler has the most bugs in the C++20 support from all our compilers. However, with this release, we could apply many workarounds to the library's code to make it work. \ud83c\udf89

    Please note those workarounds were only applied to the library's code and not to our unit tests and examples. Trying to build the entire project on MSVC will inevitably fail to compile unless the MSVC bugs are resolved. MSVC developers still have some work to do.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#representation-type-template-parameter-added-to-value-conversion-functions","title":"Representation type template parameter added to value conversion functions","text":"

    Previously, changing a representation type was only possible with a value_cast<NewRep>(q) non-member function while a change of unit was supported by all value_cast<NewU>(q), q.in(NewU), and q.force_in(NewU). The rationale for it was that passing an explicit type to a member function template requires a template disambiguator when we are dealing with a dependent name (e.g., quantity type is determined based on a template parameter).

    During a discussion in LEWGI at the St. Louis WG21 Meeting, we decided to provide such additional overloads despite possible issues when a dependent name is used. In such case, a user needs to provide a template disambiguator or switch back to using value_cast:

    // non-dependent name\nauto f(quantity<m, int> q)             { return q.in<double>(km); }\nauto g(quantity<m, int> q)             { return value_cast<double, km>(q); }\n\n// dependent name\nauto h(QuantityOf<isq::length> auto q) { return q.template in<double>(km); }\nauto i(QuantityOf<isq::length> auto q) { return value_cast<double, km>(q); }\n

    The table below provides all the value conversion functions in mp-units that may be run on x being the instance of either quantity or quantity_point:

    Forcing Representation Unit Member function Non-member function No Same u x.in(u) No T Same x.in<T>() No T u x.in<T>(u) Yes Same u x.force_in(u) value_cast<u>(x) Yes T Same x.force_in<T>() value_cast<T>(x) Yes T u x.force_in<T>(u) value_cast<u, T>(x) or value_cast<T, u>(x)"},{"location":"blog/2024/09/27/mp-units-230-released/#quantity-reference-specifiers","title":"Quantity reference specifiers","text":"

    The features described in this chapter directly solve an issue raised on std-proposals reflector. As it was reported, the code below may look correct, but it provides an invalid result:

    quantity Volume = 1.0 * m3;\nquantity Temperature = 28.0 * deg_C;\nquantity n_ = 0.04401 * kg / mol;\nquantity R_boltzman = 8.314 * N * m / (K * mol);\nquantity mass = 40.0 * kg;\nquantity Pressure = R_boltzman * Temperature.in(K) * mass / n_ / Volume;\nstd::cout << Pressure << \"\\n\";\n

    The problem is related to the accidental usage of a quantity rather than quantity_point for Temperature. This means that after conversion to kelvins, we will get 28 K instead of the expected 301.15 K, corrupting all further calculations.

    A correct code should use a quantity_point:

    quantity_point Temperature(28.0 * deg_C);\n

    This might be an obvious thing for domain experts, but new users of the library may not be aware of the affine space abstractions and how they influence temperature handling.

    After a lengthy discussion on handling such scenarios, we decided to:

    • make the above code deprecated ( breaking change ),
    • provide an alternative way to create a quantity with the delta quantity construction helper.

    Here are the main points of this new design:

    1. All references/units that specify point origin in their definition (i.e., si::kelvin, \u00a0 \u00a0si::degree_Celsius, and usc::degree_Fahrenheit) are excluded from the multiply syntax ( breaking change ).
    2. A new delta quantity construction helper is introduced:

      • delta<m>(42) results with a quantity<si::metre, int>,
      • delta<deg_C>(5) results with a quantity<si::deg_C, int>.
    3. A new absolute quantity point construction helper is introduced:

      • absolute<m>(42) results with a quantity_point<si::metre, zeroth_point_origin<kind_of<isq::length>>{}, int>,
      • absolute<deg_C>(5) results with a quantity<si::metre, si::ice_point, int>.

    Info

    Please note that si::kelvin is also excluded from the multiply syntax to prevent the following surprising issues:

    NowBefore
    quantity q = delta<K>(300);\nquantity_point qp = absolute<K>(300);\nstatic_assert(q.in(deg_C) != qp.in(deg_C).quantity_from_zero());\n
    quantity q(300 * K);\nquantity_point qp(300 * K);\nstatic_assert(q.in(deg_C) != qp.in(deg_C).quantity_from_zero());\n

    We believe that the code enforced with new utilities makes it much easier to understand what happens here.

    With such changes to the interface design, the offending code will not compile as initially written. Users will be forced to think more about what they write. To enable the compilation, the users have to create explicitly:

    • a quantity_point (the intended abstraction in this example) with any of the below syntaxes:

      quantity_point Temperature = absolute<deg_C>(28.0);\nauto Temperature = absolute<deg_C>(28.0);\nquantity_point Temperature(delta<deg_C>(28.0));\n
    • a quantity (an incorrect abstraction in this example) with:

      quantity Temperature = delta<deg_C>(28.0);\nauto Temperature = delta<deg_C>(28.0);\n

    Thanks to the new design, we can immediately see what happens here and why the result might be incorrect in the second case.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#quantity_point_like_traits-are-based-on-numerical-value-instead-of-a-quantity","title":"quantity_point_like_traits are based on numerical value instead of a quantity","text":"

    In this release, we decided to fine-tune the traits that customize the conversion between custom quantity point types and the ones provided with mp-units ( breaking change ).

    Previously, such type traits were based on the quantity type. This was inconsistent with quantity_like_traits, that is working on raw values. Also, there are cases where a custom quantity point abstraction is not modelled with a quantity type. In such cases, the previous approach required additional types to be introduced for no good reason.

    NowBefore
    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  using rep = decltype(Timestamp::seconds);\n\n  static constexpr convert_implicitly<rep> to_numerical_value(Timestamp ts)\n  {\n    return ts.seconds;\n  }\n\n  static constexpr convert_explicitly<Timestamp> from_numerical_value(rep v)\n  {\n    return Timestamp(v);\n  }\n};\n
    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  using rep = decltype(Timestamp::seconds);\n\n  static constexpr convert_implicitly<quantity<reference, rep>> to_quantity(Timestamp ts)\n  {\n    return ts.seconds * si::second;\n  }\n\n  static constexpr convert_explicitly<Timestamp> from_quantity(quantity<reference, rep> q)\n  {\n    return Timestamp(q.numerical_value_ref_in(si::second));\n  }\n};\n

    Note

    The old behavior is deprecated and will be removed in future releases.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#magpi","title":"mag<pi>","text":"

    With this release, we introduced a new strongly-typed constant to create a magnitude involving scaling by pi. The solution used before was not consistent with magnitudes of integral values and also was leaking a floating-point value of std::numbers::pi_v<long double> to the resulting magnitude type. With the new approach, this is no longer the case, and the user-facing interface is more consistent:

    NowBefore
    inline constexpr struct degree final : named_unit<{u8\"\u00b0\", \"deg\"}, mag<pi> / mag<180> * si::radian> {} degree;\n
    inline constexpr struct degree final : named_unit<{u8\"\u00b0\", \"deg\"}, mag_pi / mag<180> * si::radian> {} degree;\n

    Note

    The old mag_pi helper is marked as deprecated and will be removed in future releases.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#common-units","title":"Common units","text":"

    Adding or subtracting two quantities of different units will force the library to find a common unit for those. This is to prevent data truncation. For the cases when one of the units is an integral multiple of the another, the resulting quantity will use a \"smaller\" one in its result. For example:

    static_assert((1 * kg + 1 * g).unit == g);\nstatic_assert((1 * km + 1 * mm).unit == mm);\nstatic_assert((1 * yd + 1 * mi).unit == yd);\n

    However, in many cases an arithmetic on quantities of different units will result in a yet another unit. This happens when none of the source units is an integral multiple of another. In such cases, the library returns a special type that denotes that we are dealing with a common unit of such an equation.

    Previously we returned a scaled unit calculated against our arbitrarily appointed reference unit. This resulted often in a long and messy type exposing the prime-factorized magnitude of the unit (implementation detail). In this release, we introduced a new common_unit wrapper for such cases:

    NowBefore
    quantity q = 1 * km + 1 * mi;  // quantity<common_unit<international::mile, si::kilo_<si::metre>>{}, int>\n
    quantity q = 1 * km + 1 * mi;  // quantity<scaled_unit<magnitude<power_v<2, 3>{}, power_v<5, -3>{}>{}, si::metre>{}, int>\n

    Note

    A user should never explicitly instantiate a common_unit class template. The library's framework will do it based on the provided quantity equation.

    Such units need special printing rules for their symbols. As they represent a minimum set of common units resulting from the addition or subtraction of multiple quantities, from this release, we print all of them as a scaled version of the source unit. Previously we were printing them relative to some arbitrary reference unit (implementation detail) that often was not spelled by the user at all in the source code. For example the following:

    std::cout << 1 * km + 1 * mi << \"\\n\";\nstd::cout << 1 * nmi + 1 * mi << \"\\n\";\nstd::cout << 1 * km / h + 1 * m / s << \"\\n\";\n

    will print:

    NowBefore
    40771 ([1/25146] mi = [1/15625] km)\n108167 ([1/50292] mi = [1/57875] nmi)\n23 ([1/5] km/h = [1/18] m/s)\n
    40771 [8/125] m\n108167 [4/125] m\n23 [1/18] m/s\n

    Thanks to the above, it might be easier for the user to reason about the magnitude of the resulting unit and its impact on the value stored in the quantity.

    Info

    In order to provide common_unit strong type unit wrapper we had to rename all the common_XXX() functions to get_common_XXX() ( breaking change ).

    "},{"location":"blog/2024/09/27/mp-units-230-released/#superpowers-of-the-unit-one","title":"Superpowers of the unit one","text":"

    In this release, we also added a long-awaited change. From now on a quantity of a unit one can be:

    • implicitly constructed from the raw value,
    • explicitly converted to a raw value,
    • compared to a raw value.
    NowBefore
    quantity<one> inc(quantity<one> q) { return q + 1; }\nvoid legacy(double) { /* ... */ }\n\nif (auto q = inc(42); q != 0)\n  legacy(static_cast<int>(q));\n
    quantity<one> inc(quantity<one> q) { return q + 1 * one; }\nvoid legacy(double) { /* ... */ }\n\nif (auto q = inc(42 * one); q != 0 * one)\n  legacy(q.numerical_value_in(one));\n

    This property also expands to usual arithmetic operators.

    With the above change, we can now achieve the same results in a terser way:

    NowBefore
    static_assert(10 * km / (5 * km) == 2);\nconst quantity gain = 1. / index;\n
    static_assert(10 * km / (5 * km) == 2 * one);\nconst quantity gain = 1. / index * one;\n

    Note

    Those rules do not apply to all the dimensionless quantities. It would be unsafe and misleading to allow such operations on units with a magnitude different than 1 (e.g., percent or radian).

    "},{"location":"blog/2024/09/27/mp-units-230-released/#import-std-support","title":"import std; support","text":"

    This release brings experimental support for import std;. The only compiler that supports it for now is clang-18+. Until all the compilers start to support it and CMake removes the experimental tag from this feature, we will also keep it experimental.

    As all of the C++ compilers are buggy for now, it is not allowed to bring the same definitions through the import std; and regular header files. This applies not only to the current project but also to all its dependencies. This is why, in order to use it with mp-units, we need to disable all the dependencies as well (enforced with conanfile.py). It means that we have to use std::format (instead of fmtlib) and remove functions contract checking.

    With the above assumptions, we can refactor our smoot example to:

    import mp_units;\nimport std;\n\nusing namespace mp_units;\n\ninline constexpr struct smoot final : named_unit<\"smoot\", mag<67> * usc::inch> {} smoot;\n\nint main()\n{\n  constexpr quantity dist = 364.4 * smoot;\n  std::println(\"Harvard Bridge length = {::N[.1f]} ({::N[.1f]}, {::N[.2f]}) \u00b1 1 \u03b5ar\",\n               dist, dist.in(usc::foot), dist.in(si::metre));\n}\n
    "},{"location":"blog/2024/09/27/mp-units-230-released/#unit_can_be_prefixed-removed","title":"unit_can_be_prefixed removed","text":"

    Previously, the unit_can_be_prefixed type trait was used to limit the possibility to prefix some units that are officially known as non-prefixable (e.g., hour, minute).

    It turned out that it is not easy to determine whether some units can be prefixed. For example, for degree Celsius, the ISO 80000-5 standard explicitly states:

    Prefixes are not allowed in combination with the unit \u00b0C.

    On the other hand this NIST page says:

    Prefix symbols may be used with the unit symbol \u00baC and prefix names may be used with the unit name \u201cdegree Celsius.\u201d For example, 12 m\u00baC (12 millidegrees Celsius) is acceptable.

    It seems that it is also a common engineering practice.

    To prevent such issues, we decided to simplify the library's design and remove the unit_can_be_prefixed type trait ( breaking change ).

    From now on, every named unit in the library can be prefixed with the SI or IEC prefix.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#iec80000-system-renamed-to-iec","title":"iec80000 system renamed to iec","text":"

    As we mentioned IEC already, in this release, we decided to rename the name of the system and its corresponding namespace from iec80000 to iec ( breaking change ). This involves renaming of a defining header file and of the namespace it provides.

    With this change it should be easier to type the namespace name. This name is also more correct for some quantities and units that are introduced by IEC but not necessarily in the ISO/IEC 80000 series of documents (e.g., iec::var).

    Note

    The old iec80000 namespace in iec8000.h is marked as deprecated and will be removed in future releases.

    "},{"location":"blog/2024/09/27/mp-units-230-released/#error-messages-related-improvements","title":"Error messages-related improvements","text":"

    The readability of compile-time error messages is always a challenge for generic C++ libraries. However, for quantities and units library, generating readable errors is the most important requirement. If you do not make errors, you do not need such a library in your project .

    This is why we put lots of effort into improving here. Besides submitting compiler bugs to improve on their part, we also try to do our best here.

    Some compilers do not present the type resulting from calling a function within a template argument. This ends up with statements like get_quantity_spec(si::second{}) in the error message. Some less experienced users of the library may not know what this mean, and then why the conversion error happens.

    To improve this, we injected additional helper concepts into the definitions. It results with a bit longer but a more readable error in the end.

    For example:

    NowBefore
    error: no matching member function for call to 'in'\n   15 | const quantity time_to_goal = (distance * speed).in(s);\n      |                               ~~~~~~~~~~~~~~~~~~~^~\nnote: candidate template ignored: constraints not satisfied [with ToU = struct second]\n  221 |   [[nodiscard]] constexpr QuantityOf<quantity_spec> auto in(ToU) const\n      |                                                          ^\nnote: because 'detail::UnitCompatibleWith<si::second, unit, quantity_spec>' evaluated to false\n  219 |   template<detail::UnitCompatibleWith<unit, quantity_spec> ToU>\n      |            ^\nnote: because '!AssociatedUnit<si::second>' evaluated to false\n  164 |   (!AssociatedUnit<U> || UnitOf<U, QS>) && detail::UnitConvertibleTo<FromU, U{}>;\n      |    ^\nnote: and 'UnitOf<si::second, kind_of_<derived_quantity_spec<power<isq::length, 2>, per<isq::time> > >{}>' evaluated to false\n  164 |   (!AssociatedUnit<U> || UnitOf<U, QS>) && detail::UnitConvertibleTo<FromU, U{}>;\n      |                          ^\nnote: because 'detail::QuantitySpecConvertibleTo<get_quantity_spec(si::second{}), kind_of_<derived_quantity_spec<power<isq::length, 2>, per<isq::time> > >{}>' evaluated to false\n  141 |                  detail::QuantitySpecConvertibleTo<get_quantity_spec(U{}), QS> &&\n      |                  ^\nnote: because 'implicitly_convertible(kind_of_<struct time>{}, kind_of_<derived_quantity_spec<power<isq::length, 2>, per<isq::time> > >{})' evaluated to false\n  151 |   implicitly_convertible(From, To);\n      |   ^\n1 error generated.\nCompiler returned: 1\n
    error: no matching member function for call to 'in'\n   15 | const quantity time_to_goal = (distance * speed).in(s);\n      |                               ~~~~~~~~~~~~~~~~~~~^~\nnote: candidate template ignored: constraints not satisfied [with U = struct second]\n  185 |   [[nodiscard]] constexpr QuantityOf<quantity_spec> auto in(U) const\n      |                                                          ^\nnote: because 'detail::UnitCompatibleWith<si::second, unit, quantity_spec>' evaluated to false\n  183 |   template<detail::UnitCompatibleWith<unit, quantity_spec> U>\n      |            ^\nnote: because '!AssociatedUnit<si::second>' evaluated to false\n  207 |   (!AssociatedUnit<U> || UnitOf<U, QS>)&&(detail::have_same_canonical_reference_unit(U{}, U2));\n      |    ^\nnote: and 'UnitOf<si::second, kind_of_<derived_quantity_spec<power<isq::length, 2>, per<isq::time> > >{}>' evaluated to false\n  207 |   (!AssociatedUnit<U> || UnitOf<U, QS>)&&(detail::have_same_canonical_reference_unit(U{}, U2));\n      |                          ^\nnote: because 'implicitly_convertible(get_quantity_spec(si::second{}), kind_of_<derived_quantity_spec<power<length, 2>, per<time> > >{})' evaluated to false\n  187 |   implicitly_convertible(get_quantity_spec(U{}), QS) &&\n      |   ^\n1 error generated.\nCompiler returned: 1\n

    Note

    The above error messages were stripped a bit of the additional information (file name, namespace name, nested curlies) to provide better readability.

    "},{"location":"blog/2024/11/05/mp-units-240-released/","title":"mp-units 2.4.0 released!","text":"

    A new product version can be obtained from GitHub and Conan.

    This release was unexpected. We planned a significant new feature to happen next, but while preparing for it, and also while writing API Reference documentation, we made so many vital fixes and improvements that we decided that they deserve a dedicated release first.

    This post describes the most significant improvements while a much longer list of the changes introduced by the new version can be found in our Release Notes.

    "},{"location":"blog/2024/11/05/mp-units-240-released/#isq-quantities-cleanup","title":"ISQ quantities cleanup","text":"

    Initially, we kept quantities defined in \"IEC 80000-13: Information science and technology\" in a standalone iec80000 namespace, which was renamed to iec in the previous release. It turned out that this was incorrect. Those quantities are also a part of the ISQ. This is why, in this release, we moved all of them to the isq namespace ( breaking change ).

    From now on, iec namespace does not provide any quantities and serves purely as a system of units definition. It contains binary prefixes (based on the powers of two) and some units introduced by IEC (e.g., var, erlang, bit, or `baud).

    Note

    The quantities in iec namespace are now deprecated and will be removed in future releases.

    Also, it turns out that the latest ISO 80000-3 revision makes a small cleanup to the phase_speed and group_speed quantities. Those were always defined as scalar quantities but also had alternative names phase_velocity and group_velocity. This is misleading as velocity is typically considered a vector quantity. It is why those XXX_velocity aliases were removed from the ISO standard and from mp-units library ( breaking change ).

    "},{"location":"blog/2024/11/05/mp-units-240-released/#units-equality","title":"Units equality","text":"

    Previously we assumed that units like J, N m, and kg m\u00b2/s\u00b2 are equal. In some cases, this might not be entirely correct. Some quantities require a specific derived unit instead of a unit with a special name. For example:

    • N m should be used for moment of force (instead of J),
    • V A should be used for apparent power (instead of W).

    This is why, starting from this release units like J, N m, and kg m\u00b2/s\u00b2 will not compare equal ( breaking change ). However, they are deemed equivalent:

    static_assert(equivalent(J, N * m));\nstatic_assert(equivalent(W, V * A));\n
    "},{"location":"blog/2024/11/05/mp-units-240-released/#portable-text-output","title":"Portable text output","text":"

    From the very beginning, the text output of symbols could be formatted in two different ways:

    • Unicode,
    • portable using so-called ASCII alternatives.

    mp-units used the terms \"Unicode\" or \"ASCII\" and 'U' or 'A' formatting options for them. Even though those terms are widely understood in the C++ community, they are technically incorrect.

    During the recent SG16 meeting, we looked for proper alternatives and ended up with the \"portable\" and \"UTF-8\" terms ( breaking change ).

    From now on, we will provide the following:

    • text_encoding::utf8, symbol_text<N, M>::utf8(), and U formatting option,
    • text_encoding::portable, symbol_text<N, M>::portable(), and P formatting option.

    Note

    The old identifiers and formatting options are now deprecated and will be removed in future releases.

    "},{"location":"blog/2024/11/05/mp-units-240-released/#char_traits-removed-from-fixed_string","title":"char_traits removed from fixed_string","text":"

    During the same SG16 meeting, the room was strongly against providing char_traits for fixed_string. This is why char_traits support was removed in this release ( breaking change ).

    "},{"location":"blog/2024/11/05/mp-units-240-released/#improved-units-text-output","title":"Improved units' text output","text":"

    In the previous release, we introduced common unit abstraction. Initially, all its components were printed in parenthesis which contained a list of all the scaled units separated with =. After some feedback, we decided to change it to a new syntax.

    For example, the following:

    std::cout << 1 * km + 1 * mi << \"\\n\";\nstd::cout << 1 * nmi + 1 * mi << \"\\n\";\nstd::cout << 1 * km / h + 1 * m / s << \"\\n\";\n

    will print:

    NowBefore
    40771 EQUIV{[1/25146 mi], [1/15625 km]}\n108167 EQUIV{[1/50292 mi], [1/57875 nmi]}\n23 EQUIV{[1/5 km/h], [1/18 m/s]}\n
    40771 ([1/25146] mi = [1/15625] km)\n108167 ([1/50292] mi = [1/57875] nmi)\n23 ([1/5] km/h = [1/18] m/s)\n

    As we can see above, the scaled units output changed as well. Now, the entire scaled unit is encapsulated within [...] brackets to better denote its scope.

    Additionally, small magnitudes of scaled units do not use the power of 10, and also scaled units do not have a composition priority over the derived units anymore.

    As a result of those changes, the following:

    constexpr Unit auto L_per_100km = L / (mag<100> * km);\nstd::cout << 6.7 * L_per_100km << \"\\n\";\n

    prints:

    NowBefore
    6.7 L/[100 km]\n
    6.7 \u00d7 10\u207b\u00b2 l/km\n

    One more change that we can see above is that litre now use 'L' instead of 'l' for its symbol. The latter one too often is confused with the number 1.

    The next improvement adds proper formatting support for magnitudes. All of the formatting options that were working before for the unit symbols now also work for magnitudes.

    For example:

    using enum text_encoding;\nusing enum unit_symbol_solidus;\nusing usf = unit_symbol_formatting;\n\nstatic_assert(unit_symbol(mag<1> / (mag<2> * mag<pi>)*metre) == \"[2\u207b\u00b9 \ud835\udf0b\u207b\u00b9 m]\");\nstatic_assert(unit_symbol<usf{.solidus = always}>(mag<1> / (mag<2> * mag<pi>)*metre) == \"[1/(2 \ud835\udf0b) m]\");\nstatic_assert(unit_symbol<usf{.encoding = portable, .solidus = always}>(mag<1> / (mag<2> * mag<pi>)*metre) ==\n              \"[1/(2 pi) m]\");\n

    As we can see above, the library also learned how to print magnitude symbols. This required a change in the mag_constant definition. Now, it takes a magnitude symbol and has to be final like for other similar types in the library ( breaking change ):

    inline constexpr struct pi final : mag_constant<symbol_text{u8\"\u03c0\", \"pi\"}, std::numbers::pi_v<long double>> {} pi;\ninline constexpr auto \u03c0 = pi;\n
    "},{"location":"blog/2024/11/05/mp-units-240-released/#unicode-identifiers","title":"Unicode identifiers","text":"

    The example above introduced something interesting: a \u03c0 identifier for a variable. With the latest changes to the C++ language, we can officially use Unicode symbols as identifiers in the C++ code.

    In this release, we've added Unicode identifiers support not only for \u03c0 magnitude constant but also for unit symbols.

    Now we can type the following:

    With UTF-8 glyphsPortable
    quantity resistance = 60 * k\u03a9;\nquantity capacitance = 100 * \u00b5F;\n
    quantity resistance = 60 * kohm;\nquantity capacitance = 100 * uF;\n

    This might make the source code easier to understand, but typing those identifiers can be tricky. Sometimes, the best solution to type it might be a copy-paste approach. If we do not like this idea, we can still use old portable identifiers for those as well.

    "},{"location":"blog/2024/11/05/mp-units-240-released/#convertibility-with-quantitylike-and-quantitypointlike-entities","title":"Convertibility with QuantityLike and QuantityPointLike entities","text":"

    In this release, we decided to fine-tune further the traits that customize the conversion between custom quantity and quantity point types and the ones provided with mp-units ( breaking change ).

    Previously, to_numerical_value and from_numerical_value returned a type wrapped in a special tag type describing the conversion type (explicit or implicit).

    This was a novel and experimental approach. Finally, we decided not to do it and used a bit more verbose but a more standard solution. From now on, we need to provide two additional static data members of type bool:

    • explicit_import - true means that the conversion to the mp-units abstraction is explicit,
    • explicit_export - true means that the conversion from the mp-units abstraction is explicit.
    NowBefore
    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = true;\n  using rep = decltype(Timestamp::seconds);\n\n  static constexpr rep to_numerical_value(Timestamp ts)\n  {\n    return ts.seconds;\n  }\n\n  static constexpr Timestamp from_numerical_value(rep v)\n  {\n    return Timestamp(v);\n  }\n};\n
    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  using rep = decltype(Timestamp::seconds);\n\n  static constexpr convert_implicitly<rep> to_numerical_value(Timestamp ts)\n  {\n    return ts.seconds;\n  }\n\n  static constexpr convert_explicitly<Timestamp> from_numerical_value(rep v)\n  {\n    return Timestamp(v);\n  }\n};\n
    "},{"location":"blog/2024/11/05/mp-units-240-released/#symbolic-constants-implementation-should-be-implementation-defined","title":"Symbolic constants implementation should be implementation-defined","text":"

    In the process of writing API Reference, we decided to hide all the metadata associated with symbolic constants - tag types used to define units, dimensions, quantity specification, etc. ( breaking change ).

    All the types and values exposed by such types are needed only in the implementation details of the library. Users should not need them. Hiding those and making them implementation-defined gives other vendors the freedom to choose different ways to implement features of this library in their codebases.

    Important

    Based on Hyrum's Law some users may depend on this information already, and this release will break their code.

    If that is the case for you, we would love to hear about your use case and its rationale. It may mean that we should either:

    • extend the library's functionality to support your use case out of the box and keep those members hidden,
    • restore public visibility of such members and enforce this in the API Reference so that all the users of various library implementations may use them in the same way as you.
    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/","title":"International System of Quantities (ISQ): Part 1 - Introduction","text":"

    This post starts a series of articles about the International System of Quantities (ISQ). In this series, we will describe:

    • What is ISQ?
    • Which engineering problems does ISQ help to solve and how?
    • How to model and implement it in the programming language?
    • What is missing in the ISQ, and why is that a problem?
    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#terms-and-definitions","title":"Terms and Definitions","text":"

    From our experience, many people, including experts in the domain, often tend to name things differently, or sometimes they use the same term while having a different meaning in mind. This is why it is essential to stick to one well-defined glossary of terms for metrology.

    The mp-units project consistently uses the official metrology vocabulary defined by the ISO and BIPM:

    • International Organization for Standardization (ISO),
    • International Bureau of Weights and Measures (BIPM).

    The above are identical and contain the same set of definitions. We provide both to point out that the biggest institutions in standardizing metrology agree on the same vocabulary.

    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#systems-of-quantities-vs-systems-of-units","title":"Systems of Quantities vs Systems of Units","text":"

    Here are the official definitions from our vocabulary:

    System of quantities

    A system of quantities is a set of quantities together with a set of noncontradictory equations relating those quantities.

    System of units

    A system of units is a set of base units and derived units, together with their multiples and submultiples, defined in accordance with given rules, for a given system of quantities.

    From the definition above, we can find out that the systems of quantities and units form a hierarchy:

    flowchart TD\n    system_of_quantities[\"System of Quantities\"]\n    system_of_quantities --- system_of_units1[System of Units #1]\n    system_of_quantities --- system_of_units2[System of Units #2]\n    system_of_quantities --- system_of_units3[System of Units #3]

    System of quantities defines quantities commonly used in engineering (e.g., length, time, mass, speed, energy, power, etc.) and relations between them. It does not assign any specific units to those quantities, though.

    Systems of units are the ones that assign units of measurement to quantities from a specific system of quantities they chose to model. Different systems of units are free to chose whatever they find suitable for specific quantities and do not have to be consistent/compatible with other such systems. For example:

    • SI decided to measure length in meters, mass in kilograms, and time in seconds,
    • CGS decided to measure length in centimeters, mass in grams, and time in seconds.

    Both systems of units above agree on the unit of time, but chose different units for other quantities. In the above example, SI chose a non-prefixed unit of metre for a base quantity of length while CGS chose a scaled centimetre. On the other hand, SI chose a scaled kilogram over the gram used in the CGS. Those decisions also result in a need for different coherent units for derived quantities. For example:

    Quantity SI CGS length metre (m) centimetre (cm) mass kilogram (kg) gram (g) time second (s) second (s) force newton (N) dyne energy joule (J) erg pressure pascal (Pa) barye

    Often, there is no way to state which one is correct or which one is wrong. Each system of units has the freedom to choose whichever unit suits its engineering requirements and constraints the best for a specific quantity.

    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#isq-vs-si","title":"ISQ vs SI","text":"

    Some of the systems of quantities and units have been used more over the years and have become more popular than others. Here are the official descriptions of the most popular systems used in engineering today:

    International System of Quantities (ISQ)

    The International System of Quantities (ISQ) is a system of quantities based on the seven base quantities: length, mass, time, electric current, thermodynamic temperature, amount of substance, and luminous intensity.

    International System of Units (SI)

    The International System of Units (SI) is a system of units, based on the International System of Quantities, their names and symbols, including a series of prefixes and their names and symbols, together with rules for their use, adopted by the General Conference on Weights and Measures (CGPM).

    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#the-international-system-of-quantities-isq-standardization","title":"The International System of Quantities (ISQ) standardization","text":"

    The set of quantities constituting the ISQ is defined in the series of ISO 80000 and IEC 80000 standards under the general title \"Quantities and units\".

    ISO 80000:

    • Part 1: General
    • Part 2: Mathematical signs and symbols to be used in the natural sciences and technology
    • Part 3: Space and time
    • Part 4: Mechanics
    • Part 5: Thermodynamics
    • Part 7: Light
    • Part 8: Acoustics
    • Part 9: Physical chemistry and molecular physics
    • Part 10: Atomic and nuclear physics
    • Part 11: Characteristic numbers
    • Part 12: Condensed matter physics

    IEC 80000:

    • Part 6: Electromagnetism
    • Part 13: Information science and technology
    • Part 15: Logarithmic and related quantities, and their units
    • Part 16: Printing and writing rules
    • Part 17: Time dependency
    "},{"location":"blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will describe typical issues with libraries that do not model systems of quantities.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/","title":"International System of Quantities (ISQ): Part 2 - Problems when ISQ is not used","text":"

    This article is the next one in our series about the ISQ. After introducing the basic terms and systems, this article will talk about the issues we face when we base the quantities and units library on just units or dimensions.

    Note

    The issues described in this article do not apply to the mp-units library. Its interfaces, even if when we decide only to use simple quantities that only use units, those are still backed up by quantity kinds under the framework's hood.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#limitations-of-units-only-solutions","title":"Limitations of units-only solutions","text":"

    Units-only is not a good design for a quantities and units library. It works to some extent, but plenty of use cases can't be addressed, and for those that somehow work, we miss important safety improvements provided by additional abstractions in this article series.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#no-way-to-specify-a-quantity-type-in-generic-interfaces","title":"No way to specify a quantity type in generic interfaces","text":"

    A common requirement in the domain is to write unit-agnostic generic interfaces. For example, let's try to implement a generic avg_speed function template that takes a quantity of any unit and produces the result. If we call it with distance in km and time in h, we will get km/h as a result, but if we call it with mi and h, we expect mi/h to be returned.

    template<Unit auto U1, typename Rep1, Unit auto U2, typename Rep2>\nauto avg_speed(quantity<U1, Rep1> distance, quantity<U2, Rep2> time)\n{\n  return distance / time;\n}\n\nquantity speed = avg_speed(120 * km, 2 * h);\n

    This function works but does not provide any type safety to the users. The function arguments can be easily reordered on the call site. Also, we do not get any information about the return type of the function or any safety measures to ensure that the function logic actually returns a quantity of speed.

    To improve safety, with a units-only library, we have to write the function in the following way:

    template<typename Rep1, typename Rep2>\nquantity<si::metre / si::second, decltype(Rep1{} / Rep2{})> avg_speed(quantity<si::metre, Rep1> distance,\n                                                                      quantity<si::second, Rep2> time)\n{\n  return distance / time;\n}\n\navg_speed(120 * km, 2 * h).in(km / h);\n

    Despite being safer, the above code decreased the performance because we always pay for the conversion at the function's input and output.

    Moreover, in a good library, the above code should not compile. The reason for this is that even though the conversion from km to m and from h to s is considered value-preserving, it is not true in the opposite direction. When we try to convert the result stored in an integral type from the unit of m/s to km/h, we will inevitably lose some data.

    We could try to provide concepts like ScaledUnitOf<si::metre> that would take a set of units while trying to constrain them somehow, but it leads to even more problems with the unit definitions. For example, are Hz and Bq just scaled versions of 1/s? If we constrain the interface to just prefixed units, then litre and a cubic metre or kilometre and mile will be incompatible. What about radian and steradian or a litre per 100 kilometre (popular unit of a fuel consumption) and a squared metre? Should those be compatible?

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#disjoint-units-of-the-same-quantity-type-do-not-work","title":"Disjoint units of the same quantity type do not work","text":"

    Sometimes, we need to define several units describing the same quantity but which should not convert to each other in the library's framework. A typical example here is currency. A user may want to define EURO and USD as units of currency, so both of them can be used for such quantities. However, it is impossible to predefine one fixed conversion factor for those, as a currency exchange rate varies over time, and the library's framework can't provide such an information as an input to the built-in conversion function. User's application may have more information in this domain and handle such a conversion at runtime with custom logic (e.g., using an additional time point function argument). If we would like to model that in a unit-only solution, how can we specify that EURO and USD are units of quantities of currency, but are not convertible to each other?

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#dimensions-to-the-rescue","title":"Dimensions to the rescue?","text":"

    To resolve the above issues, most of the libraries on the market introduce dimension abstraction. Thanks to that, we could solve the first issue of the previous chapter with:

    QuantityOf<dim_speed> auto avg_speed(QuantityOf<dim_length> auto distance,\n                                     QuantityOf<dim_time> auto time)\n{\n  return distance / time;\n}\n

    and the second one by specifying that both EURO and USD are units of dim_currency. This is a significant improvement but still has some issues.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#limitations-of-dimensions","title":"Limitations of dimensions","text":"

    Let's first look at the above solution again. A domain expert seeing this code will immediately say there is no such thing as a speed dimension. The ISQ specifies only 7 dimensions with unique symbols assigned, and the dimensions of all the ISQ quantities are created as a vector product of those. For example, a quantity of speed has a dimension of \\(L^1T^{-1}\\). So, to be physically correct, the above code should be rewritten as:

    QuantityOf<dim_length / dim_time> auto avg_speed(QuantityOf<dim_length> auto distance,\n                                                 QuantityOf<dim_time> auto time)\n{\n  return distance / time;\n}\n

    Most of the libraries on the market ignore this fact and try to model distinct quantities through their dimensions, giving a false sense of safety. A dimension is not enough to describe a quantity. This has been known for a long time now. The \"Measurement Data (Archive Report)\" report from 1996 says explicitly:

    Measurement Data (Archive Report)

    Dimensional analysis does not adequately model the semantics of measurement data.

    In the following chapters, we will see a few use cases that can't be solved with an approach that only relies on units or dimensions.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#si-units-of-quantities-of-the-same-dimension-but-different-kinds","title":"SI units of quantities of the same dimension but different kinds","text":"

    The SI provides several units for distinct quantities of the same dimension but different kinds. For example:

    • hertz (Hz) is a unit of frequency and becquerel (Bq) is a unit of activity. Both are defined as \\(s^{-1}\\), and have the same dimension of \\(T^{-1}\\).
    • gray (Gy) is a unit of absorbed dose and sievert (Sv) is a unit of dose equivalent. Both are defined as \\(m^2 s^{-2}\\), and have the same dimension of \\(L^2T^{-2}\\)
    • radian (rad) is a unit of plane angle defined as \\(m/m\\), and steradian (sr) is a unit of solid angle defined as \\(m^2/m^2\\). Both are quantities of dimension one, which also has its own units like one (1) and percent (%).

    There are many more similar examples in the ISO/IEC 80000 series. For example, storage capacity quantity can be measured in units of one, bit, octet, and byte.

    The above conflicts can't be solved with dimensions, and they yield many safety issues. For example, we can ask ourselves what should be the result of the following:

    1. quantity q = 1 * Hz + 1 * Bq;
    2. quantity<Gy> q = 42 * Sv;
    3. bool b = (1 * rad + 1 * bit) == 2 * sr;

    None of the above code should compile, but most of the libraries on the market happily accept it and provide meaningless results. Some of them decide not to define one or more of the above units at all to avoid potential safety issues. For example, the Au library does not define Sv to avoid mixing it up with Gy.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#derived-quantities-of-the-same-dimension-but-different-kinds","title":"Derived quantities of the same dimension but different kinds","text":"

    Even if some quantities do not have a specially assigned unit, they may still have a totally different physical meaning even if they share the same dimension:

    • work vs. moment of force both of the same dimension \\(L^2MT^{-2}\\),
    • fuel consumption expressed in \\(\\frac{l}{100\\;km}\\) vs. area expressed in \\(m^2\\) both of the same dimension \\(L^2\\).

    Again, we don't want to accidentally mix those.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#various-quantities-of-the-same-dimension-and-kinds","title":"Various quantities of the same dimension and kinds","text":"

    Even if we somehow address all the above, there are plenty of use cases that still can't be safely implemented with such abstractions.

    Let's consider that we want to implement a freight transport application to position cargo in the container. In majority of the products on the market we will end up with something like:

    class Box {\n  length length_;\n  length width_;\n  length height_;\npublic:\n  Box(length l, length w, length h): length_(l), width_(w), height_(h) {}\n  area floor() const { return length_ * width_; }\n  // ...\n};\n
    Box my_box(2 * m, 3 * m, 1 * m);\n

    Such interfaces are not much safer than just using plain fundamental types (e.g., double). One of the main reasons of using a quantities and units library was to introduce strong-type interfaces to prevent such issues. In this scenario, we need to be able to discriminate between length, width, and height of the package.

    A similar but also really important use case is in aviation. The current altitude is a totally different quantity than the distance to the destination. The same is true for forward speed and sink rate. We do not want to accidentally mix those.

    When we deal with energy, we should be able to implicitly construct it from a proper product of any mass, length, and time. However, when we want to calculate gravitational potential energy, we may not want it to be implicitly initialized from any expression of matching dimensions. Such an implicit construction should be allowed only if we multiply a mass with acceleration of free fall and height. All other conversions should have an explicit annotation to make it clear that something potentially unsafe is being done in the code. Also, we should not be able to assign a potential energy to a quantity of kinetic energy. However, both of them (possibly accumulated with each other) should be convertible to a mechanical energy quantity.

    mass m = 1 * kg;\nlength l = 1 * m;\ntime t = 1 * s;\nacceleration_of_free_fall g = 9.81 * m / s2;\nheight h = 1 * m;\nspeed v = 1 * m / s;\nenergy e = m * pow<2>(l) / pow<2>(t);                     // OK\npotential_energy ep1 = e;                                 // should not compile\npotential_energy ep2 = static_cast<potential_energy>(e);  // OK\npotential_energy ep3 = m * g * h;                         // OK\nkinetic_energy ek1 = m * pow<2>(v) / 2;                   // OK\nkinetic_energy ek2 = ep3 + ek1;                           // should not compile\nmechanical_energy me = ep3 + ek1;                         // OK\n

    Yet another example comes from the audio industry. In the audio software, we want to treat specific counts (e.g., beats, samples) as separate quantities. We could assign dedicated base dimensions to them. However, if we divide them by duration, we should obtain a quantity convertible to frequency and even be able to express the result in a unit of Hz. With the dedicated dimensions approach, this wouldn't work as the dimension of frequency is just \\(T^{-1}\\), which would not match the results of our dimensional equations. This is why we can't assign dedicated dimensions to such counts.

    The last example that we want to mention here comes from finance. This time, we need to model currency volume as a special quantity of currency. currency volume can be obtained by multiplying currency by the dimensionless market quantity. Of course, both currency and currency volume should be expressed in the same units (e.g., USD).

    None of the above scenarios can be addressed with just units and dimensions. We need a better abstraction to safely implement them.

    "},{"location":"blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will introduce the main ideas behind the International System of Quantities and describe how we can model it in the programming language.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/","title":"International System of Quantities (ISQ): Part 3 - Modeling ISQ","text":"

    The physical units libraries on the market typically only focus on modeling one or more systems of units. However, as we have learned, this is not the only system kind to model. Another, and maybe even more important, is a system of quantities. The most important example here is the International System of Quantities (ISQ) defined by ISO/IEC 80000.

    This article continues our series about the International System of Quantities. This time, we will learn about the main ideas behind the ISQ and describe how it can be modelled in a programming language.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#dimension-is-not-enough-to-describe-a-quantity","title":"Dimension is not enough to describe a quantity","text":"

    Most of the products on the market are aware of physical dimensions. However, a dimension is not enough to describe a quantity. Let's repeat briefly some of the problems described in more detail in the previous article. For example, let's see the following implementation:

    class Box {\n  area base_;\n  length height_;\npublic:\n  Box(length l, length w, length h) : base_(l * w), height_(h) {}\n  // ...\n};\n\nBox my_box(2 * m, 3 * m, 1 * m);\n

    How do you like such an interface? It turns out that in most existing strongly-typed libraries this is often the best we can do.

    Another typical question many users ask is how to deal with work and torque. Both of those have the same dimension but are distinct quantities.

    A similar issue is related to figuring out what should be the result of:

    auto res = 1 * Hz + 1 * Bq + 1 * Bd;\n

    where:

    • Hz (hertz) - unit of frequency,
    • Bq (becquerel) - unit of activity,
    • Bd (baud) - unit of modulation rate.

    All of those quantities have the same dimension, namely \\(\\mathsf{T}^{-1}\\), but probably it is not wise to allow adding, subtracting, or comparing them, as they describe vastly different physical properties.

    If the above example seems too abstract, let's consider Gy (gray - unit of absorbed dose) and Sv (sievert - unit of dose equivalent), or radian and steradian. All of those quantities have the same dimensions.

    Another example here is fuel consumption (fuel volume divided by distance, e.g., 6.7 l/100km) and an area. Again, both have the same dimension \\(\\mathsf{L}^{2}\\), but probably it wouldn't be wise to allow adding, subtracting, or comparing a fuel consumption of a car and the area of a football field. Such an operation does not have any physical sense and should fail to compile.

    It turns out that the above issues can't be solved correctly without proper modeling of a system of quantities.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#quantities-of-the-same-kind","title":"Quantities of the same kind","text":"

    As it was described in the previous article, dimension is not enough to describe a quantity. We need a better abstraction to ensure the safety of our calculations. It turns out that ISO/IEC 80000 comes with the answer:

    ISO 80000-1:2009

    • Quantities may be grouped together into categories of quantities that are mutually comparable.
    • Mutually comparable quantities are called quantities of the same kind.
    • Two or more quantities cannot be added or subtracted unless they belong to the same category of mutually comparable quantities.
    • Quantities of the same kind within a given system of quantities have the same quantity dimension.
    • Quantities of the same dimension are not necessarily of the same kind.

    ISO Guide also explicitly states:

    ISO Guide

    Measurement units of quantities of the same quantity dimension may be designated by the same name and symbol even when the quantities are not of the same kind. For example, joule per kelvin and J/K are respectively the name and symbol of both a measurement unit of heat capacity and a measurement unit of entropy, which are generally not considered to be quantities of the same kind. However, in some cases special measurement unit names are restricted to be used with quantities of specific kind only. For example, the measurement unit \u2018second to the power minus one\u2019 (1/s) is called hertz (Hz) when used for frequencies and becquerel (Bq) when used for activities of radionuclides. As another example, the joule (J) is used as a unit of energy, but never as a unit of moment of force, i.e. the newton metre (N \u00b7 m).

    The above quotes from ISO provide answers to all the issues mentioned above and in the previous article.

    More than one quantity may be defined for the same dimension:

    • quantities of different kinds (e.g., frequency, modulation rate, activity).
    • quantities of the same kind (e.g., length, width, altitude, distance, radius, wavelength, position vector).

    Two quantities can't be added, subtracted, or compared unless they belong to the same kind. As frequency, activity, and modulation rate are of different kinds, the expression provided above should not compile.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#system-of-quantities-is-not-only-about-kinds","title":"System of quantities is not only about kinds","text":"

    ISO/IEC 80000 specifies hundreds of different quantities. Plenty of various kinds are provided, and often, each kind contains more than one quantity. It turns out that such quantities form a hierarchy of quantities of the same kind.

    For example, here are all quantities of the kind length provided in the ISO 80000-3:

    flowchart TD\n    length[\"<b>length</b><br>[m]\"]\n    length --- width[\"<b>width</b> / <b>breadth</b>\"]\n    length --- height[\"<b>height</b> / <b>depth</b> / <b>altitude</b>\"]\n    width --- thickness[\"<b>thickness</b>\"]\n    width --- diameter[\"<b>diameter</b>\"]\n    width --- radius[\"<b>radius</b>\"]\n    length --- path_length[\"<b>path_length</b>\"]\n    path_length --- distance[\"<b>distance</b>\"]\n    distance --- radial_distance[\"<b>radial_distance</b>\"]\n    length --- wavelength[\"<b>wavelength</b>\"]\n    length --- displacement[\"<b>displacement</b><br>{vector}\"]\n    displacement --- position_vector[\"<b>position_vector</b>\"]\n    radius --- radius_of_curvature[\"<b>radius_of_curvature</b>\"]

    Each of the above quantities expresses some kind of length, and each can be measured with meters, which is the unit defined by the SI for quantities of length. However, each has different properties, usage, and sometimes even a different character (position vector and displacement are vector quantities).

    Forming such a hierarchy helps us define arithmetics and conversion rules for various quantities of the same kind.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#converting-between-quantities-of-the-same-kind","title":"Converting between quantities of the same kind","text":"

    Based on the hierarchy above, we can define the following quantity conversion rules:

    1. Implicit conversions

      • Every width is a length.
      • Every radius is a width.
      static_assert(implicitly_convertible(isq::width, isq::length));\nstatic_assert(implicitly_convertible(isq::radius, isq::length));\nstatic_assert(implicitly_convertible(isq::radius, isq::width));\n

      Implicit conversions are allowed on copy-initialization:

      void foo(quantity<isq::length[m]> q);\n
      quantity<isq::width[m]> q1 = 42 * m;\nquantity<isq::length[m]> q2 = q1;  // implicit quantity conversion\nfoo(q1);                           // implicit quantity conversion\n
    2. Explicit conversions

      • Not every length is a width.
      • Not every width is a radius.
      static_assert(!implicitly_convertible(isq::length, isq::width));\nstatic_assert(!implicitly_convertible(isq::length, isq::radius));\nstatic_assert(!implicitly_convertible(isq::width, isq::radius));\nstatic_assert(explicitly_convertible(isq::length, isq::width));\nstatic_assert(explicitly_convertible(isq::length, isq::radius));\nstatic_assert(explicitly_convertible(isq::width, isq::radius));\n

      Explicit conversions are forced by passing the quantity to a call operator of a quantity_spec type:

      void foo(quantity<isq::height[m]> q);\n
      quantity<isq::length[m]> q1 = 42 * m;\nquantity<isq::height[m]> q2 = isq::height(q1);  // explicit quantity conversion\nfoo(isq::height(q1));                           // explicit quantity conversion\n
    3. Explicit casts

      • height is never a width, and vice versa.
      • Both height and width are quantities of kind length.
      static_assert(!implicitly_convertible(isq::height, isq::width));\nstatic_assert(!explicitly_convertible(isq::height, isq::width));\nstatic_assert(castable(isq::height, isq::width));\n

      Explicit casts are forced with a dedicated quantity_cast function:

      void foo(quantity<isq::height[m]> q);\n
      quantity<isq::width[m]> q1 = 42 * m;\nquantity<isq::height[m]> q2 = quantity_cast<isq::height>(q1);  // explicit quantity cast\nfoo(quantity_cast<isq::height>(q1));                           // explicit quantity cast\n
    4. No conversion

      • time has nothing in common with length.
      static_assert(!implicitly_convertible(isq::time, isq::length));\nstatic_assert(!explicitly_convertible(isq::time, isq::length));\nstatic_assert(!castable(isq::time, isq::length));\n

      Even the explicit casts will not force such a conversion:

      void foo(quantity<isq::length[m]>);\n
      quantity<isq::length[m]> q1 = 42 * s;    // Compile-time error\nfoo(quantity_cast<isq::length>(42 * s)); // Compile-time error\n
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#comparing-adding-and-subtracting-quantities-of-the-same-kind","title":"Comparing, adding, and subtracting quantities of the same kind","text":"

    ISO/IEC 80000 explicitly states that width and height are quantities of the same kind, and as such they:

    • are mutually comparable,
    • can be added and subtracted.

    This means that we should be allowed to compare any quantities from the same tree (as long as their underlying representation types are comparable):

    static_assert(isq::radius(1 * m) == isq::height(1 * m));\n

    Also, based on our hierarchy above, the only reasonable result of 1 * width + 1 * height is 2 * length, where the result of length is known as a common quantity type. A result of such an equation is always the first common node in a hierarchy tree of the same kind. For example:

    static_assert((isq::width(1 * m) + isq::height(1 * m)).quantity_spec == isq::length);\nstatic_assert((isq::thickness(1 * m) + isq::radius(1 * m)).quantity_spec == isq::width);\nstatic_assert((isq::distance(1 * m) + isq::path_length(1 * m)).quantity_spec == isq::path_length);\n
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#modeling-a-quantity-kind","title":"Modeling a quantity kind","text":"

    In the quantities and units library, we also need an abstraction describing an entire family of quantities of the same kind. Such quantities have not only the same dimension but also can be expressed in the same units.

    To annotate a quantity to represent its kind (and not just a hierarchy tree's root quantity) we introduced a kind_of<> specifier. For example, to express any quantity of length, we need to type kind_of<isq::length>.

    Important

    isq::length and kind_of<isq::length> are two different things.

    Such an entity behaves as any quantity of its kind. This means that it is implicitly convertible to any quantity in a tree.

    static_assert(!implicitly_convertible(isq::length, isq::height));\nstatic_assert(implicitly_convertible(kind_of<isq::length>, isq::height));\n

    Additionally, the result of operations on quantity kinds is also a quantity kind:

    static_assert(same_type<kind_of<isq::length> / kind_of<isq::time>, kind_of<isq::length / isq::time>>);\n

    However, if at least one equation's operand is not a quantity kind, the result becomes a \"strong\" quantity where all the kinds are converted to the hierarchy tree's root quantities:

    static_assert(!same_type<kind_of<isq::length> / isq::time, kind_of<isq::length / isq::time>>);\nstatic_assert(same_type<kind_of<isq::length> / isq::time, isq::length / isq::time>);\n

    Info

    Only a root quantity from the hierarchy tree or the one marked with is_kind specifier in the quantity_spec definition can be put as a template parameter to the kind_of specifier. For example, kind_of<isq::width> will fail to compile. However, we can call get_kind(q) to obtain a kind of any quantity:

    static_assert(get_kind(isq::width) == kind_of<isq::length>);\n
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#how-do-systems-of-units-benefit-from-the-isq-and-quantity-kinds","title":"How do systems of units benefit from the ISQ and quantity kinds?","text":"

    Modeling a system of units is the most essential feature and a selling point of every physical units library. Thanks to that, the library can protect users from assigning, adding, subtracting, or comparing incompatible units and provide automated conversion factors between various compatible units.

    Probably all the libraries in the wild model the SI (or at least most of it), and many of them provide support for additional units belonging to various other systems (e.g., imperial).

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#systems-of-units-are-based-on-systems-of-quantities","title":"Systems of units are based on systems of quantities","text":"

    Systems of quantities specify a set of quantities and equations relating to those quantities. Those equations do not take any unit or a numerical representation into account at all. In order to create a quantity, we need to add those missing pieces of information. This is where a system of units kicks in.

    The SI is explicitly stated to be based on the ISQ. Among others, it defines seven base units, one for each base quantity of ISQ. In the library, this is expressed by associating a quantity kind to a unit being defined:

    inline constexpr struct metre final : named_unit<\"m\", kind_of<isq::length>> {} metre;\n

    The kind_of<isq::length> above states explicitly that this unit has an associated quantity kind. In other words, si::metre (and scaled units based on it) can be used to express the amount of any quantity of kind length.

    Note

    For some systems of units (e.g., natural units), a unit may not have an associated quantity type. For example, if we define the speed of light constant as c = 1, we can define a system where both length and time will be measured in seconds, and speed will be a quantity measured with the unit one. In such case, the definition will look as follows:

    inline constexpr struct second final : named_unit<\"s\"> {} second;\n
    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#constraining-a-derived-unit-to-work-only-with-a-specific-derived-quantity","title":"Constraining a derived unit to work only with a specific derived quantity","text":"

    Some derived units are valid only for specific derived quantities. For example, SI specifies both hertz and becquerel derived units with the same unit equation \\(s^{-1}\\). However, it also explicitly states:

    SI

    The hertz shall only be used for periodic phenomena and the becquerel shall only be used for stochastic processes in activity referred to a radionuclide.

    This is why it is important for the library to allow constraining such units to be used only with a specific quantity kind:

    inline constexpr struct hertz final : named_unit<\"Hz\", one / second, kind_of<isq::frequency>> {} hertz;\ninline constexpr struct becquerel final : named_unit<\"Bq\", one / second, kind_of<isq::activity>> {} becquerel;\n

    With the above, hertz can only be used for frequencies, while becquerel should only be used for quantities of activity:

    quantity<isq::frequency[Hz]> q1 = 60 * Bq;   // Compile-time error\nquantity<isq::activity[Hz]> q2;              // Compile-time error\nquantity<isq::frequency[Hz]> q3 = 60 * Hz;   // OK\nstd::cout << q3.in(Bq) << \"\\n\";              // Compile-time error\n

    We know already that quantities of different kinds can't be compared, added, and subtracted. The following equation will not compile thanks to constraining derived units to be valid for specific kinds only:

    auto q = 1 * Hz + 1 * Bq;   // Fails to compile\n

    All of the above features improve the safety of our library and the products that are using it.

    "},{"location":"blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will present how we can implement our ISQ model in a C++ programming language and we will point out some of the first issues that stand in our way.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/","title":"International System of Quantities (ISQ): Part 4 - Implementing ISQ","text":"

    Up until now, we have introduced the International System of Quantities and described how we can model its main aspects. This article will present how to implement those models in a programming language, and we will point out some of the first issues that stand in our way.

    In the previous article, we have already introduced a notion of quantity kind, provided kind_of<> specifier, and described how it helps in the modeling of the system of units (e.g., SI).

    Now, it is time to see how we can implement hierarchies of quantities of the same kind.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#modeling-a-hierarchy-of-kind-length","title":"Modeling a hierarchy of kind length","text":"

    First, let's start with something easy - hierarchy of kind length. ISO 80000-3 does a good job of describing all relations between quantities in this case.

    We've seen this tree already:

    flowchart TD\n    length[\"<b>length</b><br>[m]\"]\n    length --- width[\"<b>width</b> / <b>breadth</b>\"]\n    length --- height[\"<b>height</b> / <b>depth</b> / <b>altitude</b>\"]\n    width --- thickness[\"<b>thickness</b>\"]\n    width --- diameter[\"<b>diameter</b>\"]\n    width --- radius[\"<b>radius</b>\"]\n    length --- path_length[\"<b>path_length</b>\"]\n    path_length --- distance[\"<b>distance</b>\"]\n    distance --- radial_distance[\"<b>radial_distance</b>\"]\n    length --- wavelength[\"<b>wavelength</b>\"]\n    length --- displacement[\"<b>displacement</b><br>{vector}\"]\n    displacement --- position_vector[\"<b>position_vector</b>\"]\n    radius --- radius_of_curvature[\"<b>radius_of_curvature</b>\"]

    This is how we can model it in C++:

    inline constexpr struct dim_length final          : base_dimension<\"L\"> {} dim_length;\n\ninline constexpr struct length final              : quantity_spec<dim_length> {} length;\ninline constexpr struct width final               : quantity_spec<length> {} width;\ninline constexpr auto breadth = width;\ninline constexpr struct height final              : quantity_spec<length> {} height;\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\ninline constexpr struct thickness final           : quantity_spec<width> {} thickness;\ninline constexpr struct diameter final            : quantity_spec<width> {} diameter;\ninline constexpr struct radius final              : quantity_spec<width> {} radius;\ninline constexpr struct radius_of_curvature final : quantity_spec<radius> {} radius_of_curvature;\ninline constexpr struct path_length final         : quantity_spec<length> {} path_length;\ninline constexpr auto arc_length = path_length;\ninline constexpr struct distance final            : quantity_spec<path_length> {} distance;\ninline constexpr struct radial_distance final     : quantity_spec<distance> {} radial_distance;\ninline constexpr struct wavelength final          : quantity_spec<length> {} wavelength;\ninline constexpr struct displacement final        : quantity_spec<length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final     : quantity_spec<displacement> {} position_vector;\n

    Thanks to the expressivity and power of C++ templates, we can specify all quantity properties in one line of code. In the above code:

    • length takes the base dimension to indicate that we are creating a base quantity that will serve as a root for a tree of quantities of the same kind,
    • width and following quantities are branches and leaves of this tree with the parent always provided as the first argument to quantity_spec class template,
    • breadth is an alias name for the same quantity as width.

    Note

    Some quantities may be specified to have complex, vector, or tensor character (e.g., displacement). The quantity character can be set with the last parameter of quantity_spec.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#modeling-a-hierarchy-of-kind-energy","title":"Modeling a hierarchy of kind energy","text":"

    Base quantities are simple. It is more complicated when we start modeling derived quantities. Let's try to model the hierarchy for energy.

    When we look into the ISO/IEC 80000 standards, this task immediately stops being as easy as the previous one. Derived quantity equations often do not automatically form a hierarchy tree, and ISO/IEC standards do not provide a clear answer to inter-quantity dependencies. This is why it is often not obvious what such a tree should look like.

    Even more, ISO explicitly states:

    ISO/IEC Guide 99

    The division of \u2018quantity\u2019 according to \u2018kind of quantity\u2019 is, to some extent, arbitrary.

    Let's try anyway. The below presents some arbitrary hierarchy of derived quantities of kind energy:

    flowchart TD\n    energy[\"<b>energy</b><br><i>(mass * length<sup>2</sup> / time<sup>2</sup>)</i><br>[J]\"]\n    energy --- mechanical_energy[\"<b>mechanical_energy</b>\"]\n    mechanical_energy --- potential_energy[\"<b>potential_energy</b>\"]\n    potential_energy --- gravitational_potential_energy[\"<b>gravitational_potential_energy</b><br><i>(mass * acceleration_of_free_fall * height)</i>\"]\n    potential_energy --- elastic_potential_energy[\"<b>elastic_potential_energy</b><br><i>(spring_constant * amount_of_compression<sup>2</sup>)</i>\"]\n    mechanical_energy --- kinetic_energy[\"<b>kinetic_energy</b><br><i>(mass * speed<sup>2</sup>)</i>\"]\n    energy --- enthalpy[\"<b>enthalpy</b>\"]\n    enthalpy --- internal_energy[\"<b>internal_energy</b> / <b>thermodynamic_energy</b>\"]\n    internal_energy --- Helmholtz_energy[\"<b>Helmholtz_energy</b> / <b>Helmholtz_function</b>\"]\n    enthalpy --- Gibbs_energy[\"<b>Gibbs_energy</b> / <b>Gibbs_function</b>\"]\n    energy --- active_energy[\"<b>active_energy</b>\"]

    As we can see above, besides what we've already seen for length hierarchy, derived quantities may provide specific recipes that can be used to create them implicitly:

    • energy is the most generic one and thus can be created from base quantities of mass, length, and time. As those are also the roots of quantities of their kinds and all other quantities from their trees are implicitly convertible to them, it means that an energy can be implicitly constructed from any quantity of mass, length, and time:

      static_assert(implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time), isq::energy));\nstatic_assert(implicitly_convertible(isq::mass * pow<2>(isq::height) / pow<2>(isq::time), isq::energy));\n
    • mechanical energy is a more \"specialized\" quantity than energy (not every energy is a mechanical energy). It is why an explicit cast is needed to convert from either energy or the results of its quantity equation:

      static_assert(!implicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                      isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                     isq::mechanical_energy));\n
    • gravitational potential energy is not only even more specialized one but additionally, it is special in a way that it provides its own \"constrained\" quantity equation. Maybe not every mass * pow<2>(length) / pow<2>(time) is a gravitational potential energy, but every mass * acceleration_of_free_fall * height is.

      static_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                      gravitational_potential_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                     gravitational_potential_energy));\nstatic_assert(implicitly_convertible(isq::mass * isq::acceleration_of_free_fall * isq::height,\n                                     gravitational_potential_energy));\n

    And here is the C++ code for it:

    inline constexpr struct energy final                         : quantity_spec<mass* pow<2>(length) / pow<2>(time)> {} energy;\ninline constexpr struct mechanical_energy final              : quantity_spec<energy> {} mechanical_energy;                                                            // differs from ISO 80000\ninline constexpr struct potential_energy final               : quantity_spec<mechanical_energy> {} potential_energy;                                                  // differs from ISO 80000\ninline constexpr struct gravitational_potential_energy final : quantity_spec<potential_energy, mass * acceleration_of_free_fall * height> {} potential_energy;        // not in ISO 80000\ninline constexpr struct elastic_potential_energy final       : quantity_spec<potential_energy, spring_constant * pow<2>(amount_of_compression)> {} potential_energy;  // not in ISO 80000\ninline constexpr struct kinetic_energy final                 : quantity_spec<mechanical_energy, mass* pow<2>(speed)> {} kinetic_energy;                               // differs from ISO 80000\ninline constexpr struct enthalpy final                       : quantity_spec<energy> {} enthalpy;                                                                     // differs from ISO 80000\ninline constexpr struct internal_energy final                : quantity_spec<enthalpy> {} internal_energy;                                                            // differs from ISO 80000\ninline constexpr auto thermodynamic_energy = internal_energy;\ninline constexpr struct Helmholtz_energy final               : quantity_spec<internal_energy> {} Helmholtz_energy;\ninline constexpr auto Helmholtz_function = Helmholtz_energy;\ninline constexpr struct Gibbs_energy final                   : quantity_spec<enthalpy> {} Gibbs_energy;\ninline constexpr auto Gibbs_function = Gibbs_energy;\n

    Again, the first parameter of quantity_spec determines the position in the tree. If a second argument is provided, it denotes a recipe for this quantity.

    With the above simple definitions we've automatically addressed our energy-related issues from the Various quantities of the same dimension and kinds chapter of the \"Part 2\" article.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#modeling-a-hierarchy-of-kind-dimensionless","title":"Modeling a hierarchy of kind dimensionless","text":"

    As the last example for this article, let's try to model and implement quantities of dimension one, often also called dimensionless quantities. This quantity hierarchy contains more than one quantity kind and more than one unit in its tree:

    flowchart TD\n    dimensionless[\"<b>dimensionless</b><br>[one]\"]\n    dimensionless --- rotation[\"<b>rotation</b>\"]\n    dimensionless --- thermodynamic_efficiency[\"<b>thermodynamic_efficiency</b><br><i>(work / heat)</i>\"]\n    dimensionless --- angular_measure[\"<b>angular_measure</b><br><i>(arc_length / radius)</i><br>[rad]\"]\n    angular_measure --- rotational_displacement[\"<b>rotational_displacement</b><br><i>(path_length / radius)</i>\"]\n    angular_measure --- phase_angle[\"<b>phase_angle</b>\"]\n    dimensionless --- solid_angular_measure[\"<b>solid_angular_measure</b><br><i>(area / pow<2>(radius))</i><br>[sr]\"]\n    dimensionless --- drag_factor[\"<b>drag_factor</b><br><i>(drag_force / (mass_density * pow<2>(speed) * area))</i>\"]\n    dimensionless --- storage_capacity[\"<b>storage_capacity</b><br>[bit]\"] --- equivalent_binary_storage_capacity[\"<b>equivalent_binary_storage_capacity</b>\"]\n    dimensionless --- ...

    To enable such support in the library, we provided an is_kind specifier that can be appended to the quantity specification:

    inline constexpr struct dimensionless final            : quantity_spec<detail::derived_quantity_spec<>{}> {} dimensionless;\ninline constexpr struct rotation final                 : quantity_spec<dimensionless> {} rotation;\ninline constexpr struct thermodynamic_efficiency final : quantity_spec<dimensionless, work / heat> {} efficiency;\ninline constexpr struct angular_measure final          : quantity_spec<dimensionless, arc_length / radius, is_kind> {} angular_measure;\ninline constexpr struct rotational_displacement final  : quantity_spec<angular_measure, path_length / radius> {} rotational_displacement;\ninline constexpr struct phase_angle final              : quantity_spec<angular_measure> {} phase_angle;\ninline constexpr struct solid_angular_measure final    : quantity_spec<dimensionless, area / pow<2>(radius), is_kind> {} solid_angular_measure;\ninline constexpr struct drag_factor final              : quantity_spec<dimensionless, drag_force / (mass_density * pow<2>(speed) * area)> {} drag_factor;\ninline constexpr struct storage_capacity final         : quantity_spec<dimensionless, is_kind> {} storage_capacity;\n

    With the above, we can constrain radian, steradian, and bit to be allowed for usage with specific quantity kinds only:

    inline constexpr struct radian final    : named_unit<\"rad\", metre / metre, kind_of<isq::angular_measure>> {} radian;\ninline constexpr struct steradian final : named_unit<\"sr\", square(metre) / square(metre), kind_of<isq::solid_angular_measure>> {} steradian;\ninline constexpr struct bit final       : named_unit<\"bit\", one, kind_of<storage_capacity>> {} bit;\n

    but still allow the usage of one and its scaled versions for such quantities.

    Note

    dimensionless is a special quantity which serves as an identity element in quantity equations. It is predefined in the library's framework and there is no way for the user to define it or something similar to it.

    "},{"location":"blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will present how our ISQ model helps to address the remaining issues described in the Part 2 of our series.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/","title":"International System of Quantities (ISQ): Part 5 - Benefits","text":"

    In the previous articles, we introduced the International System of Quantities, described how we can model and implement it in a programming language, and presented the issues of software that does not use such abstraction to implement a units library.

    Some of the issues raised in Part 2 of our series were addressed in Part 3 already. This article will present how our ISQ model elegantly addresses the remaining problems.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#generic-but-safe-interfaces","title":"Generic but safe interfaces","text":"

    Let's start with the implementation of a generic utility function that would calculate the average speed based on provided arguments. The resulting quantity should use a derived unit of the provided arguments (e.g., km/h for km and h, m/s for m and s, ...).

    With C++ concepts backed up with ISQ quantities, we can simply type it as:

    constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d,\n                                                QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n

    The above constrains the algorithm to proper quantity types and ensures that a quantity of speed is returned. The latter is essential not only for the users to better understand what the function does but also serves as a unit test for our implementation. It ensures that our quantity equations are correct in the implementation part of the function, and we indeed return a quantity of speed.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#non-convertible-units-of-currency","title":"Non-convertible units of currency","text":"

    Our second example was about disjoint units of currency. We want to use various units of currency but we can't provide compile-time known conversion factors between those as such ratios are only known at runtime.

    First, we define:

    • a new dimension for currency and a quantity type based on it,
    • set of disjoint units of currency for its quantity kind.
    inline constexpr struct dim_currency final : base_dimension<\"$\"> {} dim_currency;\ninline constexpr struct currency final : quantity_spec<dim_currency> {} currency;\n\ninline constexpr struct euro final : named_unit<\"EUR\", kind_of<currency>> {} euro;\ninline constexpr struct us_dollar final : named_unit<\"USD\", kind_of<currency>> {} us_dollar;\n\nnamespace unit_symbols {\n\ninline constexpr auto EUR = euro;\ninline constexpr auto USD = us_dollar;\n\n}\n\nstatic_assert(!std::equality_comparable_with<quantity<euro, int>, quantity<us_dollar, int>>);\n

    Next, we can provide a custom currency exchange facility that accounts for a specific point in time:

    template<Unit auto From, Unit auto To>\n[[nodiscard]] double exchange_rate(std::chrono::sys_seconds timestamp)\n{\n  // user-provided logic...\n}\n\ntemplate<UnitOf<currency> auto To, QuantityOf<currency> From>\nQuantityOf<currency> auto exchange_to(From q, std::chrono::sys_seconds timestamp)\n{\n  const auto rate =\n    static_cast<From::rep>(exchange_rate<From::unit, To>(timestamp) * q.numerical_value_in(q.unit));\n  return rate * From::quantity_spec[To];\n}\n

    Finally, we can use our simple model in the following way:

    using namespace unit_symbols;\nusing namespace std::chrono;\n\nconst auto yesterday = time_point_cast<seconds>(system_clock::now() - hours{24});\nconst quantity price_usd = 100 * USD;\nconst quantity price_euro = exchange_to<euro>(price_usd, yesterday);\n\nstd::cout << price_usd << \" -> \" << price_euro << \"\\n\";\n// std::cout << price_usd + price_euro << \"\\n\";  // does not compile\n

    Note

    It would be better to model the above prices as quantity points, but this is a subject for a different article .

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#derived-quantities-of-the-same-dimension-but-different-kinds","title":"Derived quantities of the same dimension but different kinds","text":"

    Until now, the issues discussed have not actually required modeling of the ISQ. The introduction of physical dimensions would be enough, and indeed, this is what most of the libraries on the market do. However, we have more exciting challenges to solve as well.

    The next issue was related to different quantities having the same dimension. In many cases, we want to prevent conversions and any other compatibility between such distinct quantities.

    Let's try to implement our fuel consumption example. First, we define the quantity type and a handy identifier for a derived unit that we want to use:

    inline constexpr struct fuel_consumption final : quantity_spec<isq::volume / isq::length> {} fuel_consumption;\ninline constexpr auto L_per_100km = si::litre / (mag<100> * si::kilo<si::metre>);\n\nstatic_assert(fuel_consumption != isq::area);\nstatic_assert(fuel_consumption.dimension == isq::area.dimension);\n

    Next, we define two quantities. The first one is based only on a derived unit of L/[100 km], while the second uses a strongly typed quantity type:

    quantity q1 = 5.8 * L_per_100km;\nquantity q2 = fuel_consumption(6.7 * L_per_100km);\nstd::println(\"Fuel consumptions: {}, {}\", q1, q2);\n\nstatic_assert(implicitly_convertible(q1.quantity_spec, isq::area));\nstatic_assert(!implicitly_convertible(q2.quantity_spec, isq::area));\nstatic_assert(!explicitly_convertible(q2.quantity_spec, isq::area));\nstatic_assert(!castable(q2.quantity_spec, isq::area));\n

    As we can see, with just units (especially derived ones) and dimensions, we often can't achieve the same level of safety as with adequately modeled hierarchies of quantities. Only in case of q2 we can prevent incorrect conversions to a different quantity of the same dimension.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#various-quantities-of-the-same-dimension-and-kinds","title":"Various quantities of the same dimension and kinds","text":"

    In the previous example, area and fuel consumption were quantities of the same dimension but of different kinds. In engineering, there are also many cases where we need to model distinct quantities of the same kind.

    Let's try to improve the safety of our Box example.

    First, we need to extend our ISQ definitions by the horizontal length quantity and a horizontal area derived from it:

    inline constexpr struct horizontal_length final : quantity_spec<isq::length> {} horizontal_length;\ninline constexpr struct horizontal_area final : quantity_spec<isq::area, horizontal_length * isq::width> {} horizontal_area;\n

    Note

    isq::length denotes any quantity of length (not only the horizontal one).

    static_assert(implicitly_convertible(horizontal_length, isq::length));\nstatic_assert(!implicitly_convertible(isq::length, horizontal_length));\n\nstatic_assert(implicitly_convertible(horizontal_area, isq::area));\nstatic_assert(!implicitly_convertible(isq::area, horizontal_area));\n\nstatic_assert(implicitly_convertible(isq::length * isq::length, isq::area));\nstatic_assert(!implicitly_convertible(isq::length * isq::length, horizontal_area));\n\nstatic_assert(implicitly_convertible(horizontal_length * isq::width, isq::area));\nstatic_assert(implicitly_convertible(horizontal_length * isq::width, horizontal_area));\n

    With simple two lines of definition, we made the above logic automatically work without needing additional customization for special cases. Based on hierarchies of derived quantities and their recipes, the proposed model automatically inherits the properties of base quantities involved in the recipe. This makes the composition of derived quantities very easy, which is not true for alternative solutions based on tag types that do not compose their properties.

    Now we can refactor our Box to benefit from the introduced safe abstractions:

    class Box {\n  quantity<horizontal_length[m]> length_;\n  quantity<isq::width[m]> width_;\n  quantity<isq::height[m]> height_;\npublic:\n  Box(quantity<horizontal_length[m]> l, quantity<isq::width[m]> w, quantity<isq::height[m]> h):\n    length_(l), width_(w), height_(h)\n  {}\n\n  quantity<horizontal_area[m2]> floor() const { return length_ * width_; }\n  // ...\n};\n

    It is important to note that the safety can be enforced only when a user provides typed quantities as arguments to the functions:

    Box my_box1(2 * m, 3 * m, 1 * m);\nBox my_box2(2 * horizontal_length[m], 3 * isq::width[m], 1 * isq::height[m]);\nBox my_box3(horizontal_length(2 * m), isq::width(3 * m), isq::height(1 * m));\n

    Important

    It is up to the user to decide when and where to care about explicit quantity types and when to prefer simple unit-only mode.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#various-kinds-of-dimensionless-quantities","title":"Various kinds of dimensionless quantities","text":"

    Most of the quantities hierarchies describe only one kind. There are some exceptions, though. One of them is a hierarchy of dimensionless quantities. This tree defines quantities that denote:

    • counts (e.g., storage capacity),
    • ratios (e.g., efficiency),
    • angles (e.g., angular measure, solid angular measure),
    • scaled numbers.

    Each of the above could form a separate tree of mutually comparable quantities. However, all of them have a common property. Every quantity from this tree, despite often being measured in a dedicated unit (e.g., bit, rad, sr), should also be able to be measured in a unit one.

    We've seen how to model such a hierarchy in a previous article in our series. This time, we will see a simplified part of a concrete, real-life example for this use case.

    We often need to provide strong types for different counts in the digital signal processing domain. Abstractions like samples, beats, MIDI clock, and others should not be possible to be intermixed with each other:

    namespace ni {\n\ninline constexpr struct SampleCount final : quantity_spec<dimensionless, is_kind> {} SampleCount;\ninline constexpr struct UnitSampleAmount final : quantity_spec<dimensionless, is_kind> {} UnitSampleAmount;\ninline constexpr struct MIDIClock final : quantity_spec<dimensionless, is_kind> {} MIDIClock;\ninline constexpr struct BeatCount final : quantity_spec<dimensionless, is_kind> {} BeatCount;\n

    We should also be able to create derived quantities from those. For example, when we divide such a quantity by time we should get a new strong quantity that can be measured in both a dedicated unit (e.g., Smpl/s for sample rate) and hertz:

    inline constexpr struct SampleDuration final : quantity_spec<isq::period_duration> {} SampleDuration;\ninline constexpr struct SamplingRate final : quantity_spec<isq::frequency, SampleCount / SampleDuration> {} SamplingRate;\n\ninline constexpr auto Amplitude = UnitSampleAmount;\ninline constexpr auto Level = UnitSampleAmount;\ninline constexpr struct Power final : quantity_spec<Level * Level> {} Power;\n\ninline constexpr struct BeatDuration final : quantity_spec<isq::period_duration> {} BeatDuration;\ninline constexpr struct Tempo final : quantity_spec<isq::frequency, BeatCount / BeatDuration> {} Tempo;\n

    We can also define a collection of units associated with specific quantity kinds and their symbols:

    inline constexpr struct Sample final : named_unit<\"Smpl\", one, kind_of<SampleCount>> {} Sample;\ninline constexpr struct SampleValue final : named_unit<\"PCM\", one, kind_of<UnitSampleAmount>> {} SampleValue;\ninline constexpr struct MIDIPulse final : named_unit<\"p\", one, kind_of<MIDIClock>> {} MIDIPulse;\n\ninline constexpr struct QuarterNote final : named_unit<\"q\", one, kind_of<BeatCount>> {} QuarterNote;\ninline constexpr struct HalfNote final : named_unit<\"h\", mag<2> * QuarterNote> {} HalfNote;\ninline constexpr struct DottedHalfNote final : named_unit<\"h.\", mag<3> * QuarterNote> {} DottedHalfNote;\ninline constexpr struct WholeNote final : named_unit<\"w\", mag<4> * QuarterNote> {} WholeNote;\ninline constexpr struct EightNote final : named_unit<\"8th\", mag_ratio<1, 2> * QuarterNote> {} EightNote;\ninline constexpr struct DottedQuarterNote final : named_unit<\"q.\", mag<3> * EightNote> {} DottedQuarterNote;\ninline constexpr struct QuarterNoteTriplet final : named_unit<\"qt\", mag_ratio<1, 3> * HalfNote> {} QuarterNoteTriplet;\ninline constexpr struct SixteenthNote final : named_unit<\"16th\", mag_ratio<1, 2> * EightNote> {} SixteenthNote;\ninline constexpr struct DottedEightNote final : named_unit<\"q.\", mag<3> * SixteenthNote> {} DottedEightNote;\n\ninline constexpr auto Beat = QuarterNote;\n\ninline constexpr struct BeatsPerMinute final : named_unit<\"bpm\", Beat / si::minute> {} BeatsPerMinute;\ninline constexpr struct MIDIPulsePerQuarter final : named_unit<\"ppqn\", MIDIPulse / QuarterNote> {} MIDIPulsePerQuarter;\n\nnamespace unit_symbols {\n\ninline constexpr auto Smpl = Sample;\ninline constexpr auto pcm = SampleValue;\ninline constexpr auto p = MIDIPulse;\n\ninline constexpr auto n_wd = 3 * HalfNote;\ninline constexpr auto n_w = WholeNote;\ninline constexpr auto n_hd = DottedHalfNote;\ninline constexpr auto n_h = HalfNote;\ninline constexpr auto n_qd = DottedQuarterNote;\ninline constexpr auto n_q = QuarterNote;\ninline constexpr auto n_qt = QuarterNoteTriplet;\ninline constexpr auto n_8thd = DottedEightNote;\ninline constexpr auto n_8th = EightNote;\ninline constexpr auto n_16th = SixteenthNote;\n\n}\n\n}  // namespace ni\n

    With the above, we can safely work with each quantity and use SI or domain-specific units as needed:

    using namespace ni::unit_symbols;\nusing namespace mp_units::si::unit_symbols;\n\nconst auto sr1 = ni::GetSampleRate();\nconst auto sr2 = 48'000.f * Smpl / s;\n\nconst auto samples = 512 * Smpl;\n\nconst auto sampleTime1 = (samples / sr1).in(s);\nconst auto sampleTime2 = (samples / sr2).in(ms);\n\nconst auto sampleDuration1 = (1 / sr1).in(ms);\nconst auto sampleDuration2 = (1 / sr2).in(ms);\n\nconst auto rampTime = 35.f * ms;\nconst auto rampSamples1 = (rampTime * sr1).force_in<int>(Smpl);\nconst auto rampSamples2 = (rampTime * sr2).force_in<int>(Smpl);\n\nstd::println(\"Sample rate 1 is: {}\", sr1);\nstd::println(\"Sample rate 2 is: {}\", sr2);\n\nstd::println(\"{} @ {} is {::N[.5f]}\", samples, sr1, sampleTime1);\nstd::println(\"{} @ {} is {::N[.5f]}\", samples, sr2, sampleTime2);\n\nstd::println(\"One sample @ {} is {::N[.5f]}\", sr1, sampleDuration1);\nstd::println(\"One sample @ {} is {::N[.5f]}\", sr2, sampleDuration2);\n\nstd::println(\"{} is {} @ {}\", rampTime, rampSamples1, sr1);\nstd::println(\"{} is {} @ {}\", rampTime, rampSamples2, sr2);\n

    The above prints:

    Sample rate 1 is: 44100 Hz\nSample rate 2 is: 48000 Smpl/s\n512 Smpl @ 44100 Hz is 0.01161 s\n512 Smpl @ 48000 Smpl/s is 10.66667 ms\nOne sample @ 44100 Hz is 0.02268 ms\nOne sample @ 48000 Smpl/s is 0.02083 ms\n35 ms is 1543 Smpl @ 44100 Hz\n35 ms is 1680 Smpl @ 48000 Smpl/s\n

    We can also do a bit more advanced computations to get the following:

    auto sampleValue = -0.4f * pcm;\nauto power1 = sampleValue * sampleValue;\nauto power2 = -0.2 * pow<2>(pcm);\n\nauto tempo = ni::GetTempo();\nauto reverbBeats = 1 * n_qd;\nauto reverbTime = reverbBeats / tempo;\n\nauto pulsePerQuarter = value_cast<float>(ni::GetPPQN());\nauto transportPosition = ni::GetTransportPos();\nauto transportBeats = (transportPosition / pulsePerQuarter).in(n_q);\nauto transportTime = (transportBeats / tempo).in(s);\n\nstd::println(\"SampleValue is: {}\", sampleValue);\nstd::println(\"Power 1 is: {}\", power1);\nstd::println(\"Power 2 is: {}\", power2);\n\nstd::println(\"Tempo is: {}\", tempo);\nstd::println(\"Reverb Beats is: {}\", reverbBeats);\nstd::println(\"Reverb Time is: {}\", reverbTime.in(s));\nstd::println(\"Pulse Per Quarter is: {}\", pulsePerQuarter);\nstd::println(\"Transport Position is: {}\", transportPosition);\nstd::println(\"Transport Beats is: {}\", transportBeats);\nstd::println(\"Transport Time is: {}\", transportTime);\n

    which prints:

    SampleValue is: -0.4 PCM\nPower 1 is: 0.16000001 PCM\u00b2\nPower 2 is: -0.2 PCM\u00b2\nTempo is: 110 bpm\nReverb Beats is: 1 q.\nReverb Time is: 0.8181818 s\nPulse Per Quarter is: 960 ppqn\nTransport Position is: 15836 p\nTransport Beats is: 16.495832 q\nTransport Time is: 8.997726 s\n

    Info

    More about this example can be found in \"Exploration of Strongly-typed Units in C++: A Case Study from Digital Audio\" CppCon 2023 talk by Roth Michaels.

    "},{"location":"blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/#to-be-continued","title":"To be continued...","text":"

    In the next part of this series, we will discuss the challenges and issues related to the modeling of the ISQ with a programming language.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/","title":"International System of Quantities (ISQ): Part 6 - Challenges","text":"

    This article might be the last one from our series. This time, we will discuss the challenges and issues with modeling of the ISQ in software.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#articles-from-this-series","title":"Articles from this series","text":"
    • Part 1 - Introduction
    • Part 2 - Problems when ISQ is not used
    • Part 3 - Modeling ISQ
    • Part 4 - Implementing ISQ
    • Part 5 - Benefits
    • Part 6 - Challenges
    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#ambiguity","title":"Ambiguity","text":"

    Some quantity names are ambiguous. It is not a problem of ISQ but of the English language and the way we communicate things. When I say: \"Every width is a length, but not every length is a width\" most people understand this right away. However, the same people trying to model our 3D box problem try to do it as follows:

    class Box {\n  quantity<isq::length[m]> length_;\n  quantity<isq::width[m]> width_;\n  quantity<isq::height[m]> height_;\npublic:\n  // ...\n};\n

    This looks correct at first sight. Only when we think about the sentence mentioned above will we realize that this implementation has a problem. We intended to specify three orthogonal dimensions of the box, each of which will be a strong quantity that is not convertible to others. But we've failed.

    When we look at the tree of quantities of length we immediately see that both width and height are special lengths so they are convertible to it.

    To implement our task correctly, we had to define and use a new quantity of kind length:

    inline constexpr struct horizontal_length final : quantity_spec<isq::length> {} horizontal_length;\n

    We do not propose adding horizontal length to ISO 80000-3. There are probably other similar cases as well, but so far, this was the most common and obvious one we've encountered.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#no-common-quantities","title":"No common quantities","text":"

    ISO 80000-1:2009 explicitly states:

    Quote

    Two or more quantities cannot be added or subtracted unless they belong to the same category of mutually comparable quantities.

    This means that we should be able to add and subtract any quantities as long as they belong to the same kind. However, ISO/IEC documents do not provide any rules or even hints about what should be the result of such operations.

    If it is possible to add radius and distance, then what quantity should be provided in return? Undoubtedly, the resulting quantity type can't be the same as any of the arguments. It is not a radius or distance. It is some closer unspecified length, though.

    Info

    Finding the correct solution took us many months of experimentation and implementation. Based on the hierarchy tree of quantities, we can define conversion rules and what a common quantity should be.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#lack-of-consistency","title":"Lack of consistency","text":"

    The documents of ISO/IEC 80000 are not 100% consistent, and programming languages do not like inconsistencies.

    For example:

    • time is mentioned as a base quantity of ISQ in ISO 80000-1 chapter 4.5.
    • ISO 80000-3 \"Space and time\", does not define a quantity of time. It provides a duration quantity (item 3-9) with symbol t, and states in the Remarks section:

      Quote

      Duration is often just called time.

    • Other parts (e.g., IEC 80000-6 \"Electromagnetism\") often say:

      Quote

      ... t is time (ISO 80000-3)

    To be consistent, ISO/IEC should either:

    • change ISO 80000-1 chapter 4.5 and all references in other parts to use duration (unlikely),
    • or add time as an alias name to duration in the definition 3-9 of ISO 80000-3.
    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#lack-of-definitions","title":"Lack of definitions","text":"

    ISQ defines derived quantities in terms of other quantities provided in the series. However, some definitions mention quantities that are not defined in the ISQ at all.

    For example, weight is defined as \\(F_\\textsf{g} = m\\;g\\), where \\(m\\) is the mass of the body (item 4-1 of ISO 80000-4 \"Mechanics\"), and \\(g\\) is the local acceleration of free fall (ISO 80000-3).

    The problem here is that ISO 80000-3 never defines a quantity with a symbol \\(g\\) or named as a local acceleration of free fall. The closest one we have is acceleration (item 3-11) with a symbol \\(a\\).

    Info

    To have a proper definition of weight in mp-units that is not defined in terms of just any kind of acceleration, we have added isq::acceleration_of_free_fall in our definitions as an extension to the original ISQ set of quantities.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#not-engineering-friendly","title":"Not engineering-friendly","text":"

    Many quantities have proper physical definitions, but they are sometimes not engineering-friendly.

    For example, velocity is defined as a rate of change of position vector \\(v = \\frac{\\textsf{d}r}{\\textsf{d}t}\\), where \\(r\\) denotes the position vector (item 3\u20111.10) and \\(t\\) the duration (item 3\u20119).

    Next, a speed quantity is defined as the magnitude of velocity. Despite being physically correct, requiring every speed to be derived from the vector quantity of velocity in software would be inconvenient. If this was the only case, people would always need to use vector representations of position vectors to talk about speeds, which differs from what we do in practice. In practice, we divide any kind of length by time to get some kind of speed.

    ISO 80000-3 provides length, height, distance and other quantities of kind length that when divided by duration can serve really well to calculate speed.

    Info

    This is why in mp-units, we decided to divert from the official definition of speed and define it as:

    inline constexpr struct speed : quantity_spec<speed, length / time> {} speed;\n

    This allows us to create a quantity of kind speed from any quantity of length divided by time.

    Additionally, it is essential to note that for the needs of our library, defining velocity as position_vector / duration would be wrong. We miss the delta part here. Even though it is not mentioned in ISO 80000-3, the delta of position vectors is actually a displacement. This is why our velocity is defined as:

    inline constexpr struct velocity : quantity_spec<speed, displacement / duration> {} velocity;\n

    Please also note that velocity is defined as a more specialized quantity of speed.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#affine-space-agnostic","title":"Affine space agnostic","text":"

    The affine space is a powerful abstraction, allowing us to model some problems safer or more accurately. It has two types of entities:

    • point - a position specified with coordinate values (e.g., location, address, etc.),
    • displacement vector - the difference between two points (e.g., shift, offset, displacement, duration, etc.).

    Vectors support all the arithmetics operations, but points have some limitations. It is not possible to:

    • add two points,
    • subtract a point from a vector,
    • multiply nor divide points with anything else.

    ISO/IEC series does not acknowledge this abstraction even though it would be really useful in some cases. Let's discuss the following two examples.

    What does it mean to add two altitudes? It is not meaningful. On the other hand, subtracting those should not result in an altitude, but in a quantity of height. Adding or subtracting height to/from altitude results in altitude. Subtracting altitude from height is meaningless again. Those quantities clearly model affine space. Maybe this is why ISQ defines them as one quantity type height/depth/altitude?

    What does it mean to add two position vectors? It is not meaningful again. However, subtracting those results in a displacement as we noted in the previous chapter. Adding or subtracting displacement to/from position vector results in another position vector, and subtracting position vector from displacement does not have physical sense. Again, those quantities perfectly model affine space. However, this time, those are defined as separate and independent quantities (i.e., displacement is not modeled as delta position vector or position vector is not modeled as a displacement from the origin of a coordinate system).

    Info

    Currently, mp-units does not enforce the affine space behavior for such quantities. Today, subtracting two altitudes result in an altitude and subtracting two position vectors result in a position vector. However, we plan to support automatic conversion to a proper quantity type on subtraction and addition shortly.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#non-negative-quantities","title":"Non-negative quantities","text":"

    Some quantities in the ISQ are defined as non-negative. This is a really interesting property that may be checked at runtime to increase safety. However, the number of such quantities is minimal. From a few hundred quantities provided by the ISO/IEC series, only the following have this property mentioned explicitly:

    • width/breadth,
    • thickness,
    • diameter,
    • radius.

    If height was defined separately from altitude, it could probably also join this group.

    Let's think a bit more about this. What does it mean that a quantity is non-negative? Indeed, it is hard to imagine something of a negative width or radius. However, if we subtract two widths, the second one may be larger. This will result in a negative quantity of width, violating our precondition. So, is it non-negative or not?

    Again, we have to talk about the affine space abstractions. Every empirical measurement can be expressed as a point. Such points for some quantities may be non-negative indeed.

    Non-negative quantities do not end on the ones provided above. For example, speed is a good example here as well. In general, all magnitudes of vector quantities will also have this property.

    When subtracting two points, we end up with a delta/displacement type, which may be negative even for quantities listed as non-negative in the ISQ. As stated in the previous chapter, having affine space abstractions acknowledged in ISQ would greatly help here.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#lack-of-quantity-recipes","title":"Lack of quantity recipes","text":"

    Definition of many derived quantities provides their recipes in the form of quantity equations (e.g., weight equation in the previous chapter). However, some of them do not. Instead, they often provide a very generic description.

    For example, force is defined as:

    Quote

    vector (ISO 80000-2) quantity describing interaction between bodies or particles.

    This is not helpful for programming languages that like explicit definitions. Different vendors may interpret the above differently, which will result in different implementations that will not be compatible with each other.

    As the derived quantity of force has to be a vector quantity, it has to be defined in terms of at least one other vector quantity. We have a few to choose from:

    • displacement (\\(\\Delta{r}\\)),
    • velocity (\\(v\\)),
    • acceleration (\\(a\\)).

    It is not stated explicitly in ISQ which one of those should be used and how.

    Info

    In mp-units we decided to define force as \\(F = m\\;a\\).

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#lack-of-generic-quantities-and-name-conflicts","title":"Lack of generic quantities and name conflicts","text":"

    In the previous chapter, we complained about some definitions needing to be more complex or generic. On the other hand, we also lack some generic quantities in ISQ that could serve as a root for a quantity hierarchy tree.

    For example:

    • ISO 80000-4 \"Mechanics\" defines power <mechanics> as \\(P = F\\;v\\) (scalar product of force \\(F\\) (item 4-9.1) acting to a body and its velocity \\(v\\) (ISO 80000-3)),
    • ISO 80000-6 \"Electromagnetism\" defines power as \\(p = u\\;i\\) (scalar quantity given by the product of instantaneous voltage \\(u\\) (item 6-11.3) and instantaneous electric current \\(i\\) (item 6-1)).

    First, the above definitions have somehow conflicting names which makes it hard for the programming languages to name them consistently by different vendors.

    Info

    In mp-units, we chose mechanical_power and electromagnetism_power for those.

    Second, we do not have any other more generic definition of power to put above those in the tree. Not having it makes it hard to answer what should be the result of:

    quantity q = isq::mechanical_power(42 * W) + isq::electromagnetism_power(60 * W);\n

    Info

    To solve the above problem, we have added isq::power in mp-units, that has a really generic definition of:

    inline constexpr struct power : quantity_spec<mass* pow<2>(length) / pow<3>(time)> {} power;\n
    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#invalid-definitions-order","title":"Invalid definitions order","text":"

    Energy is defined a bit better than power, but still not without issues.

    The first time ISQ mentions energy is in the ISO 80000-4 \"Mechanics\". It defines potential energy, kinetic energy, and a mechanical energy as the sum of the first two. Right after that a mechanical work/work is defined.

    Then ISO 80000-5 \"Thermodynamics\" defines energy <thermodynamics> as:

    Quote

    ability of a system to do work (ISO 80000-4).

    Next, internal energy/thermodynamic energy is defined in terms of the change of heat.

    From the above, it seems that what is called energy <thermodynamics> should actually be the root of our tree and probably be provided in Part 4 before the mechanical energy is defined.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#hierarchies-of-derived-quantities","title":"Hierarchies of derived quantities","text":"

    Derived quantities of the same kind are often independently defined in the ISQ. The ISO/IEC 80000 series often does not suggest any hierarchy between those. Even more, it states:

    ISO/IEC Guide 99

    The division of \u2018quantity\u2019 according to \u2018kind of quantity\u2019 is, to some extent, arbitrary.

    Because of this, it is unknown or ambiguous how to form a hierarchy tree for such quantities.

    To get some sense of the complexity here, let's look again at our tree of quantities of a kind energy:

    flowchart TD\n    energy[\"<b>energy</b><br><i>(mass * length<sup>2</sup> / time<sup>2</sup>)</i><br>[J]\"]\n    energy --- mechanical_energy[\"<b>mechanical_energy</b>\"]\n    mechanical_energy --- potential_energy[\"<b>potential_energy</b>\"]\n    mechanical_energy --- kinetic_energy[\"<b>kinetic_energy</b>\"]\n    energy --- enthalpy[\"<b>enthalpy</b>\"]\n    enthalpy --- internal_energy[\"<b>internal_energy</b> / <b>thermodynamic_energy</b>\"]\n    internal_energy --- Helmholtz_energy[\"<b>Helmholtz_energy</b> / <b>Helmholtz_function</b>\"]\n    enthalpy --- Gibbs_energy[\"<b>Gibbs_energy</b> / <b>Gibbs_function</b>\"]\n    energy --- active_energy[\"<b>active_energy</b>\"]

    Not being exact means that every vendor may implement it differently. This will result in:

    • different convertibility rules among quantities:

      static_assert(implicitly_convertible(isq::potential_energy, isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::mechanical_energy, isq::potential_energy));\n
    • different common quantities resulting from the arithmetics on various quantities of the same kind:

      static_assert((isq::potential_energy(1 * J) + isq::kinetic_energy(1 * J)).quantity_spec == isq::mechanical_energy);\n

    It would be great if ISQ could provide specific division of quantities into kinds and more information about the position of each quantity within the hierarchy of quantities of the same kind.

    Important

    We can try to do this by ourselves, but it is tough. Probably no one, for sure we are not, is an expert in all the fields of ISO/IEC 80000 applicability.

    We need the help of subject matter experts who will help us build those trees for their domains and then verify that everything works as expected.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#the-same-or-a-different-kind","title":"The same or a different kind?","text":"

    Some quantities are more complicated than others. For example, power has:

    • scalar quantities expressed in:
      • W (watts) (e.g., mechanical power, active power),
      • VA (volt-ampere) (e.g., apparent power),
      • var (e.g., reactive power),
    • complex quantities expressed in VA (volt-ampere) (e.g., complex power).

    How should we model this? Maybe those should be two or three independent trees of quantities, each having its own unit?

    flowchart TD\n    power[\"<b>power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[W]\"]\n    power --- mechanical_power[\"<b>mechanical_power</b><br><i>(scalar_product(force, velocity))</i>\"]\n    power --- electromagnetism_power[\"<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>\"]\n    power --- active_power[\"<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>\"]\n\n    nonactive_power[\"<b>nonactive_power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[VA]\"]\n    nonactive_power --- reactive_power[\"<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]\"]\n\n    complex_power[\"<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i><br>[VA]\"]\n    complex_power --- apparent_power[\"<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i>\"]

    This will mean that we will not be able to add or compare active power, reactive power, and apparent power, which probably makes a lot of sense. However, it also means that the following will fail to compile:

    quantity apparent = isq::apparent_power(100 * VA);\nquantity active = isq::active_power(60 * W);\nquantity<isq::nonactive_power[VA]> q = sqrt(pow<2>(apparent) - pow<2>(active)); // Compile-time error\n

    Also the following will not work:

    quantity active = isq::active_power(60 * W);\nquantity reactive = isq::reactive_power(40 * var);\nquantity<isq::apparent_power[VA]> q = sqrt(pow<2>(active) + pow<2>(reactive)); // Compile-time error\n

    If we want the above to work maybe we need to implement the tree as follows?

    flowchart TD\n    power[\"<b>power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[W]\"]\n    power --- mechanical_power[\"<b>mechanical_power</b><br><i>(scalar_product(force, velocity))</i>\"]\n    power --- electromagnetism_power[\"<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>\"]\n    power --- apparent_power[\"<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i><br>[VA]\"]\n    apparent_power --- active_power[\"<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>\"]\n    apparent_power --- nonactive_power[\"<b>nonactive_power</b><br><i>(sqrt(apparent_power<sup>2</sup> - active_power<sup>2</sup>))</i><br>\"]\n    nonactive_power --- reactive_power[\"<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]\"]\n    apparent_power --- complex_power[\"<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i>\"]

    However, the above allows direct addition and comparison of active power and nonactive power, and also will not complain if someone will try to use watt (W) as a unit of apparent power or reactive power.

    Again, ISQ does not provide a direct answer here.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#more-base-quantities","title":"More base quantities?","text":"

    Is ISQ really based on only seven base quantities? Let's look at the definition of traffic intensity in IEC 80000-13 \"Information science and technology\":

    Quote

    number of simultaneously busy resources in a particular pool of resources.

    It looks like a definition of a specialized dimensionless quantity or, more correctly, a quantity of dimension one. This would not be the only such case. Even in the same Part 13, we can find quantities like storage capacity with a similar property.

    Only when we look closer do we start to see differences. All dimensionless quantities, even if they have their own dedicated units, can also be measured in a unit of one (1). This is true for storage capacity (also measured in bits), angular measure (also measured in radians), _solid angular measure (also measured in steradians), and more.

    However, traffic intensity can only be measured in erlangs (E), not in a unit one (1). Does it mean that it is a \"hidden\" 8-th base quantity in ISQ? If so, should it have its own dimension as well?

    Angular quantities are another interesting case here. Scientists have written petitions and papers for years to make them an additional dimension in ISQ and SI. More about this can be found in our documentation's Strong Angular System chapter.

    "},{"location":"blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/#summary","title":"Summary","text":"

    ISQ is tremendous and solves many problems we had in modeling various subjects for years in software. As a result, we have more powerful tools in our hands that allow us to deliver safer products.

    Unfortunately, ISQ, contrarily to SI, is not widely recognized, and no libraries besides mp-units model it in any programming language. Keeping it behind a paywall does not help either. We hope that posts from this series will spread in the community, raise awareness of ISQ and its benefits, and encourage authors of other libraries to implement it in their products.

    Despite all the benefits, it is essential to realize that ISQ has many problems. International standards should be specified in such a way that there is no room for ambiguity in their interpretation by different parties trying to use them. As described above, this is not the case here.

    ISQ is not ready to be unambiguously modeled in software by various vendors. Here are the most important problems to solve to allow this:

    1. ISQ needs to define basic operations on quantities:

    - what the result of addition and subtraction should be when arguments differ, \u00a0 \u00a0 - convertibility rules.

    1. The exact quantity equation recipe needs to be included for many derived quantities.
    2. Many ISQ quantities do not provide their exact relation versus other quantities of the same kind (no strict hierarchy).
    3. Some missing quantities need to be included. Others would benefit from corrected names.

    Additionally:

    • extending ISQ with affine space abstractions,
    • specifying more quantities as non-negative,
    • adding more base quantities (i.e., angle)

    could improve the safety of our programs and products that people depend on with their lives on a daily basis.

    I hope you enjoyed following this series and learned more about the International System of Quantities. Please try it out in your domain and share feedback with us. We always love to hear about the projects in which our library is being used and about use cases it helps address.

    "},{"location":"blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/","title":"Report from the Kona 2023 ISO C++ Committee meeting","text":"

    Several groups in the ISO C++ Committee reviewed the P1935: A C++ Approach to Physical Units proposal in Belfast 2019 and Prague 2020. All those groups expressed interest in the potential standardization of such a library and encouraged further work. The authors also got valuable initial feedback that highly influenced the design of the V2 version of the mp-units library.

    In the following years, we scoped on getting more feedback from the production and design. This resulted in version 2 of the mp-units library that resolved many issues the users and Committee members raised. The features and interfaces of this version are close to being the best we can get with the current version of the C++ language standard.

    We submitted three new proposals related to the standardization of the quantities and units library for the last ISO C++ Committee meeting:

    • P2980: A motivation, scope, and plan for a physical quantities and units library,
    • P2981: Improving our safety with a physical quantities and units library,
    • P2982: std::quantity as a numeric type.

    Those were reviewed and briefly discussed in several groups: Numerics (SG6), Safety & Security (SG23), and Library Evolution Working Group (LEWG). Most of the feedback was positive, and the Committee is interested in spending more time on such proposals.

    The following poll was taken by the LEWG:

    LEWG POLL: Given that our time is limited, more time should be promised for a quantities and units library

    Strongly in Favor In favor Neutral Against Strongly Against 10 13 4 0 0

    Attendance: 22 + 6

    Number of Authors: 4

    Authors\u2019 position: 4x SF

    Outcome: Strong consensus in favor

    Additionally, some concerns were raised about the large scope of the proposal. We were encouraged to return with more details and design rationale in a unified paper. This is what we are working on now for the next Committee meeting that will happen in mid-March 2024 in Tokyo.

    "},{"location":"blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/","title":"Report from the St. Louis 2024 ISO C++ Committee meeting","text":"

    We made significant progress in the standardization of this library during the ISO C++ Committee meeting in St. Louis.

    "},{"location":"blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/#p30942r3-stdbasic_fixed_string","title":"P30942R3: std::basic_fixed_string","text":"

    First, the fixed_string was unanimously forwarded from the SG18 LEWG Incubator to the Library Evolution Working Group (LEWG). The group suggested a few minor changes to the paper, which resulted in the R3 version of the proposal.

    The paper is in excellent shape, and the entire wording is ready as well. Hopefully it will progress quickly through the remaining groups in the Committee.

    "},{"location":"blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/#p3045r1-quantities-and-units-library","title":"P3045R1: Quantities and units library","text":"

    In the SG6 (Numerics), we had a really efficient discussion about the recently raised usability issues with temperatures and the Minimal Viable Product (MVP) scope.

    The following polls were taken:

    POLL: If WG21 adds anything to the standard to provide units or quantities, then such a solution must at least include the necessary abstractions for units, dimensions, and quantity kinds. (It does not have to provide system definitions, e.g. ISQ/SI definitions.)

    Strongly in Favor In favor Neutral Against Strongly Against 7 4 7 0 0

    POLL: If WG21 adds anything to the standard to provide units or quantities, then such a solution must at least include the necessary abstractions for units, dimensions, quantity kinds, and quantities of the same kind. (It does not have to provide system definitions, e.g. ISQ/SI definitions.)

    Strongly in Favor In favor Neutral Against Strongly Against 5 2 5 2 0

    POLL: If WG21 adds anything to the standard to provide units or quantities, then such a solution must at least include the necessary abstractions for units, dimensions, quantity kinds, and affine spaces. (It does not have to provide system definitions, e.g. ISQ/SI definitions.)

    Strongly in Favor In favor Neutral Against Strongly Against 5 0 8 1 0

    As we can see, there are no controversies about the first poll that states that the MVP should include at least:

    • units,
    • dimensions,
    • quantity kinds (e.g., frequency vs activity, plane angle vs solid angle, fuel_consumption vs area).

    The next polls add either:

    • quantities of the same kind (e.g., length vs width vs wavelength, potential energy vs kinetic energy),
    • the affine space abstractions (i.e., point origins and quantity_point).

    SG6 considered those less important, but no one was strongly against including those in the MVP. We were asked to return with better motivation and usage examples for those features.

    If you are depending on quantities of the same kind or quantity points in your project and you would like to see them in the std library, please let us know about your use cases.

    Besides SG6, we spent six hours in the SG18 LEWG Incubator discussing the details of the library design. The proposal was very well received, and we got a few valuable comments and suggestions that we will apply to the next version of the paper.

    "},{"location":"blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/","title":"Report from the Tokyo 2024 ISO C++ Committee meeting","text":"

    The Tokyo 2024 meeting was a very important step in the standardization of this library. Several WG21 groups reviewed proposals, and the feedback was really good.

    "},{"location":"blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/#p3045r0-quantities-and-units-library","title":"P3045R0: Quantities and units library","text":"

    The Study Group 6 (Numerics) discussed the proposal for several hours. The initial feedback was positive. There were some concerns related to the description and design of the affine space abstractions in the library. Besides that, the people in the room liked what they saw.

    We run a few polls in SG6 as well:

    POLL: The syntax number * unit is the right solution for constructing quantities. Not allowing reordering the operands is correct.

    Strongly in Favor In favor Neutral Against Strongly Against 5 4 1 0 1

    POLL: Not defining any UDLs is the right solution.

    No objection to unanimous consent.

    The paper was also briefly discussed in SG18 LEWG Incubator, and the initial feedback was also positive. No polls were taken.

    SG16 Unicode does not meet during ISO C++ Committee F2F meetings. Still, the text output chapter paper was also reviewed by it during an online meeting before Tokyo. We got good feedback and are expected to return with the updated version. No polls were taken.

    "},{"location":"blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/#p30942r1-stdbasic_fixed_string","title":"P30942R1: std::basic_fixed_string","text":"

    In the SG18 LEWG Incubator, before we started to talk about P3045R0, we spent a few hours discussing the design of the std::basic_fixed_string, which is proposed for C++26. The group gave excellent feedback, and if the R2 version addresses it properly, the paper is expected to progress to LEWG (Library Evolution Working Group) in St. Louis.

    Plenty of polls were taken:

    POLL: We should promise more committee time to pursuing std::basic_fixed_string, knowing that our time is scarce and this will leave less time for other work.

    Strongly in Favor In favor Neutral Against Strongly Against 11 0 0 0 0

    POLL: Should the constructor from a string literal be consteval?

    Strongly in Favor In favor Neutral Against Strongly Against 6 3 2 0 0

    POLL: Do we want to add .view()?

    Strongly in Favor In favor Neutral Against Strongly Against 3 5 3 0 0

    POLL: Do we want the .size member to be an integral_constant<size_t, N> (and .empty to be bool_constant<N==0>)?

    Strongly in Favor In favor Neutral Against Strongly Against 5 2 2 2 0

    POLL: Should the index operator[] return a reference to const?

    Strongly in Favor In favor Neutral Against Strongly Against 2 2 3 3 0

    POLL: Should the constructor from a string literal have a precondition that txt[N] == 0?

    Strongly in Favor In favor Neutral Against Strongly Against 6 1 2 0 2"},{"location":"getting_started/contributing/","title":"Contributing","text":"

    \ud83d\udc4d\ud83c\udf89 First off, thanks for taking the time to contribute! \ud83c\udf89\ud83d\udc4d

    "},{"location":"getting_started/contributing/#mp-units-documentation","title":"mp-units documentation","text":"

    Before contributing, it is highly recommended to familiarize yourself with our official documentation.

    This file is also a part of it, and this is why it has non-standard Markdown formatting (which can be seen when reading in a regular Markdown renderer). To benefit from full mkdocs rendering, please switch to the Contributing chapter of our documentation.

    "},{"location":"getting_started/contributing/#where-to-start","title":"Where to start?","text":"

    If you are looking for a good issue to start with, please check the following:

    • good first issue - issues that should be pretty simple to implement,
    • help wanted - issues that typically are a bit more involved than beginner issues,
    • high priority - things to fix ASAP but often of higher complexity.
    "},{"location":"getting_started/contributing/#gitpod","title":"Gitpod","text":"

    The easiest way to start coding is to jump straight into Gitpod environment. You can either click the button below

    or prefix any mp-units URL (main branch, other branches, issues, PRs, ...) in your web browser with gitpod.io/# (e.g., https://gitpod.io/#https://github.com/mpusz/mp-units).

    The above environment provides you with:

    • all supported compilers for Linux development and the latest version of build tools like cmake and conan,
    • all Conan dependencies preinstalled on the machine,
    • all documentation generation tools ready to use,
    • completed prebuilds for all targets (Debug and Release builds for each compiler),
    • VSCode preconfigured to benefit from all the above.
    "},{"location":"getting_started/contributing/#building-testing-and-packaging","title":"Building, testing, and packaging","text":"

    Alternatively, please refer to our official docs for download, build, and install instructions with the below changes if you want to set up a development environment on your local machine.

    "},{"location":"getting_started/contributing/#conan-configuration-properties","title":"Conan configuration properties","text":"user.mp-units.build:all

    Enables compilation of all the source code, including tests and examples. To support this, it requires some additional Conan build dependencies described in Repository directory tree and dependencies. It also runs unit tests during the Conan build (unless tools.build:skip_test configuration property is set to True).

    user.mp-units.build:skip_la

    If user.mp-units.build:all is enabled, among others, Conan installs the external wg21-linear_algebra dependency and enables the compilation of linear algebra-based tests and usage examples. Such behavior can be disabled with this option.

    user.mp-units.analyze:clang-tidy

    Enables clang-tidy analysis.

    "},{"location":"getting_started/contributing/#cmake-options-for-mp-units-project-developers","title":"CMake options for mp-units project developers","text":"MP_UNITS_DEV_BUILD_LA

    2.2.0 \u00b7 ON/OFF (Default: ON)

    Enables building code depending on the linear algebra library.

    MP_UNITS_DEV_IWYU

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Enables include-what-you-use analysis.

    MP_UNITS_DEV_CLANG_TIDY

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Enables clang-tidy analysis.

    MP_UNITS_DEV_TIME_TRACE

    2.5.0 \u00b7 NONE/ALL/MODULES/HEADERS (Default: NONE)

    Enables compilation performance data collection with -ftime-trace for clang compilers.

    All our unit tests compile only for headers and never for modules. To allow fair comparison, MODULES and HEADERS do not enable the data collection for unit tests. This means that they affect only the core, systems, and examples.

    Please use ALL to profile unit tests as well.

    "},{"location":"getting_started/contributing/#building-the-entire-repository","title":"Building the entire repository","text":"

    To build all the mp-units source code (with unit tests and examples), you should:

    1. Use the CMakeLists.txt from the top-level directory.
    2. Run Conan with user.mp-units.build:all = True.
    git clone https://github.com/mpusz/mp-units.git && cd mp-units\nconan build . -pr <your_conan_profile> -s compiler.cppstd=23 -c user.mp-units.build:all=True -b missing\n

    The above will download and install all of the dependencies needed for the development of the library, build all of the source code, and run unit tests.

    If you prefer to build the project via CMake rather than Conan, then you should replace the conan build with conan install command and then follow with a regular CMake build and testing:

    conan install . -pr <your_conan_profile> -s compiler.cppstd=23 -c user.mp-units.build:all=True -b missing\ncmake --preset conan-default\ncmake --build --preset conan-release\ncmake --build --preset conan-release --target all_verify_interface_header_sets\ncmake --build --preset conan-release --target test\n

    Hint

    To ensure that we always build all the targets and to save some typing of the Conan commands, we can set the following in the ~/.conan2/global.conf:

    user.mp-units.build:all=True\n
    "},{"location":"getting_started/contributing/#packaging","title":"Packaging","text":"

    To test CMake installation and Conan packaging run:

    conan create . --user <username> --channel <channel> -pr <your_conan_profile> -s compiler.cppstd=23 \\\n               -c user.mp-units.build:all=True -b missing\n

    The above will create a Conan package and run tests provided in ./test_package directory.

    In case you would like to upload mp-units package to the Conan server, do the following:

    conan upload -r <remote-name> --all mp-units/2.2.0@<user>/<channel>\n
    "},{"location":"getting_started/contributing/#building-documentation","title":"Building documentation","text":"

    We are building our documentation using Material for MkDocs. The easiest way to install all the required dependencies is with pip:

    pip install -U mkdocs-material mkdocs-rss-plugin\n

    Additionally, a Cairo Graphics library is required by Material for MkDocs. Please follow the official MkDocs documentation to install it.

    After that, you can either:

    • easily start a live server to preview the documentation as you write

      mkdocs serve\n
    • build the documentation

      mkdocs build\n
    "},{"location":"getting_started/contributing/#generating-api-reference","title":"Generating API reference","text":"

    We need to take a few steps to set up our environment so that we are ready to generate API reference documents.

    First, we need to satisfy the requirements described in https://github.com/cplusplus/draft and https://github.com/Eelis/cxxdraft-htmlgen. On the Ubuntu platform, this is equivalent to the following instructions run from the user's home directory:

    sudo apt install latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended lmodern\nsudo apt install haskell-stack graphviz nodejs npm ghc cabal-install\nnpm install split mathjax-full mathjax-node-sre mathjax-node-cli\ncabal update\n

    On some platforms, installing mathjax-node-cli through npm does update the system's PATH environment variable resulting in tex2html not found errors. In such cases we need to add the .bin folder to the PATH environment variable manually:

    echo \"export PATH=\\\"~/node_modules/.bin:\\$PATH\\\"\" >> ~/.bashrc && source ~/.bashrc\n

    Next, we need to clone the following git repositories:

    • https://github.com/JohelEGP/jegp.cmake_modules
    • standardese_sources_base branch of https://github.com/JohelEGP/draft
    • standardese_sources_base branch of https://github.com/JohelEGP/cxxdraft-htmlgen

    For example:

    git clone https://github.com/JohelEGP/jegp.cmake_modules.git --depth=1\ngit clone https://github.com/JohelEGP/draft.git --branch=standardese_sources_base --depth=1\ngit clone https://github.com/JohelEGP/cxxdraft-htmlgen.git --branch=standardese_sources_base --depth=1\n

    Now, we are ready to start building our API reference. First, we need to configure CMake with the following:

    cmake -S docs/api_reference/src -B build/docs/api_reference \\\n      -DCMAKE_MODULE_PATH=\"<path to gh:JohelEGP/jegp.cmake_modules>/modules\" \\\n\u00a0 \u00a0 \u00a0 -DJEGP_STANDARDESE_SOURCES_GIT_REPOSITORY=\"<path to gh:JohelEGP/draft>\" \\\n      -DJEGP_CXXDRAFT_HTMLGEN_GIT_REPOSITORY=\"<path to gh:JohelEGP/cxxdraft-htmlgen>\"\n

    Then we need to build the docs with CMake:

    cmake --build build/docs/api_reference\n

    In the end, we need to move the generated documentation to the docs/api_reference/gen subdirectory:

    mv build/docs/api_reference/mp-units.html docs/api_reference/gen\n

    or just link the entire directory:

    ln -sf ../../build/docs/api_reference/mp-units.html docs/api_reference/gen\n
    "},{"location":"getting_started/contributing/#before-committing-git-changes","title":"Before committing git changes","text":"

    There are a few steps recommended to check before committing and pushing your changes to the git repository.

    "},{"location":"getting_started/contributing/#naming-conventions","title":"Naming conventions","text":"

    Here are the main rules for naming things in this repo:

    • types, functions, variables use standard_case,
    • template parameters use PascalCase,
    • C++ concept names, for now, use PascalCase, but we plan to change it (see GitHub Issue #93 for more details).
    "},{"location":"getting_started/contributing/#unified-code-formatting","title":"Unified code formatting","text":"

    A formatting standard is enforced with the pre-commit script. Before committing your changes, please do the following:

    pip install -U pre-commit\npre-commit run --all-files\n

    This will run:

    • clang-format for code formatting with the .clang-format file provided in the repo,
    • cmake-format to format the CMake files,
    • some other checks (e.g., python script checkers, whitespaces, etc.).

    The script will run on all the files in the repo and will apply the changes in place when needed. After the script is done, please make sure to review and stage all those changes for the git commit.

    "},{"location":"getting_started/contributing/#backward-compatibility","title":"Backward compatibility","text":"

    Before submission, please remember to check if the code compiles fine on the supported compilers. The CI will check it anyway, but it is good to check at least some of the configurations before pushing changes. Especially older compilers can be tricky as those do not have full C++20 conformance. The official list of supported compilers can always be found in the C++ compiler support (API/ABI) chapter of our documentation.

    "},{"location":"getting_started/cpp_compiler_support/","title":"C++ compiler support (API/ABI)","text":"

    Info

    mp-units library tries to provide the best user experience possible with the C++ language. To achieve that, it extensively uses the latest C++ language features.

    Even though the library benefits from the latest C++ versions (if available), C++20 is enough to compile and use all of the library's functionality. Newer features can be hidden behind some preprocessor macros providing a backward-compatible way to use them.

    The table below provides the minimum compiler version required to compile the code using a specific C++ feature:

    C++ Feature C++ version gcc clang apple-clang MSVC Minimum support 20 12 16 15 194 std::format 20 13 17 None 194 C++ modules 20 None 17 None None import std; 23 None 18 None None Explicit this parameter 23 14 18 None None MSVC bugs

    MSVC still has a poor C++20 conformance. We had to make many workarounds to our codebase to make it compile on this compiler. Usage of such nasty preprocessor macros degrade the readability and maintainability of our code. This is why we've applied those patches to the main library code but not to unit tests and examples. Those still do not compile on MSVC.

    Here is a list of the most important MSVC bugs:

    • Discrepancy in Behavior of operator= and operator for Multiplying int and double at compile time
    • Syntax error when using non-type template parameters in templated class member function
    • Type always preferred over value when using qualified identifiers

    Please upvote them so they get a higher fixing priority at Microsoft.

    Important

    Enabling/disabling features listed above may influence the API of the library and the ABI of the customers' projects.

    "},{"location":"getting_started/cpp_compiler_support/#stdformat","title":"std::format","text":"
    • Provides powerful text formatting capabilities for C++.
    • An alternative fmtlib library can be used instead if
      • the C++ language feature is not supported,
      • the customer's project did not switch to std::format yet (even when the compiler supports it).
    • To write code with wide compatibility a dedicated macro may be used.
    • Tested with __cpp_lib_format feature test macro.
    • Related build options:
      • Conan: std_format
      • CMake: MP_UNITS_API_STD_FORMAT
    "},{"location":"getting_started/cpp_compiler_support/#c-modules","title":"C++ modules","text":"
    • Provide new way to share declarations and definitions across translation units.
    • If used, the library will distribute both \"old-style\" headers and module interface units
      • associated with the same CMake targets.
    • Even with full compiler support, a user may still decide to not pay for C++ modules compilation if they are not needed by the customer's project.
    • Feature test macro is not used for testing here because even if the compiler does not support the entire C++ feature (e.g. header units), it is enough to build modules for this library.
    • Related build options:
      • Conan: cxx_modules
      • CMake: MP_UNITS_BUILD_CXX_MODULES

    Note

    More requirements for C++ modules support can be found in the CMake's documentation.

    "},{"location":"getting_started/cpp_compiler_support/#import-std","title":"import std;","text":"
    • If enabled, the library will obtain all the definitions from the std namespace via import std; instead of the \"old-style\" header includes.
    • Related build options:
      • Conan: import_std
      • CMake: CMAKE_CXX_MODULE_STD
    "},{"location":"getting_started/cpp_compiler_support/#explicit-this-parameter","title":"Explicit this parameter","text":"
    • This feature removes the need for the usage of the CRTP idiom in the quantity_spec definitions.
    • To write code with wide compatibility a dedicated macro may be used.
    • Tested with __cpp_explicit_this_parameter feature test macro.
    • Related build options:
      • Conan: no_crtp
      • CMake: MP_UNITS_API_NO_CRTP
    "},{"location":"getting_started/faq/","title":"Frequently Asked Questions","text":""},{"location":"getting_started/faq/#why-do-we-spell-metre-instead-of-meter","title":"Why do we spell metre instead of meter?","text":"

    This is how the BIPM defines it in the SI Brochure (British English spelling by default).

    "},{"location":"getting_started/faq/#why-dont-we-use-udls-to-create-quantities","title":"Why don't we use UDLs to create quantities?","text":"

    Many reasons make UDLs a poor choice for a physical units library:

    1. UDLs work only with literals (compile-time known values). Our observation is that besides the unit tests, there are only a few compile-time known quantity values used in the production code. Please note that for physical constants, we recommend using Faster-than-lightspeed Constants.
    2. Typical implementations of UDLs tend to always use the widest representation type available. In the case of std::chrono::duration, the following is true:

      using namespace std::chrono_literals;\nauto d1 = 42s;\nauto d2 = 42.s;\nstatic_assert(std::is_same_v<decltype(d1)::rep, std::int64_t>);\nstatic_assert(std::is_same_v<decltype(d2)::rep, long double>);\n

    When such UDL is intermixed in arithmetics with any quantity type of a shorter representation type, it will always expand it to the longest one. In other words, such long type spreads until all types use it everywhere.

    1. While increasing the coverage for the library, we learned that many unit symbols conflict with built-in types or numeric extensions. A few of those are: F (farad), J (joule), W (watt), K (kelvin), d (day), l or L (litre), erg, ergps. Usage of the _ prefix would make it work for mp-units, but in case the library is standardized, those naming collisions would be a big issue. This is why we came up with the _q_ prefix that would become q_ after standardization (e.g., 42q_s), which is not that nice anymore.

    2. UDLs with the same identifiers defined in different namespace can't be disambiguated in the C++ language. If both SI and CGS systems define _q_s UDL for a second unit, then it would not be possible to specify which one to use in case both namespaces are \"imported\" with using directives.

    3. Another bad property of UDLs is that they do not compose. A coherent unit of angular momentum would have a UDL specified as _q_kg_m2_per_s. Now imagine that we want to make every possible user happy. How many variations of that unit would we predefine for differently scaled versions of all unit ingredients?

    4. UDLs are also really expensive to define and specify. Typically, for each unit, we need two definitions. One for integral and another one for floating-point representation. Before the V2 framework, the coherent unit of angular momentum was defined as:

      constexpr auto operator\"\" _q_kg_m2_per_s(unsigned long long l)\n{\n  gsl_Expects(std::in_range<std::int64_t>(l));\n  return angular_momentum<kilogram_metre_sq_per_second, std::int64_t>(static_cast<std::int64_t>(l));\n}\n\nconstexpr auto operator\"\" _q_kg_m2_per_s(long double l)\n{\n  return angular_momentum<kilogram_metre_sq_per_second, long double>(l);\n}\n
    "},{"location":"getting_started/faq/#why-cant-i-create-a-quantity-by-passing-a-number-to-a-constructor","title":"Why can't I create a quantity by passing a number to a constructor?","text":"

    A quantity class template in the mp-units library has no publicly available constructor taking a raw value. Such support is provided by the std::chrono::duration and was pointed out to us as a red flag safety issue by a few parties already.

    Consider the following structure and a code using it:

    struct X {\n  std::vector<std::chrono::milliseconds> vec;\n  // ...\n};\n
    X x;\nx.vec.emplace_back(42);\n

    Everything works fine for years until, at some point, someone changes the structure to:

    struct X {\n  std::vector<std::chrono::microseconds> vec;\n  // ...\n};\n

    The code continues to compile just fine, but all the calculations are off now. This is why we decided to not follow this path.

    In the mp-units library, both a number and a unit have to always be explicitly provided in order to form a quantity.

    Note

    The same applies to the construction of quantity_point using an explicit point origin. To prevent similar safety issues during maintenance, the initialization always requires providing both a quantity and a PointOrigin that we use as a reference point.

    "},{"location":"getting_started/faq/#why-a-dimensionless-quantity-is-not-just-a-fundamental-arithmetic-type","title":"Why a dimensionless quantity is not just a fundamental arithmetic type?","text":"

    In the initial design of this library, the resulting type of division of two quantities was their common representation type:

    static_assert(std::is_same_v<decltype(10 * km / (5 * km)), int>);\n

    First of all, this was consistent with std::chrono::duration behavior. Additional reasoning behind it was not providing a false impression of a strong quantity type for something that looks and feels like a regular number. Also, all of the mathematic and trigonometric functions were working fine out of the box with such representation types, so we did not have to rewrite sin(), cos(), exp(), and others.

    However, the feedback we got from the production usage was that such an approach is really bad for generic programming. It is hard to handle the result of the two quantities' division (or multiplication) as it might be either a quantity or a fundamental type. If we want to raise such a result to some power, we must use units::pow or std::pow depending on the resulting type. Those are only a few issues related to such an approach.

    Moreover, suppose we divide quantities of the same dimension but with units of significantly different magnitudes. In that case, we may end up with a really small or a huge floating-point value, which may result in losing lots of precision. Returning a dimensionless quantity from such cases allows us to benefit from all the properties of scaled units and is consistent with the rest of the library.

    Note

    More information on the current design can be found in the Dimensionless Quantities chapter.

    "},{"location":"getting_started/faq/#why-derived-units-order-is-not-preserved-from-the-multiplication","title":"Why derived units order is not preserved from the multiplication?","text":"

    It might be surprising, but the quantities and units multiplication order does not impact the order of components in the derived unit. Let's try the following example:

    std::println(\"{}\", 42 * kW * h);\nconstexpr auto kWh = kW * h;\nstd::println(\"{}\", 42 * kWh);\n

    The above prints:

    42 h kW\n42 h kW\n

    Some users could expect to see 42 kWh or 42 kW h in the output. It is not the case and for a very good reason. As stated in Simplifying the resulting symbolic expressions, to be able to reason about and simplify units, the library needs to order them in an appropriate order.

    Maybe this default order could be improved a bit, but according to international standards, there is no generic ordering rule. Various quantities use different, often domain-specific, ordering of derived unit components.

    Let's see what SI says here:

    Derived quantity Symbol Derived unit expressed in terms of base units electric field strength V m\u207b\u00b9 kg m s\u207b\u00b3 A\u207b\u00b9 electric charge density C m\u207b\u00b3 A s m\u207b\u00b3 exposure (x- and \u03b3-rays) C kg\u207b\u00b9 A s kg\u207b\u00b9

    However, there is a workaround. A user can define its own named unit for a derived unit and provide the custom symbol text that suits the project's requirements. For example, the above case could be addressed with:

    inline constexpr struct kilowatt_hour final : named_unit<\"kWh\", kW * h> {} kilowatt_hour;\ninline constexpr auto kWh = kilowatt_hour;\n

    With the above, we can refactor the above code to:

    std::println(\"{}\", 42 * kWh);\nstd::println(\"{}\", (42 * kW * h).in(kWh));\n

    Both lines will produce an expected \"42 kWh\" unit in the output.

    Important

    Please note that this makes the entire \"kWh\" a single, indivisible entity that is not subject to simplification rules. This means that 42 * kWh / (2 * h) will result with 21 kWh/h rather than 21 kW. To get the latter, the user needs to explicitly provide a new derived unit:

    std::println(\"{}\", (42 * kWh / (2 * h)).in(kW));\n
    "},{"location":"getting_started/faq/#why-do-the-identifiers-for-concepts-in-the-library-use-camelcase","title":"Why do the identifiers for concepts in the library use CamelCase?","text":"

    Initially, C++20 was meant to use CamelCase for all the concept identifiers. All the concepts from the std::ranges library were merged with such names into the standard document draft. Frustratingly, CamelCase concepts got dropped from the C++ standard at the last moment before releasing C++20. Now, we are facing the predictable consequences of running out of names.

    As long as some concepts in the library could be easily named with a standard_case there are some that are hard to distinguish from the corresponding type names, such as Quantity, QuantityPoint, QuantitySpec, or Reference. This is why we decided to use CamelCase consistently for all the concept identifiers to make it clear when we are talking about a type or concept identifier.

    However, we are aware that this might be a temporary solution. In case the library gets standardized, we can expect the ISO C++ Committee to bikeshed/rename all of the concept identifiers to a standard_case, even if it will result in a harder to understand code.

    Note

    In case you have a good idea of how to rename existing concepts to the standard_case, please let us know in the associated GitHub Issue.

    "},{"location":"getting_started/faq/#why-utf-8-quantity-symbols-are-used-by-default-instead-of-portable-characters","title":"Why UTF-8 quantity symbols are used by default instead of portable characters?","text":"

    Both C++ and ISO 80000 are standardized by the ISO. ISO 80000 and the SI standards specify UTF-8 symbols as the official unit names for some quantities (e.g. \u03a9 symbol for the resistance quantity). As the mp-units library will be proposed for standardization as a part of the C++ Standard Library we have to obey the rules and be consistent with ISO specifications.

    Note

    We do understand engineering reality and the constraints of some environments. This is why the library has the option of Portable Quantity Symbols.

    "},{"location":"getting_started/faq/#why-dont-we-have-cmake-options-to-disable-the-building-of-tests-and-examples","title":"Why don't we have CMake options to disable the building of tests and examples?","text":"

    Over time, many people provided PRs proposing adding options to build tests and examples conditionally. Here are a few examples:

    • Add CMake options for disabling docs, examples and tests
    • build: add options to disable part of the build
    • CMake Refactoring and Option Cleanup

    We admit this is a common practice in the industry, but we also believe this is a bad pattern.

    First, the only need for such options comes when a user wants to use add_subdirectory() in CMake to handle dependencies. Such an approach does not scale and should be discouraged. There is little use for such a practice in times when we have dedicated package managers like Conan.

    The second thing is that our observation is that many people are fixed on disabling \"unneeded\" subdirectories from compilation, but they do not see or address the biggest issue, which is polluting user's build environment with our development-specific settings. Propagating our restrictive compilation flags to user's project is not the best idea as it might cause a lot of harm if this project stops to compile because of that.

    Last but not least, not having those options is on purpose. Top level CMakeLists.txt file should only be used by mp-units developers and contributors as an entry point for the project's development. We want to ensure that everyone will build ALL the code correctly before pushing a commit. Having such options would allow unintended issues to leak to PRs and CI.

    This is why our projects have two entry points:

    • ./CMakeLists.txt is to be used by projects developers to build ALL the project code with really restrictive compilation flags,
    • ./src/CMakeLists.txt contains only a pure library definition and should be used by the customers that prefer to use CMake's add_subdirectory() to handle the dependencies.

    Note

    For more details on this please refer to the CMake + Conan: 3 Years Later - Mateusz Pusz lecture that Mateusz Pusz provided at the C++Now 2021 conference.

    "},{"location":"getting_started/installation_and_usage/","title":"Installation And Usage","text":"

    This chapter provides all the necessary information to obtain mp-units and build the user's source code using it.

    "},{"location":"getting_started/installation_and_usage/#obtaining-dependencies","title":"Obtaining dependencies","text":"

    This library assumes that most of the dependencies will be provided by the Conan Package Manager. If you want to obtain required dependencies by other means, some modifications to the library's CMake files might be needed.

    Conan quick intro

    In case you are not familiar with Conan, to install it (or upgrade) just do:

    pip install -U conan\n

    After that, you might need to add a custom profile file for your development environment in ~/.conan2/profiles directory. An example profile can look as follows:

    ~/.conan2/profiles/gcc12
    [settings]\narch=x86_64\nbuild_type=Release\ncompiler=gcc\ncompiler.cppstd=20\ncompiler.libcxx=libstdc++11\ncompiler.version=14\nos=Linux\n\n[conf]\ntools.build:compiler_executables={\"c\": \"gcc-14\", \"cpp\": \"g++-14\"}\n

    Setting the language version

    Please note that the mp-units library requires at least C++20 to be set in a Conan profile or forced via the Conan command line. If we do the former, we will not need to provide -s compiler.cppstd=20 every time we run a Conan command line (as provided in the command line instructions below).

    Using Ninja as a CMake generator for Conan

    It is highly recommended to set Ninja as a CMake generator for Conan. To do so, we could create a ~/.conan2/global.conf file that will set tools.cmake.cmaketoolchain:generator to one of the Ninja generators. For example:

    ~/.conan2/global.conf
    tools.cmake.cmaketoolchain:generator=\"Ninja Multi-Config\"\n

    Separate build folders for different configurations

    ~/.conan2/global.conf file may also set tools.cmake.cmake_layout:build_folder_vars which makes working with several compilers or build configurations easier. For example, the below line will force Conan to generate separate CMake presets and folders for each compiler and C++ standard version:

    ~/.conan2/global.conf
    tools.cmake.cmake_layout:build_folder_vars=[\"settings.compiler\", \"settings.compiler.version\", \"settings.compiler.cppstd\"]\n

    In such a case, we will need to use a configuration-specific preset name in the Conan instructions provided below rather than just conan-default and conan-release (e.g., conan-gcc-13-23 and conan-gcc-13-23-release)

    CMake with presets support

    It is recommended to use at least CMake 3.23 to build this project to benefit from CMake Presets generated by Conan. All build instructions below assume that you have such support. If not, your CMake invocations have to be replaced with something like:

    mkdir build && cd build\ncmake .. -G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=<path_to_generators_dir>/conan_toolchain.cmake\ncmake --build . --config Release\n

    Tip

    In case you can't use CMake 3.23 but you have access to CMake 3.20 or later, you can append -c tools.cmake.cmaketoolchain.presets:max_schema_version=2 to the conan install command which will force Conan to use an older version of the CMake Presets schema.

    "},{"location":"getting_started/installation_and_usage/#build-options","title":"Build options","text":"

    Note

    Most of the below options are related to the C++ language features available in the compilers. Please refer to the C++ compiler support chapter to learn more about which C++ features are required for each option and which compilers support them.

    "},{"location":"getting_started/installation_and_usage/#conan-options","title":"Conan options","text":"cxx_modules

    2.2.0 \u00b7 True/False (Default: automatically determined from settings)

    Configures CMake to add C++ modules to the list of default targets.

    import_std

    2.3.0 \u00b7 True/False (Default: automatically determined from settings)

    Enables import std; usage.

    std_format

    2.2.0 \u00b7 True/False (Default: automatically determined from settings)

    Enables the usage of std::format and associated facilities for text formatting. If it is not supported, then the {fmt} library is used instead.

    no_crtp

    2.2.0 \u00b7 True/False (Default: automatically determined from settings)

    Removes the need for the usage of the CRTP idiom in the quantity_spec definitions.

    contracts

    2.2.0 \u00b7 none/gsl-lite/ms-gsl (Default: see below)

    Enables checking of preconditions and additional assertions in the code.

    If the automatically determined default for import_std is True, then the contracts option is set to none by default. gsl-lite otherwise.

    freestanding

    2.2.0 \u00b7 True/False (Default: False)

    Configures the library in the freestanding mode. When enabled, the library's source code will build with the compiler's -ffreestanding compilation option without any issues.

    CMake options to set when Conan is not being used"},{"location":"getting_started/installation_and_usage/#cmake-options","title":"CMake options","text":"

    Conan will automatically set all the below CMake options based on its configuration (described above). Manual setting of the below CMake options is only needed when Conan is not being used.

    MP_UNITS_BUILD_AS_SYSTEM_HEADERS

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Exports library as system headers.

    MP_UNITS_BUILD_CXX_MODULES

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Adds C++ modules to the list of default targets.

    MP_UNITS_API_STD_FORMAT

    2.2.0 \u00b7 ON/OFF (Default: automatically determined)

    Enables the usage of std::format and associated facilities for text formatting. If it is not supported, then the {fmt} library is used instead.

    MP_UNITS_API_NO_CRTP

    2.2.0 \u00b7 ON/OFF (Default: automatically determined)

    Removes the need for the usage of the CRTP idiom in the quantity_spec definitions.

    MP_UNITS_API_CONTRACTS

    2.2.0 \u00b7 NONE/GSL-LITE/MS-GSL (Default: GSL-LITE)

    Enables checking of preconditions and additional asserts in the code.

    MP_UNITS_API_FREESTANDING

    2.2.0 \u00b7 ON/OFF (Default: OFF)

    Configures the library in the freestanding mode. When enabled, the library's source code should build with the compiler's -ffreestanding compilation option without any issues.

    "},{"location":"getting_started/installation_and_usage/#installation-and-reuse","title":"Installation and reuse","text":"

    There are many different ways of installing/reusing mp-units in your project. Below we mention only a few of many options possible.

    Important: Prefer using Conan if possible

    The easiest and most recommended way to obtain mp-units is with the Conan package manager. See Conan + CMake (release) for a detailed instruction.

    "},{"location":"getting_started/installation_and_usage/#conan-cmake-release","title":"Conan + CMake (release)","text":"

    Tip

    If you are new to the Conan package manager you may want to read Obtaining Dependencies and refer to the Consuming packages chapter of the official Conan documentation for more information.

    mp-units releases are hosted on Conan-Center. The following steps may be performed to obtain an official library release:

    1. Create Conan configuration file (either conanfile.txt or conanfile.py) in your project's top-level directory and add mp-units as a dependency of your project. For example, the simplest file may look as follows:

      conanfile.txtconanfile.py
      [requires]\nmp-units/2.4.0\n\n[options]\n# The below mp-units options are set to defaults by Conan.\n# Uncomment and set to an explicit value to override the defaults.\n#\n# mp-units*:cxx_modules=True\n# mp-units*:import_std=False\n# mp-units*:std_format=True\n# mp-units*:no_crtp=True\n# mp-units*:contracts=gsl-lite\n# mp-units*:freestanding=False\n\n[layout]\ncmake_layout\n\n[generators]\nCMakeToolchain\nCMakeDeps\n
      from conan import ConanFile\nfrom conan.tools.build import can_run\nfrom conan.tools.cmake import CMake, cmake_layout\n\nclass MPUnitsTestConan(ConanFile):\n    settings = \"os\", \"arch\", \"compiler\", \"build_type\"\n    generators = \"CMakeDeps\", \"CMakeToolchain\"\n\n    def requirements(self):\n        self.requires(\n            \"mp-units/2.4.0\",\n            options={\n                # The below mp-units options are set to defaults by Conan.\n                # Uncomment and set to an explicit value to override the defaults.\n                #\n                # \"cxx_modules\": False,\n                # \"import_std\": False,\n                # \"std_format\": True,\n                # \"no_crtp\": True,\n                # \"contracts\": \"gsl-lite\",\n                # \"freestanding\": False,\n            },\n        )\n\n    def layout(self):\n        cmake_layout(self)\n\n    def build(self):\n        cmake = CMake(self)\n        cmake.configure()\n        cmake.build()\n        if can_run(self):\n            cmake.ctest(cli_args=[\"--output-on-failure\"])\n
    2. Import mp-units and its dependencies definitions with find_package:

      find_package(mp-units REQUIRED)\n
    3. Link your CMake targets with mp-units:

      target_link_libraries(<your_target> <PUBLIC|PRIVATE|INTERFACE> mp-units::mp-units)\n
    4. Download, build, and install Conan dependencies before running the CMake configuration step:

      conanfile.txt or conanfile.pyconanfile.py only
      conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing\ncmake --preset conan-default\ncmake --build --preset conan-release\ncmake --build --preset conan-release --target test\n
      conan build . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing\n
    "},{"location":"getting_started/installation_and_usage/#conan-cmake-live-at-head","title":"Conan + CMake (Live At Head)","text":"

    This chapter describes the procedure to Live At Head, which means using the latest stable version of mp-units all the time.

    Note

    Please note that even though the Conan packages that you will be using are generated ONLY for builds that are considered stable (passed our CI tests), some minor regressions may happen (CI and C++ build environments are not perfect yet). Also, please expect that the library interface might, and probably will, change occasionally. Even though we do our best, such changes might not be reflected in the project's documentation right away.

    The procedure is similar to the one described in Conan + CMake (release) with the following differences:

    1. Before starting the previous procedure, add mp-units remote to your Conan configuration:

      conan remote add conan-mpusz https://mpusz.jfrog.io/artifactory/api/conan/conan-oss\n
    2. In your Conan configuration file, provide the package identifier of the mpusz/testing stream:

      conanfile.txtconanfile.py
      [requires]\nmp-units/2.5.0@mpusz/testing\n\n[options]\n# The below mp-units options are set to defaults by Conan.\n# Uncomment and set to an explicit value to override the defaults.\n#\n# mp-units*:cxx_modules=True\n# mp-units*:import_std=False\n# mp-units*:std_format=True\n# mp-units*:no_crtp=True\n# mp-units*:contracts=gsl-lite\n# mp-units*:freestanding=False\n\n[layout]\ncmake_layout\n\n[generators]\nCMakeToolchain\nCMakeDeps\n
      from conan import ConanFile\nfrom conan.tools.build import can_run\nfrom conan.tools.cmake import CMake, cmake_layout\n\nclass MPUnitsTestConan(ConanFile):\n    settings = \"os\", \"arch\", \"compiler\", \"build_type\"\n    generators = \"CMakeDeps\", \"CMakeToolchain\"\n\n    def requirements(self):\n        self.requires(\n            \"mp-units/2.5.0@mpusz/testing\",\n            options={\n                # The below mp-units options are set to defaults by Conan.\n                # Uncomment and set to an explicit value to override the defaults.\n                #\n                # \"cxx_modules\": False,\n                # \"import_std\": False,\n                # \"std_format\": True,\n                # \"no_crtp\": True,\n                # \"contracts\": \"gsl-lite\",\n                # \"freestanding\": False,\n            },\n        )\n\n    def layout(self):\n        cmake_layout(self)\n\n    def build(self):\n        cmake = CMake(self)\n        cmake.configure()\n        cmake.build()\n        if can_run(self):\n            cmake.ctest(cli_args=[\"--output-on-failure\"])\n

      Tip

      The identifiers of the latest packages can always be found in the project's README file or on the project's Artifactory.

    3. Force Conan to check for updated recipes with -u:

      conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing -u\n
    Alternative installation scenarios"},{"location":"getting_started/installation_and_usage/#copy","title":"Copy","text":"

    As mp-units is a C++ header-only library you can simply copy all needed src/*/include subdirectories to your source tree.

    Note

    In such a case, you are on your own to ensure all the dependencies are installed and their header files can be located during the build. Please also note that some compiler-specific flags are needed to make the code compile without issues.

    "},{"location":"getting_started/installation_and_usage/#copy-cmake","title":"Copy + CMake","text":"

    If you copy the mp-units library source code from the project's ./src directory (not the entire repo from its root), you can reuse CMake targets defined by the library. To do so, you should use CMakeLists.txt file from the ./src directory:

    add_subdirectory(<path_to_mp_units_lib_folder>)\n# ...\ntarget_link_libraries(<your_target> <PUBLIC|PRIVATE|INTERFACE> mp-units::mp-units)\n

    Note

    You are still on your own to make sure all the dependencies are installed and their header and CMake configuration files can be located during the build.

    Important: Library users should not use the top-level CMake file

    Top level CMakeLists.txt file should only be used by mp-units developers and contributors as an entry point for the project's development. ./src/CMakeLists.txt contains only a pure library definition and should be used by the customers that prefer to use CMake's add_subdirectory() to handle the dependencies.

    To learn more about the rationale, please check our FAQ.

    "},{"location":"getting_started/installation_and_usage/#install","title":"Install","text":"

    If you don't want to use Conan in your project and just want to install the mp-units library on your file system, and use find_package(mp-units) from another repository to find it; it is enough to perform the following steps:

    conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing\nmv CMakeUserPresets.json src\ncd src\ncmake --preset conan-default -DCMAKE_INSTALL_PREFIX=<your_installation_path>\ncmake --build --preset conan-release --target install\n
    "},{"location":"getting_started/introduction/","title":"Introduction","text":"

    mp-units is a Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation. The initial versions of the library were inspired by the std::chrono::duration but with each release, the interfaces diverged from the original to provide a better user experience.

    Info

    A brief introduction to the library's interfaces and the rationale for changes in version 2.0 of mp-units were provided in detail by Mateusz Pusz in the \"The Power of C++ Templates With mp-units: Lessons Learned & a New Library Design\" talk at the C++ on Sea 2023 conference.

    "},{"location":"getting_started/introduction/#open-source","title":"Open Source","text":"

    mp-units is Free and Open Source, with a permissive MIT license. Check out the source code and issue tracking (for questions and support, reporting bugs, suggesting feature requests and improvements) at https://github.com/mpusz/mp-units.

    "},{"location":"getting_started/introduction/#with-the-users-experience-in-mind","title":"With the User's Experience in Mind","text":"

    Most of the critical design decisions in the library are dictated by the requirement of providing the best user experience possible. Other C++ physical units libraries are \"famous\" for their enormous and hard-to-understand error messages (one line of the error log often does not fit on one slide). The ultimate goal of mp-units is to improve this and make compile-time errors and debugging as easy and user-friendly as possible.

    To achieve this goal, several techniques are applied:

    • usage of C++20 concepts that improve compile-times and the readability of error messages when compared to the traditional template metaprogramming with SFINAE,
    • usage of strong types for framework entities (instead of type aliases),
    • usage of symbolic expressions to improve the readability of generated types,
    • limiting the number of template arguments to the bare minimum.

    Important: It is all about errors

    In many generic C++ libraries, compile-time errors do not happen often. It is hard to break std::string or std::vector in a way that won't compile with a huge error log. Physical quantities and units libraries are different. Generation of compile-time errors is the main reason to use such a library.

    "},{"location":"getting_started/introduction/#key-features","title":"Key Features","text":"Feature Description Safety - The affine space strong types (quantity and quantity_point)- Compile-time checked conversions of quantities and units- Unique support for many quantities of the same kind- Type-safe equations on scalar, vector, and tensor quantities and their units- Value-preserving conversions Performance - All the compile-time logic implemented as immediate (consteval) functions- As fast or even faster than working with fundamental types- No space size overhead needed to implement high-level abstractions Great User Experience - Optimized for readable compilation errors and great debugging experience- Efficient and composable way to specify a unit of choice- Value-based dimension, unit, and quantity equations Feature Rich - Systems of Quantities- Systems of Units- Scalar, vector, and tensor quantities- The affine space- Different models of the universe (e.g. natural units systems)- Strong dimensionless quantities- Strong angular system- Supports any unit's magnitude (huge, small, floating-point)- Faster-than-lightspeed constants- Highly adjustable text-output formatting Easy to Extend - Each entity can be defined with a single line of code- User can easily extend the systems with custom dimensions, quantities, and units Low Standardization Cost - Small number of predefined entities thanks to their composability- No external dependencies (assuming full C++20 support)- No macros in the user interface (besides portability and standard-compliance issues)- Possibility to be standardized as a freestanding part of the C++ Standard Library"},{"location":"getting_started/look_and_feel/","title":"Look and Feel","text":"

    Here is a small example of operations possible on scalar quantities:

    C++ modulesHeader files
    import mp_units;\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\n// simple numeric operations\nstatic_assert(10 * km / 2 == 5 * km);\n\n// conversions to common units\nstatic_assert(1 * h == 3600 * s);\nstatic_assert(1 * km + 1 * m == 1001 * m);\n\n// derived quantities\nstatic_assert(1 * km / (1 * s) == 1000 * m / s);\nstatic_assert(2 * km / h * (2 * h) == 4 * km);\nstatic_assert(2 * km / (2 * km / h) == 1 * h);\n\nstatic_assert(2 * m * (3 * m) == 6 * m2);\n\nstatic_assert(10 * km / (5 * km) == 2);\n\nstatic_assert(1000 / (1 * s) == 1 * kHz);\n
    #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\n// simple numeric operations\nstatic_assert(10 * km / 2 == 5 * km);\n\n// conversions to common units\nstatic_assert(1 * h == 3600 * s);\nstatic_assert(1 * km + 1 * m == 1001 * m);\n\n// derived quantities\nstatic_assert(1 * km / (1 * s) == 1000 * m / s);\nstatic_assert(2 * km / h * (2 * h) == 4 * km);\nstatic_assert(2 * km / (2 * km / h) == 1 * h);\n\nstatic_assert(2 * m * (3 * m) == 6 * m2);\n\nstatic_assert(10 * km / (5 * km) == 2);\n\nstatic_assert(1000 / (1 * s) == 1 * kHz);\n

    Try it on Compiler Explorer

    This library requires some C++20 features (concepts and constraints, classes as NTTP, ...). Thanks to them, a user gets a powerful but still easy-to-use interface where all unit conversions and dimensional analysis can be performed without sacrificing accuracy. Please see the below example for a quick preview of basic library features:

    C++ modulesHeader files
    #include <format>\n#include <iomanip>\n#include <iostream>\n#include <print>\nimport mp_units;\n\nusing namespace mp_units;\n\nconstexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d,\n                                                QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::international::unit_symbols;\n\n  constexpr quantity v1 = 110 * km / h;\n  constexpr quantity v2 = 70 * mph;\n  constexpr quantity v3 = avg_speed(220. * isq::distance[km], 2 * h);\n  constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * h);\n  constexpr quantity v5 = v3.in(m / s);\n  constexpr quantity v6 = value_cast<m / s>(v4);\n  constexpr quantity v7 = value_cast<int>(v6);\n\n  std::cout << v1 << '\\n';                                        // 110 km/h\n  std::cout << std::setw(10) << std::setfill('*') << v2 << '\\n';  // ***70 mi/h\n  std::cout << std::format(\"{:*^10}\\n\", v3);                      // *110 km/h*\n  std::println(\"{:%N in %U of %D}\", v4);                          // 70 in mi/h of LT\u207b\u00b9\n  std::println(\"{::N[.2f]}\", v5);                                 // 30.56 m/s\n  std::println(\"{::N[.2f]U[dn]}\", v6);                            // 31.29 m\u22c5s\u207b\u00b9\n  std::println(\"{:%N}\", v7);                                      // 31\n}\n
    #include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <format>\n#include <iomanip>\n#include <iostream>\n#include <print>\n\nusing namespace mp_units;\n\nconstexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d,\n                                                QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::international::unit_symbols;\n\n  constexpr quantity v1 = 110 * km / h;\n  constexpr quantity v2 = 70 * mph;\n  constexpr quantity v3 = avg_speed(220. * isq::distance[km], 2 * h);\n  constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * h);\n  constexpr quantity v5 = v3.in(m / s);\n  constexpr quantity v6 = value_cast<m / s>(v4);\n  constexpr quantity v7 = value_cast<int>(v6);\n\n  std::cout << v1 << '\\n';                                        // 110 km/h\n  std::cout << std::setw(10) << std::setfill('*') << v2 << '\\n';  // ***70 mi/h\n  std::cout << std::format(\"{:*^10}\\n\", v3);                      // *110 km/h*\n  std::println(\"{:%N in %U of %D}\", v4);                          // 70 in mi/h of LT\u207b\u00b9\n  std::println(\"{::N[.2f]}\", v5);                                 // 30.56 m/s\n  std::println(\"{::N[.2f]U[dn]}\", v6);                            // 31.29 m\u22c5s\u207b\u00b9\n  std::println(\"{:%N}\", v7);                                      // 31\n}\n

    Try it on Compiler Explorer

    Note

    More code examples can be found in the Examples chapter.

    "},{"location":"getting_started/project_structure/","title":"Project structure","text":"

    This chapter provides a high level overview of the project to make it easier to navigate, build, and use.

    "},{"location":"getting_started/project_structure/#cmake-projects-and-dependencies","title":"CMake projects and dependencies","text":"

    The GitHub repository contains three independent CMake-based projects:

    • ./src

      • header-only project containing whole mp-units library
      • ./src/CMakeLists.txt file is intended as an entry point for library users
      • in case this library becomes part of the C++ standard, it will have no external dependencies but until then, it depends on the following:

        • gsl-lite or ms-gsl to verify runtime contracts (if contract checking is enabled),
        • {fmt} to provide text formatting of quantities (if std::format is not supported yet on a specific compiler).
    • .

      • project used as an entry point for library development and CI/CD
      • it wraps ./src project together with usage examples and tests
      • additionally to the dependencies of ./src project, it uses:

        • Catch2 library as a unit tests framework,
        • linear algebra library based on proposal P1385 used in some examples and tests.
    • ./test_package

      • CMake library installation and Conan package verification.

    Important: Library users should not use the top-level CMake file

    Top level CMakeLists.txt file should only be used by mp-units developers and contributors as an entry point for the project's development. We want to ensure that everyone will build ALL the code correctly before pushing a commit. Having such options would allow unintended issues to leak to PRs and CI.

    This is why our projects have two entry points:

    • ./CMakeLists.txt is to be used by projects developers to build ALL the project code with really restrictive compilation flags,
    • ./src/CMakeLists.txt contains only a pure library definition and should be used by the customers that prefer to use CMake's add_subdirectory() to handle the dependencies.

    To learn more about the rationale, please check our FAQ.

    "},{"location":"getting_started/project_structure/#modules","title":"Modules","text":"

    The mp-units library provides the following C++ modules:

    flowchart TD\n    mp_units --- mp_units.systems --- mp_units.core
    C++ Module CMake Target Contents mp_units.core mp-units::core Core library framework and systems-independent utilities mp_units.systems mp-units::systems All the systems of quantities and units mp_units mp-units::mp-units Core + Systems

    Note

    C++ modules are provided within the package only when:

    • cxx_modules Conan option is set to True,
    • MP_UNITS_BUILD_CXX_MODULES CMake option is set to ON.
    "},{"location":"getting_started/project_structure/#header-files","title":"Header files","text":"

    All of the project's header files can be found in the mp-units/... subdirectory.

    "},{"location":"getting_started/project_structure/#core-library","title":"Core library","text":"
    • mp-units/framework.h contains the entire library's framework definitions,
    • mp-units/concepts.h exposes only the library's concepts for generic code needs,
    • mp-units/format.h provides text formatting support,
    • mp-units/ostream.h enables streaming of the library's objects to the text output,
    • mp-units/math.h provides overloads of common math functions for quantities,
    • mp-units/random.h provides C++ pseudo-random number generators for quantities,
    • mp-units/compat_macros.h provides macros for wide compatibility.
    More details

    More detailed header files can be found in subfolders which typically should not be included by the end users:

    • mp-units/framework/... provides all the public interfaces of the framework,
    • mp-units/bits/... provides private implementation details only (no public definitions),
    • mp-units/ext/... contains external dependencies that at some point in the future should be replaced with C++ standard library facilities.
    "},{"location":"getting_started/project_structure/#systems-and-associated-utilities","title":"Systems and associated utilities","text":"

    The systems definitions can be found in the mp-units/systems/... subdirectory:

    "},{"location":"getting_started/project_structure/#systems-of-quantities","title":"Systems of quantities","text":"
    • mp-units/systems/isq.h provides International System of Quantities (ISQ) definitions,
    • mp-units/systems/isq_angle.h provides a modification of the ISQ based on the proposals to make an angle a base quantity in the ISQ,
    Tip: Improving compile times

    mp-units/systems/isq.h might be expensive to compile in every translation unit. There are some smaller, domain targeted files available for explicit inclusion in the mp-units/systems/isq/... subdirectory.

    "},{"location":"getting_started/project_structure/#systems-of-units","title":"Systems of units","text":"
    • mp-units/systems/si.h provides International System of Units (SI) definitions and associated math functions,
    • mp-units/systems/iec.h provides units and prefixes defined by IEC (e.g., in the series of IEC 80000 standards),
    • mp-units/systems/angular.h provides strong angular units and associated math functions,
    • mp-units/systems/international.h provides international yard and pound units,
    • mp-units/systems/imperial.h includes international.h and extends it with imperial units,
    • mp-units/systems/usc.h includes international.h and extends it with United States customary system of units,
    • mp-units/systems/cgs.h provides centimetre-gram-second system of units,
    • mp-units/systems/iau.h provides astronomical system of units,
    • mp-units/systems/hep.h provides units used in high-energy physics,
    • mp-units/systems/typographic.h provides units used in typography or typesetting,
    • mp-units/systems/natural.h provides an example implementation of natural units.
    Tip: Improving compile times

    mp-units/systems/si.h might be expensive to compile in every translation unit. There are some smaller files available for explicit inclusion in the mp-units/systems/si/... subdirectory.

    mp-units/systems/si/unit_symbols.h is the most expensive to include.

    "},{"location":"getting_started/quick_start/","title":"Quick Start","text":"

    This chapter provides a quick introduction to get you started with mp-units. Much more details can be found in our User's Guide.

    "},{"location":"getting_started/quick_start/#quantities","title":"Quantities","text":"

    A quantity is a concrete amount of a unit representing a quantity type of a specified dimension with a specific representation. It is represented in the library with a quantity class template.

    The SI Brochure says:

    SI Brochure

    The value of the quantity is the product of the number and the unit. The space between the number and the unit is regarded as a multiplication sign (just as a space between units implies multiplication).

    Following the above, the value of a quantity in the mp-units library is created by multiplying a number with a predefined unit:

    C++ modulesHeader files
    import mp_units;\n\nusing namespace mp_units;\n\nquantity q = 42 * si::metre / si::second;\n
    #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\n\nquantity q = 42 * si::metre / si::second;\n

    Info

    In case someone doesn't like the multiply syntax or there is an ambiguity between operator* provided by this and other libraries, there are two other ways to create a quantity:

    1. delta construction helper:

      C++ modulesHeader files
      import mp_units;\n\nusing namespace mp_units;\n\nquantity q = delta<si::metre / si::second>(42);\n
      #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\n\nquantity q = delta<si::metre / si::second>(42);\n
    2. A two-parameter constructor:

      C++ modulesHeader files
      import mp_units;\n\nusing namespace mp_units;\n\nquantity q{42, si::metre / si::second};\n
      #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\n\nquantity q{42, si::metre / si::second};\n

    The above creates an instance of quantity<derived_unit<si::metre, per<si::second>>{}, int>. The same can be obtained using optional unit symbols:

    C++ modulesHeader files
    import mp_units;\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nquantity q = 42 * m / s;\n
    #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nquantity q = 42 * m / s;\n

    Important

    Unit symbols introduce a lot of short identifiers into the current scope, which may cause naming collisions with unrelated but already existing identifiers in the code base. This is why unit symbols are opt-in and typically should be imported only in the context where they are being used (e.g., function scope).

    A user has several options here to choose from depending on the required scenario and possible naming conflicts:

    using-directiveusing-declarationcustom short identifierunit names

    Explicitly \"import\" all of the symbols of a specific system of units from a dedicated unit_symbols namespace with a using-directive:

    using namespace mp_units;\n\nvoid foo(double speed_m_s)\n{\n  // imports all the SI symbols at once\n  using namespace si::unit_symbols;\n  quantity speed = speed_m_s * m / s;\n  // ...\n}\n

    Note

    This solution is perfect for small and isolated scopes but can cause surprising issues when used in larger scopes or when used for the entire program namespace.

    There are 29 named units in SI, and each of them has many prefixed variations (e.g., ng, kcd, ...). It is pretty easy to introduce a name collision with those.

    Selectively bring only the required and not-conflicting symbols with using-declarations:

    using namespace mp_units;\n\nvoid foo(double N)\n{\n  // 'N' function parameter would collide with the SI symbol for Newton, so we only bring what we need\n  using si::unit_symbols::m;\n  using si::unit_symbols::s;\n  quantity speed = N * m / s;\n  // ...\n}\n

    Specify a custom not conflicting unit identifier for a unit:

    using namespace mp_units;\n\nvoid foo(double speed_m_s)\n{\n  // names of some local variables are conflicting with the symbols we want to use\n  auto m = ...;\n  auto s = ...;\n\n  constexpr Unit auto mps = si::metre / si::second;\n  quantity speed = speed_m_s * mps;\n}\n

    Full unit names are straightforward to use and often provide the most readable code:

    using namespace mp_units;\n\nvoid foo(double m, double s)\n{\n  quantity speed = m * si::metre / (s * si::second);\n  // ...\n}\n

    Quantities of the same kind can be added, subtracted, and compared to each other:

    C++ modulesHeader files
    import mp_units;\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nstatic_assert(1 * km + 50 * m == 1050 * m);\n
    #include <mp-units/systems/si.h>\n\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n\nstatic_assert(1 * km + 50 * m == 1050 * m);\n

    Various quantities can be multiplied or divided by each other:

    static_assert(140 * km / (2 * h) == 70 * km / h);\n

    Note

    In case you wonder why this library does not use UDLs to create quantities, please check our FAQ.

    "},{"location":"getting_started/quick_start/#quantity-points","title":"Quantity points","text":"

    The quantity point specifies an absolute quantity with respect to an origin. If no origin is provided explicitly, an implicit one will be provided by the library.

    Together with quantities, they model The Affine Space.

    Quantity points should be used in all places where adding two values is meaningless (e.g., temperature points, timestamps, altitudes, readouts from the car's odometer, etc.).

    The set of operations that can be done on quantity points is limited compared to quantities. This introduces an additional type-safety.

    C++ modulesHeader files
    #include <print>\nimport mp_units;\n\nint main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::usc::unit_symbols;\n\n  quantity_point temp = point<deg_C>(20.);\n  std::println(\"Temperature: {} ({})\",\n               temp.quantity_from_zero(),\n               temp.in(deg_F).quantity_from_zero());\n}\n
    #include <mp-units/format.h>\n#include <mp-units/systems/si.h>\n#include <mp-units/systems/usc.h>\n#include <print>\n\nint main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::usc::unit_symbols;\n\n  quantity_point temp = point<deg_C>(20.);\n  std::println(\"Temperature: {} ({})\",\n               temp.quantity_from_zero(),\n               temp.in(deg_F).quantity_from_zero());\n}\n

    The above outputs:

    Temperature: 20 \u2103 (68 \u2109)\n

    Info

    Check The Affine Space chapter to learn more about quantity points.

    "},{"location":"users_guide/terms_and_definitions/","title":"Terms and Definitions","text":"

    The mp-units project consistently uses the official metrology vocabulary defined by the ISO and BIPM. You can find essential project-related definitions in our documentation's \"Glossary\" chapter. Even more, terms are provided in the official metrology vocabulary of the ISO and BIPM.

    Tip

    Please familiarize yourself with terms from \"Glossary\" to better understand the documentation and improve domain-related communication and discussions.

    "},{"location":"users_guide/examples/avg_speed/","title":"avg_speed","text":"

    Try it on Compiler Explorer

    Let's continue the previous example. This time, our purpose will not be to showcase as many library features as possible, but we will scope on different interfaces one can provide with the mp-units. We will also describe some advantages and disadvantages of presented solutions.

    First, we either import a module or include all the necessary header files and import all the identifiers from the mp_units namespace:

    avg_speed.cpp
    #ifdef MP_UNITS_IMPORT_STD\nimport std;\n#else\n#include <exception>\n#include <iostream>\n#endif\n#ifdef MP_UNITS_MODULES\nimport mp_units;\n#else\n#include <mp-units/ostream.h>\n#include <mp-units/systems/cgs.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#endif\n\nnamespace {\n\nusing namespace mp_units;\n

    Next, we define two functions calculating average speed based on quantities of fixed units and integral and floating-point representation types, respectively, and a third function that we introduced in the previous example:

    avg_speed.cpp
    constexpr quantity<si::metre / si::second, int> fixed_int_si_avg_speed(quantity<si::metre, int> d,\n                                                                       quantity<si::second, int> t)\n{\n  return d / t;\n}\n\nconstexpr quantity<si::metre / si::second> fixed_double_si_avg_speed(quantity<si::metre> d, quantity<si::second> t)\n{\n  return d / t;\n}\n\nconstexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d, QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n

    We also added a simple utility to print our results:

    avg_speed.cpp
    template<QuantityOf<isq::length> D, QuantityOf<isq::time> T, QuantityOf<isq::speed> V>\nvoid print_result(D distance, T duration, V speed)\n{\n  const auto result_in_kmph = speed.force_in(si::kilo<si::metre> / non_si::hour);\n  std::cout << \"Average speed of a car that makes \" << distance << \" in \" << duration << \" is \" << result_in_kmph\n            << \".\\n\";\n}\n

    Now, let's analyze how those three utility functions behave with different sets of arguments. First, we are going to use quantities of SI units and integral representation:

    avg_speed.cpp
    void example()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  // SI (int)\n  {\n    constexpr auto distance = 220 * km;\n    constexpr auto duration = 2 * h;\n\n    std::cout << \"SI units with 'int' as representation\\n\";\n\n    print_result(distance, duration, fixed_int_si_avg_speed(distance, duration));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n

    The above provides the following output:

    SI units with 'int' as representation\nAverage speed of a car that makes 220 km in 2 h is 108 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\n

    Please note that in the first two cases, we must convert length from km to m and time from h to s. The converted values are used to calculate speed in m/s which is then again converted to the one in km/h. Those conversions not only impact the application's runtime performance but may also affect the precision of the final result. Such truncation can be easily observed in the first case where we deal with integral representation types (the resulting speed is 108 km/h).

    The second scenario is really similar to the previous one, but this time, function arguments have floating-point representation types:

    avg_speed.cpp
      // SI (double)\n  {\n    constexpr auto distance = 220. * km;\n    constexpr auto duration = 2. * h;\n\n    std::cout << \"\\nSI units with 'double' as representation\\n\";\n\n    // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed\n    print_result(distance, duration, fixed_int_si_avg_speed(value_cast<int>(distance), value_cast<int>(duration)));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n

    Conversion from floating-point to integral representation types is considered value-truncating and that is why now, in the first case, we need an explicit call to value_cast<int>.

    In the text output, we can observe that, again, the resulting value gets truncated during conversions in the first cast:

    SI units with 'double' as representation\nAverage speed of a car that makes 220 km in 2 h is 108 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\n

    Next, let's do the same for integral and floating-point representations, but this time using international mile:

    avg_speed.cpp
      // International mile (int)\n  {\n    using namespace mp_units::international::unit_symbols;\n\n    constexpr auto distance = 140 * mi;\n    constexpr auto duration = 2 * h;\n\n    std::cout << \"\\nInternational mile with 'int' as representation\\n\";\n\n    // it is not possible to make a lossless conversion of miles to meters on an integral type\n    // (explicit cast needed)\n    print_result(distance, duration, fixed_int_si_avg_speed(distance.force_in(m), duration));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n\n  // International mile (double)\n  {\n    using namespace mp_units::international::unit_symbols;\n\n    constexpr auto distance = 140. * mi;\n    constexpr auto duration = 2. * h;\n\n    std::cout << \"\\nInternational mile with 'double' as representation\\n\";\n\n    // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed\n    // also it is not possible to make a lossless conversion of miles to meters on an integral type\n    // (explicit cast needed)\n    print_result(distance, duration, fixed_int_si_avg_speed(value_cast<m, int>(distance), value_cast<int>(duration)));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n

    One important difference here is the fact that as it is not possible to make a lossless conversion of miles to meters on a quantity using an integral representation type, so this time, we need a value_cast<m, int> to force it.

    If we check the text output of the above, we will see the following:

    International mile with 'int' as representation\nAverage speed of a car that makes 140 mi in 2 h is 111 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112.654 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112 km/h.\n\nInternational mile with 'double' as representation\nAverage speed of a car that makes 140 mi in 2 h is 111 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112.654 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112.654 km/h.\n

    Please note how the first and third results get truncated using integral representation types.

    In the end, we repeat the scenario for CGS units:

    avg_speed.cpp
      // CGS (int)\n  {\n    constexpr auto distance = 22'000'000 * cgs::centimetre;\n    constexpr auto duration = 7200 * cgs::second;\n\n    std::cout << \"\\nCGS units with 'int' as representation\\n\";\n\n    // it is not possible to make a lossless conversion of centimeters to meters on an integral type\n    // (explicit cast needed)\n    print_result(distance, duration, fixed_int_si_avg_speed(distance.force_in(m), duration));\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n\n  // CGS (double)\n  {\n    constexpr auto distance = 22'000'000. * cgs::centimetre;\n    constexpr auto duration = 7200. * cgs::second;\n\n    std::cout << \"\\nCGS units with 'double' as representation\\n\";\n\n    // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed\n    // it is not possible to make a lossless conversion of centimeters to meters on an integral type\n    // (explicit cast needed)\n    print_result(distance, duration, fixed_int_si_avg_speed(value_cast<m, int>(distance), value_cast<int>(duration)));\n\n    print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\n    print_result(distance, duration, avg_speed(distance, duration));\n  }\n}\n\n}  // namespace\n

    Again, we observe value_cast being used in the same places and consistent truncation errors in the text output:

    CGS units with 'int' as representation\nAverage speed of a car that makes 22000000 cm in 7200 s is 108 km/h.\nAverage speed of a car that makes 22000000 cm in 7200 s is 110 km/h.\nAverage speed of a car that makes 22000000 cm in 7200 s is 109 km/h.\n\nCGS units with 'double' as representation\nAverage speed of a car that makes 2.2e+07 cm in 7200 s is 108 km/h.\nAverage speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h.\nAverage speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h.\n

    The example file ends with a simple main() function:

    avg_speed.cpp
    int main()\n{\n  try {\n    example();\n  } catch (const std::exception& ex) {\n    std::cerr << \"Unhandled std exception caught: \" << ex.what() << '\\n';\n  } catch (...) {\n    std::cerr << \"Unhandled unknown exception caught\\n\";\n  }\n}\n
    ","tags":["CGS System","International System"]},{"location":"users_guide/examples/hello_units/","title":"hello_units","text":"

    Try it on Compiler Explorer

    This is a really simple example showcasing the features of the mp-units library.

    First, we either import the mp_units module or include the headers for:

    • an International System of Quantities (ISQ),
    • an International System of units (SI),
    • units derived from the International Yard and Pound,
    • text formatting and stream output support.
    hello_units.cpp
    #include <mp-units/compat_macros.h>\n#include <mp-units/ext/format.h>\n#ifdef MP_UNITS_IMPORT_STD\nimport std;\n#else\n#include <iomanip>\n#include <iostream>\n#endif\n#ifdef MP_UNITS_MODULES\nimport mp_units;\n#else\n#include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#endif\n

    Also, to shorten the definitions, we \"import\" all the symbols from the mp_units namespace.

    hello_units.cpp
    using namespace mp_units;\n

    Next, we define a simple function that calculates the average speed based on the provided arguments of length and time:

    hello_units.cpp
    constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d, QuantityOf<isq::time> auto t)\n{\n  return d / t;\n}\n

    The above function template takes any quantities implicitly convertible to isq::length and isq::time, respectively. Those quantities can use any compatible unit and a representation type. The function returns a result of a straightforward equation and ensures that its quantity type is implicitly convertible to isq::speed.

    Tip

    Besides verifying the type returned from the function, constraining a generic return type is beneficial for users of such a function as it provides more information of what to expect from a function than just using auto.

    hello_units.cpp
    int main()\n{\n  using namespace mp_units::si::unit_symbols;\n  using namespace mp_units::international::unit_symbols;\n

    The above lines explicitly opt into using unit symbols from two systems of units. As this introduces a lot of short identifiers into the current scope, it is not done implicitly while including a header file.

    hello_units.cpp
      constexpr quantity v1 = 110 * km / h;\n  constexpr quantity v2 = 70 * mph;\n  constexpr quantity v3 = avg_speed(220. * km, 2 * h);\n  constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * isq::duration[h]);\n  constexpr quantity v5 = v3.in(m / s);\n  constexpr quantity v6 = value_cast<m / s>(v4);\n  constexpr quantity v7 = value_cast<int>(v6);\n
    • Lines 27 & 28 create a quantity of kind isq::length / isq::time with the numbers and units provided. Such quantities can be converted or assigned to any other quantity with a matching kind.
    • Line 29 calls our function template with quantities of kind isq::length and isq::time and number and units provided.
    • Line 30 explicitly provides quantity types of the quantities passed to a function template. This time, those will not be quantity kinds anymore and will have more restrictive conversion rules.
    • Line 31 changes the unit of a quantity v3 to m / s in a value-preserving way (floating-point representations are considered to be value-preserving).
    • Line 32 does a similar operation, but this time, it would also succeed for value-truncating cases (if that was the case).
    • Line 33 does a value-truncating conversion of changing the underlying representation type from double to int.
    hello_units.cpp
      std::cout << v1 << '\\n';                                           // 110 km/h\n  std::cout << std::setw(10) << std::setfill('*') << v2 << '\\n';     // ***70 mi/h\n  std::cout << MP_UNITS_STD_FMT::format(\"{:*^10}\\n\", v3);            // *110 km/h*\n  std::cout << MP_UNITS_STD_FMT::format(\"{:%N in %U of %D}\\n\", v4);  // 70 in mi/h of LT\u207b\u00b9\n  std::cout << MP_UNITS_STD_FMT::format(\"{::N[.2f]}\\n\", v5);         // 30.56 m/s\n  std::cout << MP_UNITS_STD_FMT::format(\"{::N[.2f]U[dn]}\\n\", v6);    // 31.29 m\u22c5s\u207b\u00b9\n  std::cout << MP_UNITS_STD_FMT::format(\"{:%N}\\n\", v7);              // 31\n}\n

    The above presents various ways to print a quantity. Both stream insertion operations and std::format facilities are supported.

    Tip

    MP_UNITS_STD_FMT is used for compatibility reasons. If a specific compiler does not support std::format or a user prefers to use the {fmt} library, this macro will resolve to fmt namespace. Otherwise, the std namespace will be used.

    More about it can be found in the Wide Compatibility chapter.

    ","tags":["International System","Text Formatting"]},{"location":"users_guide/examples/hw_voltage/","title":"hw_voltage","text":"

    Try it on Compiler Explorer

    As it was stated in The Affine Space chapter, every measurement can (and probably should) be modelled as a quantity_point. This is a perfect example of such a use case.

    This example implements a simplified scenario of measuring voltage read from hardware through a mapped 16-bits register. The actual voltage range of [-10 V, 10 V] is mapped to [-32767, 32767] on hardware. Translation of the value requires not only scaling of the value but also applying of an offset.

    First we include all the dependencies:

    hw_voltage.cpp
    #include <mp-units/compat_macros.h>\n#include <mp-units/ext/format.h>\n#ifdef MP_UNITS_IMPORT_STD\nimport std;\n#else\n#include <iostream>\n#include <optional>\n#endif\n#ifdef MP_UNITS_MODULES\nimport mp_units;\n#else\n#include <mp-units/format.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#endif\n\nusing namespace mp_units;\n

    Next, we specify the real measurement voltage range to be in the range of [-10, 10]:

    hw_voltage.cpp
    // real voltage range\ninline constexpr int min_voltage = -10;\ninline constexpr int max_voltage = 10;\ninline constexpr int voltage_range = max_voltage - min_voltage;\n

    and provide a storage type and special values for the hardware representation:

    hw_voltage.cpp
    // hardware encoding of voltage\nusing voltage_hw_t = std::uint16_t;\ninline constexpr voltage_hw_t voltage_hw_error = std::numeric_limits<voltage_hw_t>::max();\ninline constexpr voltage_hw_t voltage_hw_min = 0;\ninline constexpr voltage_hw_t voltage_hw_max = voltage_hw_error - 1;\ninline constexpr voltage_hw_t voltage_hw_range = voltage_hw_max - voltage_hw_min;\ninline constexpr voltage_hw_t voltage_hw_zero = voltage_hw_range / 2;\n

    Finally, we define a quantity point origin, an offset unit that scales the value and uses this origin to offset the zero of the sale, and a dedicated quantity point alias using those:

    hw_voltage.cpp
    inline constexpr struct hw_voltage_origin final :\n  relative_point_origin<point<si::volt>(min_voltage)> {} hw_voltage_origin;\n\ninline constexpr struct hw_voltage_unit final :\n  named_unit<\"hwV\", mag_ratio<voltage_range, voltage_hw_range> * si::volt, hw_voltage_origin> {} hw_voltage_unit;\n\nusing hw_voltage_quantity_point = quantity_point<hw_voltage_unit, hw_voltage_origin, voltage_hw_t>;\n

    Now, when everything is ready, we can simulate mapping of our hardware register, and provide a helper function that will read the value and construct a quantity point from the obtained copy:

    hw_voltage.cpp
    // mapped HW register\nvolatile voltage_hw_t hw_voltage_value;\n\nstd::optional<hw_voltage_quantity_point> read_hw_voltage()\n{\n  voltage_hw_t local_copy = hw_voltage_value;\n  if (local_copy == voltage_hw_error) return std::nullopt;\n  return point<hw_voltage_unit>(local_copy);\n}\n

    We also provide a simple print helper for our quantity points:

    hw_voltage.cpp
    void print(QuantityPoint auto qp)\n{\n  std::cout << MP_UNITS_STD_FMT::format(\"{:10} ({:5})\", qp.quantity_from_zero(),\n                                        value_cast<double, si::volt>(qp).quantity_from_zero());\n}\n

    In the main function we simulate setting of 3 values by our hardware. Each of them is read and printed in the voltage unit used on the hardware as well as in the standard SI unit:

    hw_voltage.cpp
    int main()\n{\n  // simulate reading of 3 values from the hardware\n  hw_voltage_value = voltage_hw_min;\n  quantity_point qp1 = read_hw_voltage().value();\n  hw_voltage_value = voltage_hw_zero;\n  quantity_point qp2 = read_hw_voltage().value();\n  hw_voltage_value = voltage_hw_max;\n  quantity_point qp3 = read_hw_voltage().value();\n\n  print(qp1);\n  print(qp2);\n  print(qp3);\n}\n

    The above program results with the following text output:

         0 hwV (-10 V)\n 32767 hwV (  0 V)\n 65534 hwV ( 10 V)\n
    ","tags":["Affine Space","Embedded","Text Formatting"]},{"location":"users_guide/examples/si_constants/","title":"si_constants","text":"

    Try it on Compiler Explorer

    The next example presents all the seven defining constants of the SI system. We can observe how Faster-than-lightspeed Constants work in practice.

    si_constants.cpp
    #include <mp-units/compat_macros.h>\n#include <mp-units/ext/format.h>\n#ifdef MP_UNITS_IMPORT_STD\nimport std;\n#else\n#include <iostream>\n#endif\n#ifdef MP_UNITS_MODULES\nimport mp_units;\n#else\n#include <mp-units/format.h>\n#include <mp-units/systems/si.h>\n#endif\n

    As always, we start with the inclusion of all the needed header files. The main part of the example prints all of the SI-defining constants:

    si_constants.cpp
    int main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si;\n  using namespace mp_units::si::unit_symbols;\n\n  std::cout << \"The seven defining constants of the SI and the seven corresponding units they define:\\n\";\n  std::cout << MP_UNITS_STD_FMT::format(\"- hyperfine transition frequency of Cs: {} = {::N[.0]}\\n\",\n                                        1. * si2019::hyperfine_structure_transition_frequency_of_cs,\n                                        (1. * si2019::hyperfine_structure_transition_frequency_of_cs).in(Hz));\n  std::cout << MP_UNITS_STD_FMT::format(\"- speed of light in vacuum:             {} = {::N[.0]}\\n\",\n                                        1. * si2019::speed_of_light_in_vacuum,\n                                        (1. * si2019::speed_of_light_in_vacuum).in(m / s));\n  std::cout << MP_UNITS_STD_FMT::format(\"- Planck constant:                      {} = {::N[.8e]}\\n\",\n                                        1. * si2019::planck_constant, (1. * si2019::planck_constant).in(J * s));\n  std::cout << MP_UNITS_STD_FMT::format(\"- elementary charge:                    {} = {::N[.9e]}\\n\",\n                                        1. * si2019::elementary_charge, (1. * si2019::elementary_charge).in(C));\n  std::cout << MP_UNITS_STD_FMT::format(\"- Boltzmann constant:                   {} = {::N[.6e]}\\n\",\n                                        1. * si2019::boltzmann_constant, (1. * si2019::boltzmann_constant).in(J / K));\n  std::cout << MP_UNITS_STD_FMT::format(\"- Avogadro constant:                    {} = {::N[.8e]}\\n\",\n                                        1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant).in(one / mol));\n  std::cout << MP_UNITS_STD_FMT::format(\"- luminous efficacy:                    {} = {}\\n\",\n                                        1. * si2019::luminous_efficacy, (1. * si2019::luminous_efficacy).in(lm / W));\n}\n

    While analyzing the output of this program (provided below), we can easily notice that a direct printing of the quantity provides just a value 1 with a proper constant symbol. This is the main power of the Faster-than-lightspeed Constants feature. Only after we explicitly convert the unit of a quantity to proper SI units we get an actual numeric value of the constant.

    The seven defining constants of the SI and the seven corresponding units they define:\n- hyperfine transition frequency of Cs: 1 \u0394\u03bd_Cs = 9192631770 Hz\n- speed of light in vacuum:             1 c = 299792458 m/s\n- Planck constant:                      1 h = 6.62607015e-34 J s\n- elementary charge:                    1 e = 1.602176634e-19 C\n- Boltzmann constant:                   1 k = 1.380649e-23 J/K\n- Avogadro constant:                    1 N_A = 6.02214076e+23 1/mol\n- luminous efficacy:                    1 K_cd = 683 lm/W\n
    ","tags":["Physical Constants","Text Formatting"]},{"location":"users_guide/framework_basics/character_of_a_quantity/","title":"Character of a Quantity","text":"

    Warning

    This chapter's features are experimental and subject to change or removal. Please share your feedback if something seems wrong or could be improved.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#scalars-vectors-and-tensors","title":"Scalars, vectors, and tensors","text":"

    ISO 80000-2

    Scalars, vectors and tensors are mathematical objects that can be used to denote certain physical quantities and their values. They are as such independent of the particular choice of a coordinate system, whereas each scalar component of a vector or a tensor and each component vector and component tensor depend on that choice.

    Such distinction is important because each quantity character represents different properties and allows different operations to be done on its quantities.

    For example, imagine a physical units library that allows the creation of a \\(speed\\) quantity from both \\(length / time\\) and \\(length * time\\). It wouldn't be too safe to use such a product, right?

    Now we have to realize that both of the above operations (multiplication and division) are not even mathematically defined for linear algebra types such as vectors or tensors. On the other hand, two vectors can be passed as arguments to dot and cross-product operations. The result of the first one is a scalar. The second one results in a vector that is perpendicular to both vectors passed as arguments. Again, it wouldn't be safe to allow replacing those two operations with each other or expect the same results from both cases. This simply can't work.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#isq-defines-quantities-of-all-characters","title":"ISQ defines quantities of all characters","text":"

    While defining quantities ISO 80000 explicitly mentions when a specific quantity has a vector or tensor character. Here are some examples:

    Quantity Character Quantity Equation \\(duration\\) scalar {base quantity} \\(mass\\) scalar {base quantity} \\(length\\) scalar {base quantity} \\(path\\; length\\) scalar {base quantity} \\(radius\\) scalar {base quantity} \\(position\\; vector\\) vector {base quantity} \\(velocity\\) vector \\(position\\; vector / duration\\) \\(acceleration\\) vector \\(velocity / duration\\) \\(force\\) vector \\(mass * acceleration\\) \\(power\\) scalar \\(force \\cdot velocity\\) \\(moment\\; of\\; force\\) vector \\(position\\; vector \\times force\\) \\(torque\\) scalar \\(moment\\; of\\; force \\cdot \\{unit\\; vector\\}\\) \\(surface\\; tension\\) scalar \\(\\lvert force \\rvert / length\\) \\(angular\\; displacement\\) scalar \\(path\\; length / radius\\) \\(angular\\; velocity\\) vector \\(angular\\; displacement / duration * \\{unit\\; vector\\}\\) \\(momentum\\) vector \\(mass * velocity\\) \\(angular\\; momentum\\) vector \\(position\\; vector \\times momentum\\) \\(moment\\; of\\; inertia\\) tensor \\(angular\\; momentum \\otimes angular\\; velocity\\)

    In the above equations:

    • \\(a * b\\) - regular multiplication where one of the arguments has to be scalar
    • \\(a / b\\) - regular division where the divisor has to be scalar
    • \\(a \\cdot b\\) - dot product of two vectors
    • \\(a \\times b\\) - cross product of two vectors
    • \\(\\lvert a \\rvert\\) - magnitude of a vector
    • \\(\\{unit\\; vector\\}\\) - a special vector with the magnitude of \\(1\\)
    • \\(a \\otimes b\\) - tensor product of two vectors or tensors

    Note

    As of now, all of the C++ physical units libraries on the market besides mp-units do not support the operations mentioned above. They expose only multiplication and division operators, which do not work for linear algebra-based representation types. If a user of those libraries would like to create the quantities provided in the above table properly, this would result in a compile-time error stating that multiplication and division of two linear algebra vectors is impossible.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#characters-dont-apply-to-dimensions-and-units","title":"Characters don't apply to dimensions and units","text":"

    ISO 80000 explicitly states that dimensions are orthogonal to quantity characters:

    ISO 80000-1:2009

    In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character.

    Also, it explicitly states that:

    ISO 80000-2

    All units are scalars.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#defining-vector-and-tensor-quantities","title":"Defining vector and tensor quantities","text":"

    To specify that a specific quantity has a vector or tensor character a value of quantity_character enumeration can be appended to the quantity_spec describing such a quantity type:

    C++23C++20Portable
    inline constexpr struct displacement final : quantity_spec<length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final : quantity_spec<displacement> {} position_vector;\n
    inline constexpr struct displacement final : quantity_spec<displacement, length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final : quantity_spec<position_vector, displacement> {} position_vector;\n
    QUANTITY_SPEC(displacement, length, quantity_character::vector);\nQUANTITY_SPEC(position_vector, displacement);\n

    With the above, all the quantities derived from position_vector or displacement will have a correct character determined according to the kind of operations included in the quantity equation defining a derived quantity.

    For example, velocity in the below definition will be defined as a vector quantity (no explicit character override is needed):

    C++23C++20Portable
    inline constexpr struct velocity final : quantity_spec<speed, displacement / duration> {} velocity;\n
    inline constexpr struct velocity final : quantity_spec<velocity, speed, displacement / duration> {} velocity;\n
    QUANTITY_SPEC(velocity, speed, displacement / duration);\n
    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#representation-types-for-vector-and-tensor-quantities","title":"Representation types for vector and tensor quantities","text":"

    As we remember, the quantity class template is defined as follows:

    template<Reference auto R,\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity;\n

    The second template parameter is constrained with a RepresentationOf concept that checks if the provided representation type satisfies the requirements for the character associated with this quantity type.

    Note

    The current version of the C++ Standard Library does not provide any types that could be used as a representation type for vector and tensor quantities. This is why users are on their own here .

    To provide examples and implement unit tests, our library uses the types proposed in the P1385 and available as a Conan package in the Conan Center. However, thanks to the provided customization points, any linear algebra library types can be used as a vector or tensor quantity representation type.

    To enable the usage of a user-defined type as a representation type for vector or tensor quantities, we need to provide a partial specialization of is_vector or is_tensor customization points.

    For example, here is how it can be done for the P1385 types:

    #include <matrix>\n\nusing la_vector = STD_LA::fixed_size_column_vector<double, 3>;\n\ntemplate<>\nconstexpr bool mp_units::is_vector<la_vector> = true;\n

    With the above, we can use la_vector as a representation type for our quantity:

    Quantity auto q = la_vector{1, 2, 3} * isq::velocity[m / s];\n

    In case there is an ambiguity of operator* between mp-units and a linear algebra library, we can either:

    • use two-parameter constructor

      Quantity auto q = quantity{la_vector{1, 2, 3}, isq::velocity[m / s]};\n
    • provide a dedicated overload of operator* that will resolve the ambiguity and wrap the above

      template<Reference R>\nQuantity auto operator*(la_vector rep, R)\n{\n  return quantity{rep, R{}};\n}\n

    Note

    The following does not work:

    Quantity auto q1 = la_vector{1, 2, 3} * m / s;\nQuantity auto q2 = isq::velocity(la_vector{1, 2, 3} * m / s);\nquantity<isq::velocity[m/s]> q3{la_vector{1, 2, 3} * m / s};\n

    In all the cases above, the SI unit m / s has an associated scalar quantity of isq::length / isq::time. la_vector is not a correct representation type for a scalar quantity so the construction fails.

    "},{"location":"users_guide/framework_basics/character_of_a_quantity/#hacking-the-character","title":"Hacking the character","text":"

    Sometimes we want to use a vector quantity, but we don't care about its direction. For example, the standard gravity acceleration constant always points down, so we might not care about this in a particular scenario. In such a case, we may want to \"hack\" the library to allow scalar types to be used as a representation type for scalar quantities.

    For example, we can do the following:

    template<class T>\n  requires mp_units::is_scalar<T>\nconstexpr bool mp_units::is_vector<T> = true;\n

    which says that every type that can be used as a scalar representation is also allowed for vector quantities.

    Doing the above is actually not such a big \"hack\" as the ISO 80000 explicitly allows it:

    ISO 80000-2

    A vector is a tensor of the first order and a scalar is a tensor of order zero.

    Despite it being allowed by ISO 80000, for type-safety reasons, we do not allow such a behavior by default, and a user has to opt into such scenarios explicitly.

    "},{"location":"users_guide/framework_basics/concepts/","title":"Concepts","text":"

    This chapter enumerates all the user-facing concepts in the mp-units library.

    "},{"location":"users_guide/framework_basics/concepts/#Dimension","title":"Dimension<T>","text":"

    Dimension concept matches a dimension of either a base or derived quantity:

    • Base dimensions are explicitly defined by the user by inheriting from the instantiation of a base_dimension class template. It should be instantiated with a unique symbol identifier describing this dimension in a specific system of quantities.
    • Derived dimensions are implicitly created by the library's framework based on the quantity equation provided in the quantity specification.

    All of the above dimensions have to be marked as final.

    "},{"location":"users_guide/framework_basics/concepts/#DimensionOf","title":"DimensionOf<T, V>","text":"

    DimensionOf concept is satisfied when both arguments satisfy a Dimension concept and when they compare equal.

    "},{"location":"users_guide/framework_basics/concepts/#QuantitySpec","title":"QuantitySpec<T>","text":"

    QuantitySpec concept matches all the quantity specifications including:

    • Base quantities defined by a user by inheriting from the quantity_spec class template instantiated with a base dimension argument.
    • Derived named quantities defined by a user by inheriting from the quantity_spec class template instantiated with a result of a quantity equation passed as an argument.
    • Other named quantities forming a hierarchy of quantities of the same kind defined by a user by inheriting from the quantity_spec class template instantiated with another \"parent\" quantity specification passed as an argument.
    • Quantity kinds describing a family of mutually comparable quantities.
    • Intermediate derived quantity specifications being a result of a quantity equations on other specifications.

    All of the above quantity specifications have to be marked as final.

    "},{"location":"users_guide/framework_basics/concepts/#QuantitySpecOf","title":"QuantitySpecOf<T, V>","text":"

    QuantitySpecOf concept is satisfied when both arguments satisfy a QuantitySpec concept and when T is implicitly convertible to V.

    More details

    Additionally:

    • T should not be a nested quantity specification of V
    • either T is quantity kind or V should not be a nested quantity specification of T

    Those additional conditions are required to make the following work:

    static_assert(ReferenceOf<si::radian, isq::angular_measure>);\nstatic_assert(!ReferenceOf<si::radian, dimensionless>);\nstatic_assert(!ReferenceOf<isq::angular_measure[si::radian], dimensionless>);\nstatic_assert(ReferenceOf<one, isq::angular_measure>);\nstatic_assert(!ReferenceOf<dimensionless[one], isq::angular_measure>);\n
    "},{"location":"users_guide/framework_basics/concepts/#Unit","title":"Unit<T>","text":"

    Unit concept matches all the units in the library including:

    • Base units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier describing this unit in a specific system of units.
    • Named scaled units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier and a product of multiplying another unit with some magnitude.
    • Prefixed units defined by a user by inheriting from the prefixed_unit class template instantiated with a prefix symbol, a magnitude, and a unit to be prefixed.
    • Derived named units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier and a result of unit equation passed as an argument.
    • Derived unnamed units being a result of a unit equations on other units.

    All of the above units have to be marked as final.

    Note

    In the mp-units library, physical constants are also implemented as units.

    "},{"location":"users_guide/framework_basics/concepts/#AssociatedUnit","title":"AssociatedUnit<T>","text":"

    AssociatedUnit concept describes a unit with an associated quantity and is satisfied by:

    • All units derived from a named_unit class template instantiated with a unique symbol identifier and a QuantitySpec of a quantity kind.
    • All units being a result of unit equations on other associated units.
    Examples

    All units in the SI have associated quantities. For example, si::second is specified to measure isq::time.

    Natural units typically do not have an associated quantity. For example, if we assume c = 1, a natural::second unit can be used to measure both time and length. In such case, speed would have a unit of one.

    "},{"location":"users_guide/framework_basics/concepts/#PrefixableUnit","title":"PrefixableUnit<T>","text":"

    PrefixableUnit concept is satisfied by all units derived from a named_unit class template. Such units can be passed as an argument to a prefixed_unit class template.

    "},{"location":"users_guide/framework_basics/concepts/#UnitOf","title":"UnitOf<T, V>","text":"

    UnitOf concept is satisfied for all units T matching an AssociatedUnit concept with an associated quantity type implicitly convertible to V.

    More details

    Additionally, the kind of V and the kind of quantity type associated with T must be the same, or the quantity type associated with T may not be derived from the kind of V.

    This condition is required to make dimensionless[si::radian] invalid as si::radian should be only used for isq::angular_measure, which is a nested quantity kind within the dimensionless quantities tree.

    "},{"location":"users_guide/framework_basics/concepts/#Reference","title":"Reference<T>","text":"

    Reference concept is satisfied by all quantity reference types. Such types provide all the meta-information required to create a Quantity. A Reference can either be:

    • An AssociatedUnit.
    • The instantiation of a reference class template with a QuantitySpec passed as the first template argument and a Unit passed as the second one.
    "},{"location":"users_guide/framework_basics/concepts/#ReferenceOf","title":"ReferenceOf<T, V>","text":"

    ReferenceOf concept is satisfied by references T which have a quantity specification that satisfies QuantitySpecOf<V> concept. |

    "},{"location":"users_guide/framework_basics/concepts/#Representation","title":"Representation<T>","text":"

    Representation concept constraints a type of a number that stores the value of a quantity.

    "},{"location":"users_guide/framework_basics/concepts/#RepresentationOf","title":"RepresentationOf<T, V>","text":"

    RepresentationOf concept is satisfied:

    • if the type of V satisfies QuantitySpec:

      • by all Representation types when V describes a quantity kind,
      • otherwise, by Representation types that are of a quantity character associated with a provided quantity specification V.
    • if V is of quantity_character type:

      • by Representation types that are of a provided quantity character.

    A user can declare a custom representation type to be of a specific character by providing the specialization with true for one or more of the following variable templates:

    • is_scalar<T>
    • is_complex<T>
    • is_vector<T>
    • is_tensor<T>
    Tip

    If we want to use scalar types to also express vector quantities (e.g., ignoring the \"direction\" of the vector) the following definition can be provided to enable such a behavior:

    template<class T>\n  requires mp_units::is_scalar<T>\nconstexpr bool mp_units::is_vector<T> = true;\n
    "},{"location":"users_guide/framework_basics/concepts/#Quantity","title":"Quantity<T>","text":"

    Quantity concept matches every quantity in the library and is satisfied by all types being or deriving from an instantiation of a quantity class template.

    "},{"location":"users_guide/framework_basics/concepts/#QuantityOf","title":"QuantityOf<T, V>","text":"

    QuantityOf concept is satisfied by all the quantities for which a QuantitySpecOf<V> is true.

    "},{"location":"users_guide/framework_basics/concepts/#PointOrigin","title":"PointOrigin<T>","text":"

    PointOrigin concept matches all quantity point origins in the library. It is satisfied by either:

    • All types derived from an absolute_point_origin class template.
    • All types derived from a relative_point_origin class template.
    "},{"location":"users_guide/framework_basics/concepts/#PointOriginFor","title":"PointOriginFor<T, V>","text":"

    PointOriginFor concept is satisfied by all PointOrigin types that have quantity type implicitly convertible from quantity specification V, which means that V must satisfy QuantitySpecOf<T::quantity_spec>.

    Examples

    si::ice_point can serve as a point origin for points of isq::Celsius_temperature because this quantity type implicitly converts to isq::thermodynamic_temperature.

    However, if we define mean_sea_level in the following way:

    inline constexpr struct mean_sea_level final : absolute_point_origin<isq::altitude> {} mean_sea_level;\n

    then it can't be used as a point origin for points of isq::length or isq::width as none of them is implicitly convertible to isq::altitude:

    • not every length is an altitude,
    • width is not compatible with altitude.
    "},{"location":"users_guide/framework_basics/concepts/#QuantityPoint","title":"QuantityPoint<T>","text":"

    QuantityPoint concept is satisfied by all types being either a specialization or derived from quantity_point class template.

    "},{"location":"users_guide/framework_basics/concepts/#QuantityPointOf","title":"QuantityPointOf<T, V>","text":"

    QuantityPointOf concept is satisfied by all the quantity points T that match the following value V:

    V Condition QuantitySpec The quantity point quantity specification satisfies QuantitySpecOf<V> concept. PointOrigin The point and V have the same absolute point origin."},{"location":"users_guide/framework_basics/concepts/#QuantityLike","title":"QuantityLike<T>","text":"

    QuantityLike concept provides interoperability with other libraries and is satisfied by a type T for which an instantiation of quantity_like_traits type trait yields a valid type that provides:

    • reference static data member that matches the Reference concept,
    • rep type that matches RepresentationOf concept with the character provided in reference,
    • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity type should happen explicitly (if true),
    • explicit_export static data member convertible to bool that specifies that the conversion from a quantity type to T should happen explicitly (if true),
    • to_numerical_value(T) static member function returning a raw value of the quantity,
    • from_numerical_value(rep) static member function returning T.
    Examples

    This is how support for std::chrono::seconds can be provided:

    template<>\nstruct mp_units::quantity_like_traits<std::chrono::seconds> {\n  static constexpr auto reference = si::second;\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = false;\n  using rep = std::chrono::seconds::rep;\n\n  [[nodiscard]] static constexpr rep to_numerical_value(const std::chrono::seconds& d)\n  {\n    return d.count();\n  }\n\n  [[nodiscard]] static constexpr std::chrono::seconds from_numerical_value(const rep& v)\n  {\n    return std::chrono::seconds(v);\n  }\n};\n\nquantity q = 42s;\nstd::chrono::seconds dur = 42 * s;\n
    "},{"location":"users_guide/framework_basics/concepts/#QuantityPointLike","title":"QuantityPointLike<T>","text":"

    QuantityPointLike concept provides interoperability with other libraries and is satisfied by a type T for which an instantiation of quantity_point_like_traits type trait yields a valid type that provides:

    • reference static data member that matches the Reference concept.
    • point_origin static data member that matches the PointOrigin concept.
    • rep type that matches RepresentationOf concept with the character provided in reference.
    • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity_point type should happen explicitly (if true),
    • explicit_export static data member convertible to bool that specifies that the conversion from a quantity_point type to T should happen explicitly (if true),
    • to_numerical_value(T) static member function returning a raw value of the quantity being the offset of the point from the origin,
    • from_numerical_value(rep) static member function returning T.
    Examples

    This is how support for a std::chrono::time_point of std::chrono::seconds can be provided:

    template<typename C>\nstruct mp_units::quantity_point_like_traits<std::chrono::time_point<C, std::chrono::seconds>> {\n  static constexpr auto reference = si::second;\n  static constexpr struct point_origin_ final : absolute_point_origin<isq::time> {} point_origin{};\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = false;\n  using rep = std::chrono::seconds::rep;\n  using T = std::chrono::time_point<C, std::chrono::seconds>;\n\n  [[nodiscard]] static constexpr rep to_numerical_value(const T& tp)\n  {\n    return tp.time_since_epoch().count();\n  }\n\n  [[nodiscard]] static constexpr T from_numerical_value(const rep& v)\n  {\n    return T(std::chrono::seconds(v));\n  }\n};\n\nquantity_point qp = time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());\nstd::chrono::sys_seconds q = qp + 42 * s;\n
    "},{"location":"users_guide/framework_basics/design_overview/","title":"Design Overview","text":"

    The most important entities in the mp-units library are:

    • quantity,
    • quantity point,
    • unit,
    • dimension,
    • quantity specification
    • quantity reference,
    • quantity representation.

    The graph provided below presents how those and a few other entities depend on each other:

    flowchart TD\n    Unit --- Reference\n    Dimension --- QuantitySpec[\"Quantity specification\"]\n    quantity_character[\"Quantity character\"] --- QuantitySpec\n    QuantitySpec --- Reference[\"Quantity reference\"]\n    Reference --- Quantity\n    quantity_character -.- Representation\n    Representation --- Quantity\n    Quantity --- QuantityPoint[\"Quantity point\"]\n    PointOrigin[\"Point origin\"] --- QuantityPoint\n\n    click Dimension \"#dimension\"\n    click quantity_character \"#quantity-character\"\n    click QuantitySpec \"#quantity-specification\"\n    click Unit \"#unit\"\n    click Reference \"#quantity-reference\"\n    click Representation \"#quantity-representation\"\n    click Quantity \"#quantity\"\n    click PointOrigin \"#point-origin\"\n    click QuantityPoint \"#quantity-point\"
    "},{"location":"users_guide/framework_basics/design_overview/#dimension","title":"Dimension","text":"

    Dimension specifies the dependence of a quantity on the base quantities of a particular system of quantities. It is represented as a product of powers of factors corresponding to the base quantities, omitting any numerical factor.

    In the mp-units library, we use the terms:

    • base dimension to refer to the dimension of a base quantity,
    • derived dimension to refer to the dimension of a derived quantity.

    For example:

    • length (\\(\\mathsf{L}\\)), mass (\\(\\mathsf{M}\\)), time (\\(\\mathsf{T}\\)), electric current (\\(\\mathsf{I}\\)), thermodynamic temperature (\\(\\mathsf{\u0398}\\)), amount of substance (\\(\\mathsf{N}\\)), and luminous intensity (\\(\\mathsf{J}\\)) are the base dimensions of the ISQ.
    • A derived dimension of force in the ISQ is denoted by \\(\\textsf{dim }F = \\mathsf{LMT}^{\u20132}\\).
    • The implementation of IEC 80000 in this library provides iec::dim_traffic_intensity base dimension to extend ISQ with strong information technology quantities.

    Base dimensions can be defined by the user in the following way:

    inline constexpr struct dim_length final : base_dimension<\"L\"> {} dim_length;\ninline constexpr struct dim_time final : base_dimension<\"T\"> {} dim_time;\n

    Derived dimensions are implicitly created by the library's framework based on the quantity equation provided in the quantity specification:

    C++23C++20Portable
    inline constexpr struct length final : quantity_spec<dim_length> {} length;\ninline constexpr struct time final : quantity_spec<dim_time> {} time;\ninline constexpr struct speed final : quantity_spec<length / time> {} speed;\n\nstatic_assert(speed.dimension == dim_length / dim_time);\n
    inline constexpr struct length final : quantity_spec<length, dim_length> {} length;\ninline constexpr struct time final : quantity_spec<time, dim_time> {} time;\ninline constexpr struct speed final : quantity_spec<speed, length / time> {} speed;\n\nstatic_assert(speed.dimension == dim_length / dim_time);\n
    QUANTITY_SPEC(length, dim_length);\nQUANTITY_SPEC(time, dim_time);\nQUANTITY_SPEC(speed, length / time);\n\nstatic_assert(speed.dimension == dim_length / dim_time);\n

    Important

    Users should not explicitly define any derived dimensions. Those should always be implicitly created by the framework.

    The multiplication/division on quantity specifications also multiplies/divides their dimensions:

    static_assert((length / time).dimension == dim_length / dim_time);\n

    The dimension equation of isq::dim_length / isq::dim_time results in the derived_dimension<isq::dim_length, per<isq::dim_time>> type.

    "},{"location":"users_guide/framework_basics/design_overview/#quantity-character","title":"Quantity character","text":"

    ISO 80000 explicitly states that quantities (even of the same kind) may have different characters:

    • scalar,
    • vector,
    • tensor.

    The quantity character in the mp-units library is implemented with the quantity_character enumeration:

    enum class quantity_character { scalar, vector, tensor };\n

    Info

    You can read more on quantity characters in the \"Character of a Quantity\" chapter.

    "},{"location":"users_guide/framework_basics/design_overview/#quantity-specification","title":"Quantity specification","text":"

    Dimension is not enough to describe a quantity. This is why the ISO 80000 provides hundreds of named quantity types. It turns out that there are many more quantity types in the ISQ than the named units in the SI.

    This is why the mp-units library introduces a quantity specification entity that stores:

    • dimension,
    • quantity type/name,
    • quantity character,
    • the quantity equation being the recipe to create this quantity (only for derived quantities that specify such a recipe).

    Note

    We know that it might be sometimes confusing to talk about quantities, quantity types/names, and quantity specifications. However, it might be important to notice here that even the ISO 80000 admits that:

    It is customary to use the same term, \"quantity\", to refer to both general quantities, such as length, mass, etc., and their instances, such as given lengths, given masses, etc. Accordingly, we are used to saying both that length is a quantity and that a given length is a quantity by maintaining the specification \u2013 \"general quantity, \\(Q\\)\" or \"individual quantity, \\(Q_\\textsf{a}\\)\" \u2013 implicit and exploiting the linguistic context to remove the ambiguity.

    In the mp-units library, we have a:

    • quantity - implemented as a quantity class template,
    • quantity specification - implemented with a quantity_spec class template that among others identifies a specific quantity type/name.

    For example:

    • isq::length, isq::mass, isq::time, isq::electric_current, isq::thermodynamic_temperature, isq::amount_of_substance, and isq::luminous_intensity are the specifications of base quantities in the ISQ.
    • isq::width, isq::height, isq::radius, and isq::position_vector are only a few of many quantities of a kind length specified in the ISQ.
    • isq::area, isq::speed, isq::moment_of_force are only a few of many derived quantities provided in the ISQ.

    Quantity specification can be defined by the user in one of the following ways:

    C++23C++20Portable
    inline constexpr struct length final : quantity_spec<dim_length> {} length;\ninline constexpr struct height final : quantity_spec<length> {} height;\ninline constexpr struct speed final : quantity_spec<length / time> {} speed;\n
    inline constexpr struct length final : quantity_spec<length, dim_length> {} length;\ninline constexpr struct height final : quantity_spec<height, length> {} height;\ninline constexpr struct speed final : quantity_spec<speed, length / time> {} speed;\n
    QUANTITY_SPEC(length, dim_length);\nQUANTITY_SPEC(height, length);\nQUANTITY_SPEC(speed, length / time);\n

    The quantity equation of isq::length / isq::time results in the derived_quantity_spec<isq::length, per<isq::time>> type.

    "},{"location":"users_guide/framework_basics/design_overview/#unit","title":"Unit","text":"

    A unit is a concrete amount of a quantity that allows us to measure the values of quantities of the same kind and represent the result as a number being the ratio of the two quantities.

    For example:

    • si::second, si::metre, si::kilogram, si::ampere, si::kelvin, si::mole, and si::candela are the base units of the SI.
    • si::kilo<si::metre> is a prefixed unit of length.
    • si::radian, si::newton, and si::watt are examples of named derived units within the SI.
    • non_si::minute is an example of a scaled unit of time.
    • si::si2019::speed_of_light_in_vacuum is a physical constant standardized by the SI in 2019.

    Note

    In the mp-units library, physical constants are also implemented as units.

    A unit can be defined by the user in one of the following ways:

    template<PrefixableUnit U> struct kilo_ : prefixed_unit<\"k\", mag_power<10, 3>, U{}> {};\ntemplate<PrefixableUnit auto U> constexpr kilo_<decltype(U)> kilo;\n\ninline constexpr struct second final : named_unit<\"s\", kind_of<isq::time>> {} second;\ninline constexpr struct minute final : named_unit<\"min\", mag<60> * second> {} minute;\ninline constexpr struct gram   final : named_unit<\"g\", kind_of<isq::mass>> {} gram;\ninline constexpr auto kilogram = kilo<gram>;\ninline constexpr struct newton final : named_unit<\"N\", kilogram * metre / square(second)> {} newton;\n\ninline constexpr struct speed_of_light_in_vacuum final : named_unit<\"c\", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum;\n

    The unit equation of si::metre / si::second results in the derived_unit<si::metre, per<si::second>> type.

    "},{"location":"users_guide/framework_basics/design_overview/#quantity-reference","title":"Quantity reference","text":"

    ISO defines a quantity as:

    Quote

    property of a phenomenon, body, or substance, where the property has a magnitude that can be expressed as a number and a reference

    After that, it says:

    Quote

    A reference can be a measurement unit, a measurement procedure, a reference material, or a combination of such.

    In the mp-units library, a quantity reference provides all the domain-specific metadata for the quantity besides its numerical value:

    • all the data stored in the quantity specification,
    • unit.

    Together with the value of a representation type, it forms a quantity.

    In the library, we have two different ways to provide a reference:

    • every unit with the associated quantity kind is a valid reference,
    • providing a unit to an indexing operator of a quantity specification explicitly instantiates a reference class template with this quantity spec and a unit passed as arguments.

    Note

    All the units of the SI have associated quantity kinds and may serve as a reference.

    For example:

    • si::metre is defined in the SI as a unit of isq::length and thus can be used as a reference to instantiate a quantity of length (e.g., 42 * m).
    • The expression isq::height[m] results with reference<isq::height, si::metre>, which can be used to instantiate a quantity of isq::height with a unit of si::metre (e.g., 42 * isq::height[m]).
    "},{"location":"users_guide/framework_basics/design_overview/#quantity-representation","title":"Quantity representation","text":"

    Quantity representation defines the type used to store the numerical value of a quantity. Such a type should be of a specific quantity character provided in the quantity specification.

    Note

    By default, all floating-point and integral (besides bool) types are treated as scalars.

    "},{"location":"users_guide/framework_basics/design_overview/#quantity","title":"Quantity","text":"

    ISO defines a quantity as:

    Quote

    property of a phenomenon, body, or substance, where the property has a magnitude that can be expressed as a number and a reference

    This is why a quantity class template is defined in the library as:

    template<Reference auto R,\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity;\n

    Its value can be easily created by multiplying/dividing the numerical value and a reference.

    For example:

    • All of 42 * m, 42 * si::metre, 42 * isq::height[m], and isq::height(42 * m) create a quantity.
    • A quantity type can also be specified explicitly (e.g., quantity<si::metre, int>, quantity<isq::height[m]>).
    "},{"location":"users_guide/framework_basics/design_overview/#point-origin","title":"Point origin","text":"

    In the affine space theory, the point origin specifies where the \"zero\" of our measurement's scale is.

    In the mp-units library, we have two types of point origins:

    • absolute - defines an absolute \"zero\" for our point,
    • relative - defines an origin that has some \"offset\" relative to an absolute point.

    For example:

    • the absolute point origin can be defined in the following way:
    inline constexpr struct absolute_zero final : absolute_point_origin<isq::thermodynamic_temperature> {} absolute_zero;\n
    • the relative point origin can be defined in the following way:
    inline constexpr struct ice_point final : relative_point_origin<absolute_zero + 273'150 * milli<kelvin>> {} ice_point;\n
    "},{"location":"users_guide/framework_basics/design_overview/#quantity-point","title":"Quantity point","text":"

    Quantity point implements a point in the affine space theory.

    In the mp-units library, the quantity point is implemented as:

    template<Reference auto R,\n         PointOriginFor<get_quantity_spec(R)> auto PO,\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity_point;\n

    Its value can be easily created by adding/subtracting the quantity with a point origin.

    For example:

    • The following specifies a quantity point defined in terms of an ice_point provided in the previous example:
    constexpr auto room_reference_temperature = ice_point + delta<isq::Celsius_temperature[deg_C]>(21);\n
    "},{"location":"users_guide/framework_basics/dimensionless_quantities/","title":"Dimensionless Quantities","text":"

    The quantities we discussed so far always had some specific type and physical dimension. However, this is not always the case. While performing various computations, we sometimes end up with so-called \"dimensionless\" quantities, which ISO defines as quantities of dimension one:

    ISO/IEC Guide 99

    • Quantity for which all the exponents of the factors corresponding to the base quantities in its quantity dimension are zero.
    • The measurement units and values of quantities of dimension one are numbers, but such quantities convey more information than a number.
    • Some quantities of dimension one are defined as the ratios of two quantities of the same kind.
    • Numbers of entities are quantities of dimension one.
    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-two-quantities-of-the-same-kind","title":"Dividing two quantities of the same kind","text":"

    Dividing two quantities of the same kind always results in a quantity of dimension one. However, depending on what type of quantities we divide or what their units are, we may end up with slightly different results.

    Note

    In mp-units, dividing two quantities of the same dimension always results in a quantity with the dimension being dimension_one. This is often different for other physical units libraries, which may return a raw representation type for such cases. A raw value is also always returned from the division of two std::chrono::duration objects.

    To read more about the reasoning for this design decision, please check our FAQ.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-quantities-of-the-same-type","title":"Dividing quantities of the same type","text":"

    First, let's analyze what happens if we divide two quantities of the same type:

    constexpr QuantityOf<dimensionless> auto q = isq::height(200 * m) / isq::height(50 * m);\n

    In such a case, we end up with a dimensionless quantity that has the following properties:

    static_assert(q.quantity_spec == dimensionless);\nstatic_assert(q.dimension == dimension_one);\nstatic_assert(q.unit == one);\n

    In case we would like to print its value, we would see a raw value of 4 in the output with no unit being printed.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-quantities-of-different-types","title":"Dividing quantities of different types","text":"

    Now let's see what happens if we divide quantities of the same dimension and unit but which have different quantity types:

    constexpr QuantityOf<dimensionless> auto q = isq::work(200 * J) / isq::heat(50 * J);\n

    Again we end up with dimension_one and one, but this time:

    static_assert(q.quantity_spec == isq::work / isq::heat);\n

    As shown above, the result is not of a dimensionless type anymore. Instead, we get a quantity type derived from the performed quantity equation. According to the ISQ, work divided by heat is the recipe for the thermodynamic efficiency quantity, thus:

    static_assert(implicitly_convertible(q.quantity_spec, isq::efficiency_thermodynamics));\n

    Note

    The quantity of isq::efficiency_thermodynamics is of a kind dimensionless, so it is implicitly convertible to dimensionless and satisfies the QuantityOf<dimensionless> concept.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-quantities-of-different-units","title":"Dividing quantities of different units","text":"

    Now, let's see what happens when we divide two quantities of the same type but different units:

    constexpr QuantityOf<dimensionless> auto q = isq::height(4 * km) / isq::height(2 * m);\n

    This time, we still get a quantity of the dimensionless type with a dimension_one as its dimension. However, the resulting unit is not one anymore:

    static_assert(q.unit == mag_power<10, 3> * one);\n

    In case we would print the text output of this quantity, we would not see a raw value of 2000, but 2 km/m.

    First, it may look surprising, but this is consistent with dividing quantities of different dimensions. For example, if we divide 4 * km / 2 * s, we do not expect km to be \"expanded\" to m before the division, right? We would expect the result of 2 km/s, which is exactly what we get when we divide quantities of the same kind.

    This is a compelling feature that allows us to express huge or tiny ratios without the need for big and expensive representation types. With this, we can easily define things like a Hubble's constant that uses a unit that is proportional to the ratio of kilometers per megaparsecs, which are both units of length:

    inline constexpr struct hubble_constant final :\n    named_unit<{u8\"H\u2080\", \"H_0\"}, mag_ratio<701, 10> * si::kilo<si::metre> / si::second / si::mega<parsec>> {} hubble_constant;\n
    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#counts-of-things","title":"Counts of things","text":"

    Another important use case for dimensionless quantities is to provide strong types for counts of things. For example:

    • ISO-80000-3 provides a rotation quantity defined as the number of revolutions,
    • IEC-80000-6 provides a number of turns in a winding quantity,
    • IEC-80000-13 provides a Hamming distance quantity defined as the number of digit positions in which the corresponding digits of two words of the same length are different.

    Thanks to assigning strong names to such quantities, later on, they can be explicitly used as arguments in the quantity equations of other quantities deriving from them.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#predefined-units-of-the-dimensionless-quantity","title":"Predefined units of the dimensionless quantity","text":"

    As we observed above, the most common unit for dimensionless quantities is one. It has the ratio of 1 and does not output any textual symbol.

    Important: one is an identity

    A unit one is special in the entire type system of units as it is considered to be an identity operand in the unit symbolic expressions. This means that, for example:

    static_assert(one * one == one);\nstatic_assert(one * si::metre == si::metre);\nstatic_assert(si::metre / si::metre == one);\n

    The same is also true for dimension_one and dimensionless in the domains of dimensions and quantity specifications.

    Besides the unit one, there are a few other scaled units predefined in the library for usage with dimensionless quantities:

    inline constexpr struct percent final : named_unit<\"%\", mag_ratio<1, 100> * one> {} percent;\ninline constexpr struct per_mille final : named_unit<{u8\"\u2030\", \"%o\"}, mag_ratio<1, 1000> * one> {} per_mille;\ninline constexpr struct parts_per_million final : named_unit<\"ppm\", mag_ratio<1, 1'000'000> * one> {} parts_per_million;\ninline constexpr auto ppm = parts_per_million;\n
    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#superpowers-of-the-unit-one","title":"Superpowers of the unit one","text":"

    Quantities of the unit one are the only ones that are:

    • implicitly constructible from the raw value,
    • explicitly convertible to a raw value,
    • comparable to a raw value.
    quantity<one> inc(quantity<one> q) { return q + 1; }\nvoid legacy(double) { /* ... */ }\n\nif (auto q = inc(42); q != 0)\n  legacy(static_cast<int>(q));\n

    This property also expands to usual arithmetic operators.

    Note

    Those rules do not apply to all the dimensionless quantities. It would be unsafe and misleading to allow such operations on units with a magnitude different than 1 (e.g., percent or radian).

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#angular-quantities","title":"Angular quantities","text":"

    Special, often controversial, examples of dimensionless quantities are an angular measure and solid angular measure quantities that are defined in the ISQ to be the result of a division of \\(arc\\; length / radius\\) and \\(area / radius^2\\) respectively. Moreover, ISQ also explicitly states that both can be expressed in the unit one. This means that both angular measure and solid angular measure should be of a kind dimensionless.

    On the other hand, ISQ also specifies that a unit radian can be used for angular measure, and a unit steradian can be used for solid angular measure. Those should not be mixed or used to express other types of dimensionless quantities. This means that both angular measure and solid angular measure should also be quantity kinds by themselves.

    Note

    Many people claim that angle being a dimensionless quantity is a bad idea. There are proposals submitted to make an angle a base quantity and rad to become a base unit. More on this topic can be found in the \"Strong Angular System\" chapter.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#radians-and-degrees-support","title":"Radians and degrees support","text":"

    Thanks to the usage of magnitudes the library provides efficient strong types for all angular types. This means that with the built-in support for magnitudes of \\(\\pi\\) we can provide accurate conversions between radians and degrees. The library also provides common trigonometric functions for angular quantities:

    using namespace mp_units::si::unit_symbols;\nusing mp_units::angular::unit_symbols::rad;\nusing mp_units::angular::unit_symbols::deg;\nusing mp_units::angular::unit_symbols::grad;\n\nquantity speed = 110 * km / h;\nquantity rate_of_climb = -0.63657 * m / s;\nquantity glide_ratio = speed / -rate_of_climb;\nquantity glide_angle = angular::asin(1 / glide_ratio);\n\nstd::println(\"Glide ratio: {::N[.1f]}\", glide_ratio.in(one));\nstd::println(\"Glide angle:\");\nstd::println(\" - {::N[.4f]}\", glide_angle.in(rad));\nstd::println(\" - {::N[.2f]}\", glide_angle.in(deg));\nstd::println(\" - {::N[.2f]}\", glide_angle.in(grad));\n

    The above program prints:

    Glide ratio: 48.0\nGlide angle:\n - 0.0208 rad\n - 1.19\u00b0\n - 1.33\u1d4d\n

    Note

    In the production code the above speed and rate_of_climb quantities should probably be modelled as separate typed quantities of the same kind.

    "},{"location":"users_guide/framework_basics/dimensionless_quantities/#nested-quantity-kinds","title":"Nested quantity kinds","text":"

    Angular quantities are not the only ones with such a \"strange\" behavior. Another but a similar case is a storage capacity quantity specified in IEC-80000-13 that again allows expressing it in both one and bit units.

    Those cases make dimensionless quantities an exceptional tree in the library. This quantity hierarchy contains more than one quantity kind and more than one unit in its tree:

    flowchart TD\n    dimensionless[\"<b>dimensionless</b><br>[one]\"]\n    dimensionless --- rotation[\"<b>rotation</b>\"]\n    dimensionless --- thermodynamic_efficiency[\"<b>thermodynamic_efficiency</b><br><i>(work / heat)</i>\"]\n    dimensionless --- angular_measure[\"<b>angular_measure</b><br><i>(arc_length / radius)</i><br>[rad]\"]\n    angular_measure --- rotational_displacement[\"<b>rotational_displacement</b><br><i>(path_length / radius)</i>\"]\n    angular_measure --- phase_angle[\"<b>phase_angle</b>\"]\n    dimensionless --- solid_angular_measure[\"<b>solid_angular_measure</b><br><i>(area / pow<2>(radius))</i><br>[sr]\"]\n    dimensionless --- drag_factor[\"<b>drag_factor</b><br><i>(drag_force / (mass_density * pow<2>(speed) * area))</i>\"]\n    dimensionless --- storage_capacity[\"<b>storage_capacity</b><br>[bit]\"] --- equivalent_binary_storage_capacity[\"<b>equivalent_binary_storage_capacity</b>\"]\n    dimensionless --- ...

    To provide such support in the library, we provided an is_kind specifier that can be appended to the quantity specification:

    C++23C++20Portable
    inline constexpr struct angular_measure final : quantity_spec<dimensionless, arc_length / radius, is_kind> {} angular_measure;\ninline constexpr struct solid_angular_measure final : quantity_spec<dimensionless, area / pow<2>(radius), is_kind> {} solid_angular_measure;\ninline constexpr struct storage_capacity final : quantity_spec<dimensionless, is_kind> {} storage_capacity;\n
    inline constexpr struct angular_measure final : quantity_spec<angular_measure, dimensionless, arc_length / radius, is_kind> {} angular_measure;\ninline constexpr struct solid_angular_measure final : quantity_spec<solid_angular_measure, dimensionless, area / pow<2>(radius), is_kind> {} solid_angular_measure;\ninline constexpr struct storage_capacity final : quantity_spec<storage_capacity, dimensionless, is_kind> {} storage_capacity;\n
    QUANTITY_SPEC(angular_measure, dimensionless, arc_length / radius, is_kind);\nQUANTITY_SPEC(solid_angular_measure, dimensionless, area / pow<2>(radius), is_kind);\nQUANTITY_SPEC(storage_capacity, dimensionless, is_kind);\n

    With the above, we can constrain radian, steradian, and bit to be allowed for usage with specific quantity kinds only:

    inline constexpr struct radian final : named_unit<\"rad\", metre / metre, kind_of<isq::angular_measure>> {} radian;\ninline constexpr struct steradian final : named_unit<\"sr\", square(metre) / square(metre), kind_of<isq::solid_angular_measure>> {} steradian;\ninline constexpr struct bit final : named_unit<\"bit\", one, kind_of<storage_capacity>> {} bit;\n

    but still allow the usage of one and its scaled versions for such quantities.

    "},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/","title":"Faster-than-lightspeed Constants","text":"

    In most libraries, physical constants are implemented as constant (possibly constexpr) quantity values. Such an approach has some disadvantages, often affecting the run time performance and causing a loss of precision.

    "},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/#simplifying-constants-in-an-equation","title":"Simplifying constants in an equation","text":"

    When dealing with equations involving physical constants, they often occur more than once in an expression. Such a constant may appear both in a numerator and denominator of a quantity equation. As we know from fundamental physics, we can simplify such an expression by striking a constant out of the equation. Supporting such behavior allows a faster runtime performance and often a better precision of the resulting value.

    "},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/#physical-constants-as-units","title":"Physical constants as units","text":"

    The mp-units library allows and encourages the implementation of physical constants as regular units. With that, the constant's value is handled at compile-time, and under favorable circumstances, it can be simplified in the same way as all other repeated units do. If it is not simplified, the value is stored in a type, and the expensive multiplication or division operations can be delayed in time until a user selects a specific unit to represent/print the data.

    Such a feature often also allows using simpler or faster representation types in the equation. For example, instead of always having to multiply a small integral value with a big floating-point constant number, we can just use the integral type all the way. Only in case a constant will not simplify in the equation, and the user will require a specific unit, such a multiplication will be lazily invoked, and the representation type will need to be expanded to facilitate that. With that, addition, subtractions, multiplications, and divisions will always be the fastest - compiled away or done in out-of-order execution.

    To benefit from all of the above, in the mp-units library, SI defining and other constants are implemented as units in the following way:

    namespace si {\n\nnamespace si2019 {\n\ninline constexpr struct speed_of_light_in_vacuum final :\n  named_unit<\"c\", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum;\n\n}  // namespace si2019\n\ninline constexpr struct magnetic_constant final :\n  named_unit<{u8\"\u03bc\u2080\", \"u_0\"}, mag<4> * mag<\u03c0> * mag_power<10, -7> * henry / metre> {} magnetic_constant;\n\n}  // namespace mp_units::si\n
    "},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/#usage-examples","title":"Usage examples","text":"

    With the above definitions, we can calculate vacuum permittivity as:

    constexpr auto permeability_of_vacuum = 1. * si::magnetic_constant;\nconstexpr auto speed_of_light_in_vacuum = 1 * si::si2019::speed_of_light_in_vacuum;\n\nQuantityOf<isq::permittivity_of_vacuum> auto q = 1 / (permeability_of_vacuum * pow<2>(speed_of_light_in_vacuum));\n\nstd::println(\"permittivity of vacuum = {} = {::N[.3e]}\", q, q.in(F / m));\n

    The above first prints the following:

    permittivity of vacuum = 1  \u03bc\u2080\u207b\u00b9 c\u207b\u00b2 = 8.854e-12 F/m\n

    As we can clearly see, all the calculations above were just about multiplying and dividing the number 1 with the rest of the information provided as a compile-time type. Only when a user wants a specific SI unit as a result, the unit ratios are lazily resolved.

    Another similar example can be an equation for total energy:

    QuantityOf<isq::mechanical_energy> auto total_energy(QuantityOf<isq::momentum> auto p,\n                                                     QuantityOf<isq::mass> auto m,\n                                                     QuantityOf<isq::speed> auto c)\n{\n  return isq::mechanical_energy(sqrt(pow<2>(p * c) + pow<2>(m * pow<2>(c))));\n}\n
    constexpr auto GeV = si::giga<si::electronvolt>;\nconstexpr QuantityOf<isq::speed> auto c = 1. * si::si2019::speed_of_light_in_vacuum;\nconstexpr auto c2 = pow<2>(c);\n\nconst auto p1 = isq::momentum(4. * GeV / c);\nconst QuantityOf<isq::mass> auto m1 = 3. * GeV / c2;\nconst auto E = total_energy(p1, m1, c);\n\nstd::cout << \"in `GeV` and `c`:\\n\"\n          << \"p = \" << p1 << \"\\n\"\n          << \"m = \" << m1 << \"\\n\"\n          << \"E = \" << E << \"\\n\";\n\nconst auto p2 = p1.in(GeV / (m / s));\nconst auto m2 = m1.in(GeV / pow<2>(m / s));\nconst auto E2 = total_energy(p2, m2, c).in(GeV);\n\nstd::cout << \"\\nin `GeV`:\\n\"\n          << \"p = \" << p2 << \"\\n\"\n          << \"m = \" << m2 << \"\\n\"\n          << \"E = \" << E2 << \"\\n\";\n\nconst auto p3 = p1.in(kg * m / s);\nconst auto m3 = m1.in(kg);\nconst auto E3 = total_energy(p3, m3, c).in(J);\n\nstd::cout << \"\\nin SI base units:\\n\"\n          << \"p = \" << p3 << \"\\n\"\n          << \"m = \" << m3 << \"\\n\"\n          << \"E = \" << E3 << \"\\n\";\n

    The above prints the following:

    in `GeV` and `c`:\np = 4 GeV/c\nm = 3 GeV/c\u00b2\nE = 5 GeV\n\nin `GeV`:\np = 1.33426e-08 GeV s/m\nm = 3.33795e-17 GeV s\u00b2/m\u00b2\nE = 5 GeV\n\nin SI base units:\np = 2.13771e-18 kg m/s\nm = 5.34799e-27 kg\nE = 8.01088e-10 J\n
    "},{"location":"users_guide/framework_basics/generic_interfaces/","title":"Generic Interfaces","text":"

    Using a concrete unit in the interface often makes a lot of sense. It is especially useful if we store the data internally in the object. In such a case, we have to select a specific unit anyway.

    For example, let's consider a simple storage tank:

    class StorageTank {\n  quantity<horizontal_area[m2]> base_;\n  quantity<isq::height[m]> height_;\n  quantity<isq::mass_density[kg / m3]> density_ = air_density;\npublic:\n  constexpr StorageTank(const quantity<horizontal_area[m2]>& base, const quantity<isq::height[m]>& height) :\n      base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n

    As the quantities provided in the function's interface are then stored in the class, there is probably no sense in using generic interfaces here.

    "},{"location":"users_guide/framework_basics/generic_interfaces/#the-issues-with-unit-specific-interfaces","title":"The issues with unit-specific interfaces","text":"

    However, in many cases, using a specific unit in the interface is counterproductive. Let's consider the following function:

    quantity<km / h> avg_speed(quantity<km> distance, quantity<h> duration)\n{\n  return distance / duration;\n}\n

    Everything seems fine for now. It also works great if we call it with:

    quantity<km / h> s1 = avg_speed(220 * km, 2 * h);\n

    However, if the user starts doing the following:

    quantity<mi / h> s2 = avg_speed(140 * mi, 2 * h);\nquantity<m / s> s3 = avg_speed(20 * m, 2 * s);\n

    some issues start to be clearly visible:

    1. The arguments must be converted to units mandated by the function's parameters at each call. This involves potentially expensive multiplication/division operations at runtime.
    2. After the function returns the speed in a unit of km/h, another potentially expensive multiplication/division operations must be performed to convert the resulting quantity into a unit being the derived unit of the initial function's arguments.
    3. Besides the obvious runtime cost, some unit conversions may result in a value truncation, which means that the result will not be exactly equal to a direct division of the function's arguments.
    4. We have to use a floating-point representation type (the quantity class template by default uses double as a representation type) which is considered value-preserving. Trying to use an integral type in this scenario will work only for s1, while s2 and s3 will fail to compile. Failing to compile is a good thing here as the library tries to prevent the user from doing a clearly wrong thing. To make the code compile, the user needs to use dedicated value_cast or force_in like this:

      quantity<isq::speed[mi / h]> s2 = avg_speed(value_cast<km>(140 * mi), 2 * h);\nquantity<isq::speed[m / s]> s3 = avg_speed((20 * m).force_in(km), (2 * s).force_in(h));\n

      but the above will obviously provide an incorrect behavior (e.g., division by 0 in the evaluation of s3).

    "},{"location":"users_guide/framework_basics/generic_interfaces/#a-naive-solution","title":"A naive solution","text":"

    A naive solution here would be to implement the function as an unconstrained function template:

    auto avg_speed(auto distance, auto duration)\n{\n  return distance / duration;\n}\n

    Beware, this is not a good solution. The above code is too generic. Such a function template accepts everything:

    • quantities of other types
      • the compiler will not prevent accidental reordering of the function's arguments,
      • quantities of different types can be passed as well,
    • plain double arguments,
    • std::vector and std::lock_guard will be accepted as well (of course, this will fail in the instantiation of a function's body later in the compilation process).

    Note

    The usage of auto instead of a function parameter type is a C++20 feature. It makes such a code a function template where the type of such a parameter will be deduced during the template instantiation process from the argument type passed by the user.

    "},{"location":"users_guide/framework_basics/generic_interfaces/#constraining-function-template-arguments-with-concepts","title":"Constraining function template arguments with concepts","text":"

    Much better generic code can be implemented using basic concepts provided with the library:

    Original template notationThe shorthand notationTerse notation
    template<typename Distance, typename Duration>\n  requires QuantityOf<Distance, isq::length> && QuantityOf<Duration, isq::time>\nauto avg_speed(Distance distance, Duration duration)\n{\n  return isq::speed(distance / duration);\n}\n
    template<QuantityOf<isq::length> Distance, QuantityOf<isq::time> Duration>\nauto avg_speed(Distance distance, Duration duration)\n{\n  return isq::speed(distance / duration);\n}\n
    auto avg_speed(QuantityOf<isq::length> auto distance,\n               QuantityOf<isq::time> auto duration)\n{\n  return isq::speed(distance / duration);\n}\n

    This explicitly states that the arguments passed by the user must not only satisfy a Quantity concept, but also their quantity specification must be implicitly convertible to isq::length and isq::time accordingly. This no longer leaves room for error while still allowing the compiler to generate the most efficient code.

    Tip

    Please note that now it is safe just to use integral types all the way which again improves the runtime performance as the multiplication/division operations are often faster on the integral rather than floating-point types.

    "},{"location":"users_guide/framework_basics/generic_interfaces/#constraining-function-template-return-type","title":"Constraining function template return type","text":"

    The above function template resolves all of the issues described before. However, we can do even better here by additionally constraining the return type:

    QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto distance,\n                                      QuantityOf<isq::time> auto duration)\n{\n  return isq::speed(distance / duration);\n}\n

    Doing so has two important benefits:

    1. It informs the users of our interface about what to expect to be the result of a function invocation. It is superior to just returning auto, which does not provide any hint about the thing being returned there.
    2. Such a concept constrains the type returned from the function. This means that it works as a unit test to verify if our function actually performs what it is supposed to do. If there is an error in quantity equations, we will learn about it right away.
    "},{"location":"users_guide/framework_basics/generic_interfaces/#constraining-a-variable-on-the-stack","title":"Constraining a variable on the stack","text":"

    If we know precisely what the function does in its internals and if we know the exact argument types passed to such a function, we often know the exact type that will be returned from its invocation.

    However, if we care about performance, we should often use the generic interfaces described in this chapter. A side effect is that we sometimes are unsure about the return type. Even if we know it today, it might change a week from now due to some code refactoring.

    In such cases, we can again use auto to denote the type:

    auto s1 = avg_speed(220 * km, 2 * h);\nauto s2 = avg_speed(140 * mi, 2 * h);\nauto s3 = avg_speed(20 * m, 2 * s);\n

    or benefit from CTAD:

    quantity s1 = avg_speed(220 * km, 2 * h);\nquantity s2 = avg_speed(140 * mi, 2 * h);\nquantity s3 = avg_speed(20 * m, 2 * s);\n

    In both cases, it is probably OK to do so as the avg_speed function name explicitly provides the information on what to expect as a result.

    In other scenarios where the returned quantity type is not so obvious, it is again helpful to constrain the type with a concept like so:

    QuantityOf<isq::speed> auto s1 = avg_speed(220 * km, 2 * h);\nQuantityOf<isq::speed> auto s2 = avg_speed(140 * mi, 2 * h);\nQuantityOf<isq::speed> auto s3 = avg_speed(20 * m, 2 * s);\n

    The above explicitly provides additional information about the quantity we are dealing with in the code, and it serves as a unit test checking if the \"thing\" returned from a function is actually what we expected here.

    Note

    The QuantityOf and QuantityPointOf concepts are probably the most useful, but there are a few more to play with. A list of all the concepts can be found in the Basic Concepts chapter.

    "},{"location":"users_guide/framework_basics/interface_introduction/","title":"Interface Introduction","text":""},{"location":"users_guide/framework_basics/interface_introduction/#new-style-of-definitions","title":"New style of definitions","text":"

    The mp-units library decided to use a rather unusual pattern to define entities. Here is how we define metre and second SI base units:

    inline constexpr struct metre final : named_unit<\"m\", kind_of<isq::length>> {} metre;\ninline constexpr struct second final : named_unit<\"s\", kind_of<isq::time>> {} second;\n

    Please note that the above reuses the same identifier for a type and its value. The rationale behind this is that:

    • Users always work with values and never have to spell such a type name.
    • The types appear in the compilation errors and during debugging.

    Important

    To improve compiler errors' readability and make it easier to correlate them with a user's written code, a new idiom in the library is to use the same identifier for a type and its instance.

    Also, to prevent possible issues in compile-time logic, all of the library's entities must be marked final. This prevents the users to derive own strong types from them, which would prevent symbolic expressions simplification of equivalent entities.

    "},{"location":"users_guide/framework_basics/interface_introduction/#strong-types-instead-of-aliases","title":"Strong types instead of aliases","text":"

    Let's look again at the above units definitions. Another important point to notice is that all the types describing entities in the library are short, nicely named identifiers that derive from longer, more verbose class template instantiations. This is really important to improve the user experience while debugging the program or analyzing the compilation error.

    Note

    Such a practice is rare in the industry. Some popular C++ physical units libraries generate enormously long error messages where even only the first line failed to fit on a slide with a tiny font.

    "},{"location":"users_guide/framework_basics/interface_introduction/#entities-composability","title":"Entities composability","text":"

    Many physical units libraries (in C++ or any other programming language) assign strong types to library entities (e.g., derived units). While metre_per_second as a type may not look too scary, consider, for example, units of angular momentum. If we followed this path, its coherent unit would look like kilogram_metre_sq_per_second. Now, consider how many scaled versions of this unit you would predefine in the library to ensure that all users are happy with your choice? How expensive would it be from the implementation point of view? What about potential future standardization efforts?

    This is why in mp-units, we put a strong requirement to make everything as composable as possible. For example, to create a quantity with a unit of speed, one may write:

    quantity<si::metre / si::second> q;\n

    In case we use such a unit often and would prefer to have a handy helper for it, we can always do something like this:

    constexpr auto metre_per_second = si::metre / si::second;\nquantity<metre_per_second> q;\n

    or choose any shorter identifier of our choice.

    Coming back to the angular momentum case, thanks to the composability of units, a user can create such a quantity in the following way:

    using namespace mp_units::si::unit_symbols;\nauto q = la_vector{1, 2, 3} * isq::angular_momentum[kg * m2 / s];\n

    It is a much better solution. It is terse and easy to understand. Please also notice how easy it is to obtain any scaled version of such a unit (e.g., mg * square(mm) / min) without having to introduce hundreds of types to predefine them.

    "},{"location":"users_guide/framework_basics/interface_introduction/#value-based-equations","title":"Value-based equations","text":"

    The mp-units library is based on C++20, significantly improving user experience. One of such improvements is the usage of value-based equations.

    As we have learned above, the entities are being used as values in the code, and they compose. Moreover, derived entities can be defined in the library using such value-based equations. This is a huge improvement compared to what we can find in other physical units libraries or what we have to deal with when we want to write some equations for std::ratio.

    For example, below are a few definitions of the SI derived units showing the power of C++20 extensions to Non-Type Template Parameters, which allow us to directly pass a result of the value-based unit equation to a class template definition:

    inline constexpr struct newton final : named_unit<\"N\", kilogram * metre / square(second)> {} newton;\ninline constexpr struct pascal final : named_unit<\"Pa\", newton / square(metre)> {} pascal;\ninline constexpr struct joule  final : named_unit<\"J\", newton * metre> {} joule;\n
    "},{"location":"users_guide/framework_basics/interface_introduction/#symbolic-expressions","title":"Symbolic expressions","text":"

    The previous chapter provided a rationale for not having predefined types for derived entities. In many libraries, such an approach results in long and unreadable compilation errors, as framework-generated types are typically far from being easy to read and understand.

    The mp-units library greatly improves the user experience by extensively using symbolic expressions. Such expressions are used consistently throughout the entire library to describe the results of:

    • dimension equation - the result is put into the derived_dimension<> class template
    • quantity equation - the result is put into the derived_quantity_spec<> class template
    • unit equation - the result is put into the derived_unit<> class template

    For example, if we take the above-defined base units and put the results of their division into the quantity class template like this:

    quantity<metre / second> q;\n

    we will observe the following type in the debugger

    (gdb) ptype q\ntype = class mp_units::quantity<mp_units::derived_unit<metre, mp_units::per<second>>(), double> [with Rep = double] {\n

    The same type identifier will be visible in the compilation error (in case it happens).

    Important

    Expressions templates are extensively used throughout the library to improve the readability of the resulting types.

    "},{"location":"users_guide/framework_basics/interface_introduction/#identities","title":"Identities","text":"

    As mentioned above, equations can be performed on dimensions, quantities, and units. Each such domain must introduce an identity object that can be used in the resulting expressions. Here is the list of identities used in the library:

    Domain Concept Identity Dimension dimension_one QuantitySpec dimensionless Unit one

    In the equations, a user can explicitly refer to an identity object. For example:

    constexpr auto my_unit = one / second;\n

    Note

    Another way to achieve the same result is to call an inverse() function:

    constexpr auto my_unit = inverse(second);\n

    Both cases will result in the same symbolic expression being generated and put into the wrapper class template.

    "},{"location":"users_guide/framework_basics/interface_introduction/#supported-operations-and-their-results","title":"Supported operations and their results","text":"

    There are only a few operations that one can do on such entities, and the result of each of them has its unique representation in the library:

    Operation Resulting template expression arguments A * B A, B B * A A, B A * A power<A, 2> {identity} * A A A * {identity} A A / B A, per<B> A / A {identity} A / {identity} A {identity} / A {identity}, per<A> pow<2>(A) power<A, 2> pow<2>({identity}) {identity} sqrt(A) or pow<1, 2>(A) power<A, 1, 2> sqrt({identity}) or pow<1, 2>({identity}) {identity}"},{"location":"users_guide/framework_basics/interface_introduction/#simplifying-the-resulting-symbolic-expressions","title":"Simplifying the resulting symbolic expressions","text":"

    To limit the length and improve the readability of generated types, there are many rules to simplify the resulting symbolic expression.

    1. Ordering

      The resulting comma-separated arguments of multiplication are always sorted according to a specific predicate. This is why:

      static_assert(A * B == B * A);\nstatic_assert(std::is_same_v<decltype(A * B), decltype(B * A)>);\n

      This is probably the most important of all the steps, as it allows comparing types and enables the rest of the simplification rules.

    2. Aggregation

      In case two of the same identifiers are found next to each other on the argument list they will be aggregated in one entry:

      Before After A, A power<A, 2> A, power<A, 2> power<A, 3> power<A, 1, 2>, power<A, 2> power<A, 5, 2> power<A, 1, 2>, power<A, 1, 2> A
    3. Simplification

      In case two of the same identifiers are found in the numerator and denominator argument lists; they are being simplified into one entry:

      Before After A, per<A> {identity} power<A, 2>, per<A> A power<A, 3>, per<A> power<A, 2> A, per<power<A, 2>> {identity}, per<A>

      It is important to notice here that only the elements with exactly the same type are being simplified. This means that, for example, m/m results in one, but km/m will not be simplified. The resulting derived unit will preserve both symbols and their relative magnitude. This allows us to properly print symbols of some units or constants that require such behavior. For example, the Hubble constant is expressed in km\u22c5s\u207b\u00b9\u22c5Mpc\u207b\u00b9, where both km and Mpc are units of length.

      Also, to prevent possible issues in compile-time logic, all of the library's entities must be marked final. This prevents the users to derive own strong types from them, which would prevent symbolic expression simplification of equivalent entities.

    4. Repacking

      In case an expression uses two results of other operations, the components of its arguments are repacked into one resulting type and simplified there.

      For example, assuming:

      constexpr auto X = A / B;\n

      then:

      Operation Resulting template expression arguments X * B A X * A power<A, 2>, per<B> X * X power<A, 2>, per<power<B, 2>> X / X {identity} X / A {identity}, per<B> X / B A, per<power<B, 2>>
    "},{"location":"users_guide/framework_basics/interface_introduction/#example","title":"Example","text":"

    Thanks to all of the features described above, a user may write the code like this one:

    using namespace mp_units::si::unit_symbols;\nquantity speed = 60. * isq::speed[km / h];\nquantity duration = 8 * s;\nquantity acceleration = speed / duration;\nstd::cout << \"acceleration: \" << acceleration << \" (\" << acceleration.in(m / s2) << \")\\n\";\n

    The acceleration quantity, being the result of the above code, has the following type (after stripping the mp_units namespace for brevity):

    quantity<reference<derived_quantity_spec<isq::speed, per<isq::time>>{}, derived_unit<si::kilo_<si::metre{}>, per<non_si::hour, si::second>>{}>{}, int>\n

    and the text output presents:

    acceleration: 7.5 km h\u207b\u00b9 s\u207b\u00b9 (2.08333 m/s\u00b2)\n
    "},{"location":"users_guide/framework_basics/obtaining_metadata/","title":"Obtaining Metadata","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#quantity-spec","title":"quantity spec","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#unit","title":"unit","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#reference","title":"reference","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#quantity","title":"quantity","text":""},{"location":"users_guide/framework_basics/quantity_arithmetics/","title":"Quantity Arithmetics","text":""},{"location":"users_guide/framework_basics/quantity_arithmetics/#quantity-is-a-numeric-wrapper","title":"quantity is a numeric wrapper","text":"

    If we think about it, the quantity class template is just a \"smart\" numeric wrapper. It exposes properly constrained set of arithmetic operations on one or two operands.

    Important: quantity propagates the underlying interface

    Every single arithmetic operator is exposed by the quantity class template only if the underlying representation type provides it as well, and when its implementation has proper semantics (e.g., returns a reasonable type).

    For example, in the following code, -a will compile only if MyInt exposes such an operation as well:

    quantity a = MyInt{42} * m;\nquantity b = -a;\n

    Assuming that:

    • q is our quantity,
    • qi is a quantity implicitly convertible to q,
    • qk is a quantity of the same kind as q,
    • q1 is a quantity of dimension_one with the unit one,
    • qq is any other quantity,
    • number is a value of a type \"compatible\" with q's representation type,

    here is the list of all the supported operators:

    • unary:
      • +q
      • -q
      • ++q
      • q++
      • --q
      • q--
    • compound assignment:
      • q += qi
      • q -= qi
      • q %= qi
      • q *= number
      • q *= q1
      • q /= number
      • q /= q1
    • binary:
      • q + qk
      • q - qk
      • q % qk
      • q * qq
      • q * number
      • number * q
      • q / qq
      • q / number
      • number / q
    • ordering and comparison:
      • q == qk
      • q <=> qk

    As we can see, there are plenty of operations one can do on a value of a quantity type. As most of them are obvious, in the following chapters, we will discuss only the most important or non-trivial aspects of quantity arithmetics.

    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#addition-and-subtraction","title":"Addition and subtraction","text":"

    Quantities can easily be added or subtracted from each other:

    static_assert(1 * m + 1 * m == 2 * m);\nstatic_assert(2 * m - 1 * m == 1 * m);\nstatic_assert(isq::height(1 * m) + isq::height(1 * m) == isq::height(2 * m));\nstatic_assert(isq::height(2 * m) - isq::height(1 * m) == isq::height(1 * m));\n

    The above uses the same types for LHS, RHS, and the result, but in general, we can add, subtract, or compare the values of any quantity type as long as both quantities are of the same kind. The result of such an operation will be the common type of the arguments:

    static_assert(1 * km + 1.5 * m == 1001.5 * m);\nstatic_assert(isq::height(1 * m) + isq::width(1 * m) == isq::length(2 * m));\nstatic_assert(isq::height(2 * m) - isq::distance(0.5 * m) == 1.5 * m);\nstatic_assert(isq::radius(1 * m) - 0.5 * m == isq::radius(0.5 * m));\n

    Note

    Please note that for the compound assignment operators, we always need to end up with the left-hand-side argument type:

    static_assert((1 * m += 1 * km) == 1001 * m);\nstatic_assert((isq::length(1 * m) += isq::height(1 * m)) == isq::length(1 * m));\nstatic_assert((isq::height(1.5 * m) -= 1 * m) == isq::height(0.5 * m));\n

    If we will break typical library's convertibility rules, the following code will not compile:

    quantity q1 = 1 * m -= 0.5 * m;                         // Compile-time error(1)\nquantity q2 = 1 * km += 1 * m;                          // Compile-time error(2)\nquantity q3 = isq::height(1 * m) += isq::length(1 * m); // Compile-time error(3)\n
    1. Conversion of the floating-point to integral representation type is considered narrowing.
    2. Conversion of quantity with integral representation type from a unit of a higher resolution to the one with a lower resolution is considered narrowing.
    3. Conversion from a more generic quantity type to a more specific one is considered unsafe.
    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#multiplication-and-division","title":"Multiplication and division","text":"

    Multiplying or dividing a quantity by a number does not change its quantity type or unit. However, its representation type may change. For example:

    static_assert(isq::height(3 * m) * 0.5 == isq::height(1.5 * m));\n

    Note

    Unless we use a compound assignment operator, in which case we always have to result with the type of the left-hand-side argument. This, together with the fact that this library tries to prevent truncation of a quantity value means, that the following does not compile:

    quantity q = isq::height(3 * m) *= 0.5; // Compile-time error\n

    However, suppose we multiply or divide quantities of the same or different types or we divide a raw number by a quantity. In that case, we most probably will end up in a quantity of yet another type:

    static_assert(120 * km / (2 * h) == 60 * km / h);\nstatic_assert(isq::width(2 * m) * isq::length(2 * m) == isq::area(4 * m2));\nstatic_assert(50 / isq::time(1 * s) == isq::frequency(50 * Hz));\n

    Note

    An exception from the above rule happens when one of the arguments is a dimensionless quantity. If we multiply or divide by such a quantity, the quantity type will not change. If such a quantity has a unit one, also the unit of a quantity will not change:

    static_assert(120 * m / (2 * one) == 60 * m);\n

    An interesting special case happens when we divide the same quantity kinds or multiply a quantity by its inverted type. In such a case, we end up with a dimensionless quantity.

    static_assert(isq::height(4 * m) / isq::width(2 * m) == 2 * one); // (1)!\nstatic_assert(5 * h / (120 * min) == 0 * one);  // (2)!\nstatic_assert(5. * h / (120 * min) == 2.5 * one);\n
    1. The resulting quantity type of the LHS is isq::height / isq::width, which is a quantity of the dimensionless kind.
    2. The resulting quantity of the LHS is 0 * dimensionless[h / min]. To be consistent with the division of different quantity types, we do not convert quantity values to a common unit before the division.

    Important: Beware of integral division

    The physical units library can't do any runtime branching logic for the division operator. All logic must be done at compile-time when the actual values are unknown, and the quantity types can't change at runtime.

    If we expect 120 * km / (2 * h) to return 60 km / h, we have to agree with the fact that 5 * km / (24 * h) returns 0 km/h. We can't do a range check at runtime to dynamically adjust scales and types based on the values of provided function arguments.

    This is why we often prefer floating-point representation types when dealing with units. Some popular physical units libraries even forbid integer division at all.

    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#modulo","title":"Modulo","text":"

    Now that we know how addition, subtraction, multiplication, and division work, it is time to discuss modulo. What would we expect to be returned from the following quantity equation?

    auto q = 5 * h % (120 * min);\n

    Most of us would probably expect to see 1 h or 60 min as a result. And this is where the problems start.

    C++ language defines its / and % operators with the quotient-remainder theorem:

    q = a / b;\nr = a % b;\nq * b + r == a;\n

    The important property of the modulo operation is that it only works for integral representation types (it is undefined what modulo for floating-point types means). However, as we saw in the previous chapter, integral types are tricky because they often truncate the value.

    From the quotient-remainder theorem, the result of modulo operation is r = a - q * b. Let's see what we get from such a quantity equation on integral representation types:

    const quantity a = 5 * h;\nconst quantity b = 120 * min;\nconst quantity q = a / b;\nconst quantity r = a - q * b;\n\nstd::cout << \"reminder: \" << r << \"\\n\";\n

    The above code outputs:

    reminder: 5 h\n

    And now, a tough question needs an answer. Do we really want modulo operation on physical units to be consistent with the quotient-remainder theorem and return 5 h for 5 * h % (120 * min)?

    This is exactly why we decided not to follow this hugely surprising path in the mp-units library. The selected approach was also consistent with the feedback from the C++ experts. For example, this is what Richard Smith said about this issue:

    Richard Smith

    I think the quotient-remainder property is a less important motivation here than other factors -- the constraints on % and / are quite different, so they lack the inherent connection they have for integers. In particular, I would expect that A / B works for all quantities A and B, whereas A % B is only meaningful when A and B have the same dimension. It seems like a nice-to-have for the property to apply in the case where both / and % are defined, but internal consistency of / across all cases seems much more important to me.

    I would expect 61 min % 1 h to be 1 min, and 1 h % 59 min to also be 1 min, so my intuition tells me that the result type of A % B, where A and B have the same dimension, should have the smaller unit of A and B (and if the smaller one doesn't divide the larger one, we should either use the gcd / std::common_type of the units of A and B or perhaps just produce an error). I think any other behavior for % is hard to defend.

    On the other hand, for division it seems to me that the choice of unit should probably not affect the result, and so if we want that 5 mm / 120 min = 0 mm/min, then 5 h / 120 min == 0 hc (where hc is a dimensionless \"hexaconta\", or 60x, unit). I don't like the idea of taking SI base units into account; that seems arbitrary and like it would do the wrong thing as often as it does the right thing, especially when the units have a multiplier that is very large or small. We could special-case the situation of a dimensionless quantity, but that could lead to problematic overflow pretty easily: a calculation such as 10 s * 5 GHz * 2 uW would overflow an int if it produces a dimensionless quantity for 10 s * 5 GHz, but it could equally produce 50 G * 2 uW = 100 kW without any overflow, and presumably would if the terms were merely reordered.

    If people want to use integer-valued quantities, I think it's fundamental that you need to know what the units of the result of an operation will be, and take that into account in how you express computations; the simplest rule for heterogeneous operators like * or / seems to be that the units of the result are determined by applying the operator to the units of the operands -- and for homogeneous operators like + or %, it seems like the only reasonable option is that you get the std::common_type of the units of the operands.

    To summarize, the modulo operation on physical units has more in common with addition and division operators than with the quotient-remainder theorem. To avoid surprising results, the operation uses a common unit to do the calculation and provide its result:

    static_assert(5 * h / (120 * min) == 0 * one);\nstatic_assert(5 * h % (120 * min) == 60 * min);\nstatic_assert(61 * min % (1 * h) == 1 * min);\nstatic_assert(1 * h % (59 * min) == 1 * min);\n
    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#comparison-against-zero","title":"Comparison against zero","text":"

    In our code, we often want to compare the value of a quantity against zero. For example, we do it every time we want to ensure that we deal with a non-zero or positive value.

    We could implement such checks in the following way:

    if (q1 / q2 != 0 * m / s)\n  // ...\n

    The above would work (assuming we are dealing with the quantity of speed) but could be suboptimal if the result of q1 / q2 is not expressed in m / s. To eliminate the need for conversion, we need to write:

    if (auto q = q1 / q2; q != q.zero())\n  // ...\n

    but that is a bit inconvenient, and inexperienced users could be unaware of this technique and its reasons.

    For the above reasons, the library provides dedicated interfaces to compare against zero that follow the naming convention of named comparison functions in the C++ Standard Library. The mp-units/compare.h header file exposes the following functions:

    • is_eq_zero
    • is_neq_zero
    • is_lt_zero
    • is_gt_zero
    • is_lteq_zero
    • is_gteq_zero

    Thanks to them, to save typing and not pay for unneeded conversions, our check could be implemented as follows:

    if (is_neq_zero(q1 / q2))\n  // ...\n

    Tip

    Those functions will work with any type T that exposes zero() member function returning something comparable to T. Thanks to that, we can use them not only with quantities but also with std::chrono::duration or any other type that exposes such an interface.

    "},{"location":"users_guide/framework_basics/quantity_arithmetics/#other-maths","title":"Other maths","text":"

    This chapter scopes only on the quantity type's operators. However, there are many named math functions taking quantities as arguments. Those can be found in the mp-units/math.h header file. Among others, we can find there the following:

    • pow(), sqrt(), cbrt(),
    • exp(),
    • abs(),
    • epsilon(),
    • fma(), fmod(), remainder(),
    • isfinite(), isinf(), isnan(),
    • floor(), ceil(), round(),
    • inverse(),
    • hypot(),
    • sin(), cos(), tan(),
    • asin(), acos(), atan(), atan2().

    In the library, we can also find mp-units/random.h header file with all the pseudo-random number generators working on quantity types.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/","title":"Simple and Typed Quantities","text":"

    ISO defines a quantity as:

    Quote

    property of a phenomenon, body, or substance, where the property has a magnitude that can be expressed as a number and a reference

    After that, it says:

    Quote

    A reference can be a measurement unit, a measurement procedure, a reference material, or a combination of such.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#quantity-class-template","title":"quantity class template","text":"

    In the mp-units library, a quantity is represented with the following class template:

    template<Reference auto R,\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity;\n

    The concept Reference is satisfied by a type that provides all the domain-specific metadata describing a quantity (besides the representation type and its value). Such a type can be either:

    • a unit with an associated quantity type (e.g., si::metre, m / s),
    • a reference type explicitly specifying the quantity type and its unit.

    Important

    All units in the SI system have an associated quantity type.

    A reference type is implicitly created as a result of the following expression:

    constexpr auto ref = isq::length[m];\n

    The above example results in the following type reference<isq::length(), si::metre()> being instantiated.

    As we have two alternative options that satisfy the Reference concept in the mp-units library, we also have two modes of dealing with quantities.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#simple-quantities","title":"Simple quantities","text":"

    The simple mode might be preferred by many developers. It is all about units. Quantities using this mode have shorter type identifiers, resulting in easier-to-understand error messages and better debugging experience.

    Here is a simple example showing how to deal with such quantities:

    C++ modulesHeader files
    #include <print>\nimport mp_units;\n\nusing namespace mp_units;\n\nconstexpr quantity<si::metre / si::second> avg_speed(quantity<si::metre> dist,\n                                                     quantity<si::second> time)\n{\n  return dist / time;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  const quantity distance = 110 * km;\n  const quantity duration = 2 * h;\n  const quantity speed = avg_speed(distance, duration);\n\n  std::println(\"A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})\",\n               distance, duration, speed, speed.in(km / h));\n}\n
    #include <mp-units/format.h>\n#include <mp-units/systems/si.h>\n#include <print>\n\nusing namespace mp_units;\n\nconstexpr quantity<si::metre / si::second> avg_speed(quantity<si::metre> dist,\n                                                     quantity<si::second> time)\n{\n  return dist / time;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  const quantity distance = 110 * km;\n  const quantity duration = 2 * h;\n  const quantity speed = avg_speed(distance, duration);\n\n  std::println(\"A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})\",\n               distance, duration, speed, speed.in(km / h));\n}\n

    The code above prints:

    A car driving 110 km in 2 h has an average speed of 15.28 m/s (55 km/h)\n

    Try it on Compiler Explorer

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#user-provided-unit-wrappers","title":"User-provided unit wrappers","text":"

    Sometimes it might be awkward to type some derived units:

    quantity speed = 60 * km / h;\n

    In case such a unit is used a lot in the project, a user can easily provide a nicely named wrapper for it with:

    constexpr auto kmph = km / h;\nquantity speed = 60 * kmph;\n
    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#easy-to-understand-compilation-error-messages","title":"Easy-to-understand compilation error messages","text":"

    In case a user makes an error in a quantity equation and the result of the calculation will not match the function return type, the compiler will detect such an issue at compile-time.

    For example, in case we will make the following error:

    constexpr quantity<si::metre / si::second> avg_speed(quantity<si::metre> dist,\n                                                     quantity<si::second> time)\n{\n  return dist * time;  // (1)!\n}\n
    1. Quantities multiplied (instead of divided) by accident.

    the following compilation error message will be provided:

    error: no viable conversion from returned value of type\n       'quantity<mp_units::derived_unit<mp_units::si::metre, mp_units::si::second>{{{}}}, [...]>'\n       to function return type\n       'quantity<mp_units::derived_unit<mp_units::si::metre, mp_units::per<mp_units::si::second>>{{{}}}, [...]>'\n   10 |   return dist * time;\n      |          ^~~~~~~~~~~\n
    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#typed-quantities","title":"Typed quantities","text":"

    Simple mode is all about and just about units. In case we care about a specific quantity type, typed quantities should be preferred. With this mode, for example, we can specify if we deal with width, height, or radius and ensure we will not assign one to another by accident.

    The previous example can be re-typed using typed quantities in the following way:

    C++ modulesHeader files
    #include <print>\nimport mp_units;\n\nusing namespace mp_units;\n\nconstexpr quantity<isq::speed[si::metre / si::second]> avg_speed(quantity<isq::length[si::metre]> dist,\n                                                                 quantity<isq::time[si::second]> time)\n{\n  return dist / time;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  const quantity distance = isq::distance(110 * km);\n  const quantity duration = isq::time(2 * h);\n  const quantity speed = avg_speed(distance, duration);\n\n  std::println(\"A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})\",\n               distance, duration, speed, speed.in(km / h));\n}\n
    #include <mp-units/format.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <print>\n\nusing namespace mp_units;\n\nconstexpr quantity<isq::speed[si::metre / si::second]> avg_speed(quantity<isq::length[si::metre]> dist,\n                                                                 quantity<isq::time[si::second]> time)\n{\n  return dist / time;\n}\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n\n  const quantity distance = isq::distance(110 * km);\n  const quantity duration = isq::time(2 * h);\n  const quantity speed = avg_speed(distance, duration);\n\n  std::println(\"A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})\",\n               distance, duration, speed, speed.in(km / h));\n}\n
    A car driving 110 km in 2 h has an average speed of 15.28 m/s (55 km/h)\n

    Try it on Compiler Explorer

    In case we will accidentally make the same calculation error as before, this time, we will get a bit longer error message, this time also containing information about the quantity type:

    error: no viable conversion from returned value of type\n       'quantity<reference<get_quantity_spec(metre{}) * struct time{{{}}}, metre{} * second{{}}>{}, [...]>'\n       to function return type\n       'quantity<reference<speed{}, derived_unit<metre, per<second>>{}>{}, [...]>'\n   12 |   return dist * time;\n      |          ^~~~~~~~~~~\n

    As we can see above, the compilation error is longer but still relatively easy to understand.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#additional-type-safety-with-typed-quantities","title":"Additional type safety with typed quantities","text":"

    Based on the previous example, it might seem that typed quantities are not that useful, more to type and provide harder-to-understand error messages. It might be true in some cases, but there are scenarios where they offer additional level of safety.

    Let's see another example:

    C++ modulesHeader files SimpleTyped
    #include <numbers>\nimport mp_units;\n\nusing namespace mp_units;\n\nclass StorageTank {\n  quantity<square(si::metre)> base_;\n  quantity<si::metre> height_;\npublic:\n  constexpr StorageTank(const quantity<square(si::metre)>& base,\n                        const quantity<si::metre>& height) :\n    base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n\nclass CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<si::metre>& radius,\n                                   const quantity<si::metre>& height) :\n    StorageTank(std::numbers::pi * pow<2>(radius), height)\n  {\n  }\n};\n\nclass RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<si::metre>& length,\n                                   const quantity<si::metre>& width,\n                                   const quantity<si::metre>& height) :\n    StorageTank(length * width, height)\n  {\n  }\n};\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  auto tank = RectangularStorageTank(1'000 * mm, 500 * mm, 200 * mm);\n  // ...\n}\n
    #include <numbers>\nimport mp_units;\n\nusing namespace mp_units;\n\n// add a custom quantity type of kind isq::length\ninline constexpr struct horizontal_length final :\n    quantity_spec<isq::length> {} horizontal_length;\n\n// add a custom derived quantity type of kind isq::area\n// with a constrained quantity equation\ninline constexpr struct horizontal_area final :\n    quantity_spec<isq::area, horizontal_length * isq::width> {} horizontal_area;\n\nclass StorageTank {\n  quantity<horizontal_area[square(si::metre)]> base_;\n  quantity<isq::height[si::metre]> height_;\npublic:\n  constexpr StorageTank(const quantity<horizontal_area[square(si::metre)]>& base,\n                        const quantity<isq::height[si::metre]>& height) :\n    base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n\nclass CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<isq::radius[si::metre]>& radius,\n                                   const quantity<isq::height[si::metre]>& height) :\n    StorageTank(quantity_cast<horizontal_area>(std::numbers::pi * pow<2>(radius)),\n                height)\n  {\n  }\n};\n\nclass RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<horizontal_length[si::metre]>& length,\n                                   const quantity<isq::width[si::metre]>& width,\n                                   const quantity<isq::height[si::metre]>& height) :\n    StorageTank(length * width, height)\n  {\n  }\n};\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\n                                     isq::width(500 * mm),\n                                     isq::height(200 * mm));\n  // ...\n}\n
    SimpleTyped
    #include <mp-units/math.h>\n#include <mp-units/systems/si.h>\n#include <numbers>\n\nusing namespace mp_units;\n\nclass StorageTank {\n  quantity<square(si::metre)> base_;\n  quantity<si::metre> height_;\npublic:\n  constexpr StorageTank(const quantity<square(si::metre)>& base,\n                        const quantity<si::metre>& height) :\n    base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n\nclass CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<si::metre>& radius,\n                                   const quantity<si::metre>& height) :\n    StorageTank(std::numbers::pi * pow<2>(radius), height)\n  {\n  }\n};\n\nclass RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<si::metre>& length,\n                                   const quantity<si::metre>& width,\n                                   const quantity<si::metre>& height) :\n    StorageTank(length * width, height)\n  {\n  }\n};\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  auto tank = RectangularStorageTank(1'000 * mm, 500 * mm, 200 * mm);\n  // ...\n}\n
    #include <mp-units/math.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <numbers>\n\nusing namespace mp_units;\n\n// add a custom quantity type of kind isq::length\ninline constexpr struct horizontal_length final :\n    quantity_spec<isq::length> {} horizontal_length;\n\n// add a custom derived quantity type of kind isq::area\n// with a constrained quantity equation\ninline constexpr struct horizontal_area final :\n    quantity_spec<isq::area, horizontal_length * isq::width> {} horizontal_area;\n\nclass StorageTank {\n  quantity<horizontal_area[square(si::metre)]> base_;\n  quantity<isq::height[si::metre]> height_;\npublic:\n  constexpr StorageTank(const quantity<horizontal_area[square(si::metre)]>& base,\n                        const quantity<isq::height[si::metre]>& height) :\n    base_(base), height_(height)\n  {\n  }\n\n  // ...\n};\n\nclass CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<isq::radius[si::metre]>& radius,\n                                   const quantity<isq::height[si::metre]>& height) :\n    StorageTank(quantity_cast<horizontal_area>(std::numbers::pi * pow<2>(radius)),\n                height)\n  {\n  }\n};\n\nclass RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<horizontal_length[si::metre]>& length,\n                                   const quantity<isq::width[si::metre]>& width,\n                                   const quantity<isq::height[si::metre]>& height) :\n    StorageTank(length * width, height)\n  {\n  }\n};\n\nint main()\n{\n  using namespace mp_units::si::unit_symbols;\n  auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\n                                     isq::width(500 * mm),\n                                     isq::height(200 * mm));\n  // ...\n}\n

    In the above example, the highlighted call doesn't look that safe anymore in the case of simple quantities, right? Suppose someone, either by mistake or due to some refactoring, will call the function with an invalid order of arguments. In that case, the program will compile fine but not work as expected.

    Let's see what will happen if we reorder the arguments in the case of typed quantities:

    auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\n                                   isq::height(200 * mm),\n                                   isq::width(500 * mm));\n

    This time, a compiler provides the following compilation error:

    <source>:53:15: error: no matching constructor for initialization of 'RectangularStorageTank'\n   53 |   auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\n      |               ^                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n   54 |                                      isq::height(200 * mm),\n      |                                      ~~~~~~~~~~~~~~~~~~~~~~\n   55 |                                      isq::width(500 * mm));\n      |                                      ~~~~~~~~~~~~~~~~~~~~\n<source>:43:13: note: candidate constructor not viable: no known conversion from\n                'quantity<mp_units::reference<mp_units::isq::height{{{{{}}}}},\n                                              mp_units::si::milli_<mp_units::si::metre{{}}>{{{{}}}}>{}, int>' to\n                'const quantity<reference<width{}, metre{}>{}, (default) double>' for 2nd argument\n   43 |   constexpr RectangularStorageTank(const quantity<horizontal_length[m]>& length,\n      |             ^\n   44 |                                    const quantity<isq::width[m]>& width,\n      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n

    What about derived quantities? In the above example, you probably noticed that we also defined a custom horizontal_area quantity of kind isq::area. This quantity has the unique property of being implicitly constructible only from the result of the multiplication of quantities of horizontal_area and isq::width or the ones that implicitly convert to them.

    Based on the above error message, we already know that a quantity of isq::height is not implicitly constructible to the quantity of isq::width. This property is transitively passed to derived quantities using them. If by accident, we will try to create a StorageTank base class in the following way:

    class RectangularStorageTank : public StorageTank {\npublic:\n  constexpr RectangularStorageTank(const quantity<horizontal_length[m]>& length,\n                                   const quantity<isq::width[m]>& width,\n                                   const quantity<isq::height[m]>& height) :\n    StorageTank(length * height, height)\n  {\n  }\n};\n

    we will again get a compilation error message like this one:

    error: no matching constructor for initialization of 'StorageTank'\n   46 |     StorageTank(length * height, height)\n      |     ^           ~~~~~~~~~~~~~~~~~~~~~~~\n<source>:22:13: note: candidate constructor not viable: no known conversion from\n                'quantity<mp_units::reference<mp_units::derived_quantity_spec<horizontal_length, mp_units::isq::height>{{}, {{}}},\n                                              mp_units::derived_unit<mp_units::power<mp_units::si::metre, 2>>{{{}}}>{}, [...]>' to\n                'const quantity<reference<horizontal_area{}, derived_unit<power<metre, 2>>{}>{}, [...]>' for 1st argument\n   22 |   constexpr StorageTank(const quantity<horizontal_area[m2]>& base,\n      |             ^           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n

    Tip

    If you need to use various quantities of the same kind, consider using typed quantities to bring an additional level of safety to your project.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#quantity_cast-to-force-unsafe-conversions","title":"quantity_cast() to force unsafe conversions","text":"

    Did you notice the quantity_cast() usage in the other child class?

    class CylindricalStorageTank : public StorageTank {\npublic:\n  constexpr CylindricalStorageTank(const quantity<isq::radius[m]>& radius,\n                                   const quantity<isq::height[m]>& height) :\n    StorageTank(quantity_cast<horizontal_area>(std::numbers::pi * pow<2>(radius)),\n                height)\n  {\n  }\n};\n

    As isq::radius is not convertible to horizontal_length, the derived quantity of pow<2>(radius) can't be converted to horizontal_area as well. It would be unsafe to allow such a conversion as not all of the circles lie flat on the ground, right?

    In such a case, the user has to explicitly force such an unsafe conversion with the help of a quantity_cast(). This function name is easy to spot in code reviews or while searching the project for problems if something goes sideways. In case of unexpected quantities-related issues, this should be the first function to look for.

    Tip

    Do not overuse quantity_cast(). Use it only when necessary and ensure that the requested conversion is exactly what you need in this case.

    "},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#which-mode-should-i-use-in-my-project","title":"Which mode should I use in my project?","text":"

    We have good news for you if you wonder which mode you should choose for your project. Simple and typed quantity modes can be freely mixed with each other. When you use different quantities of the same kind (e.g., radius, wavelength, altitude, ...), you should probably reach for typed quantities to bring additional safety for those cases. Otherwise, just use simple mode for the remaining quantities. The mp-units library will do its best to protect your project based on the information provided.

    Tip

    You can easily mix simple and typed quantities in your project.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/","title":"Systems of Quantities","text":"

    The physical units libraries on the market typically only scope on modeling one or more systems of units. However, this is not the only system kind to model. Another, and maybe even more important, system kind is a system of quantities.

    Info

    Please note that the mp-units is probably the first library on the Open Source market (in any programming language) that models the ISQ with all its definitions provided in ISO 80000. Please provide feedback if something looks odd or could be improved.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#dimension-is-not-enough-to-describe-a-quantity","title":"Dimension is not enough to describe a quantity","text":"

    Most of the products on the market are aware of physical dimensions. However, a dimension is not enough to describe a quantity. For example, let's see the following implementation:

    class Box {\n  area base_;\n  length height_;\npublic:\n  Box(length l, length w, length h) : base_(l * w), height_(h) {}\n  // ...\n};\n\nBox my_box(2 * m, 3 * m, 1 * m);\n

    How do you like such an interface? It turns out that in most existing strongly-typed libraries this is often the best we can do

    Another typical question many users ask is how to deal with work and torque. Both of those have the same dimension but are different quantities.

    A similar issue is related to figuring out what should be the result of:

    auto res = 1 * Hz + 1 * Bq + 1 * Bd;\n

    where:

    • Hz (hertz) - unit of frequency
    • Bq (becquerel) - unit of activity
    • Bd (baud) - unit of modulation rate

    All of those quantities have the same dimension, namely \\(\\mathsf{T}^{-1}\\), but probably it is not wise to allow adding, subtracting, or comparing them, as they describe vastly different physical properties.

    If the above example seems too abstract, let's consider a fuel consumption (fuel volume divided by distance, e.g., 6.7 l/km) and an area. Again, both have the same dimension \\(\\mathsf{L}^{2}\\), but probably it wouldn't be wise to allow adding, subtracting, or comparing a fuel consumption of a car and the area of a football field. Such an operation does not have any physical sense and should fail to compile.

    Important

    More than one quantity may be defined for the same dimension:

    • quantities of different kinds (e.g. frequency, modulation rate, activity, ...)
    • quantities of the same kind (e.g. length, width, altitude, distance, radius, wavelength, position vector, ...)

    It turns out that the above issues can't be solved correctly without proper modeling of a system of quantities.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#quantities-of-the-same-kind","title":"Quantities of the same kind","text":"

    ISO 80000-1

    • Quantities may be grouped together into categories of quantities that are mutually comparable
    • Mutually comparable quantities are called quantities of the same kind
    • Two or more quantities cannot be added or subtracted unless they belong to the same category of mutually comparable quantities
    • Quantities of the same kind within a given system of quantities have the same quantity dimension
    • Quantities of the same dimension are not necessarily of the same kind

    The above quotes from ISO 80000 provide answers to all the issues above. Two quantities can't be added, subtracted, or compared unless they belong to the same kind. As frequency, activity, and modulation rate are of different kinds, the expression provided above should not compile.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#system-of-quantities-is-not-only-about-kinds","title":"System of quantities is not only about kinds","text":"

    ISO 80000 specify hundreds of different quantities. There are plenty of different kinds provided and often each kind contains more than one quantity. In fact, it turns out that such quantities form a hierarchy of quantities of the same kind.

    For example, here are all quantities of the kind length provided in the ISO 80000:

    flowchart TD\n    length[\"<b>length</b><br>[m]\"]\n    length --- width[\"<b>width</b> / <b>breadth</b>\"]\n    length --- height[\"<b>height</b> / <b>depth</b> / <b>altitude</b>\"]\n    width --- thickness[\"<b>thickness</b>\"]\n    width --- diameter[\"<b>diameter</b>\"]\n    width --- radius[\"<b>radius</b>\"]\n    length --- path_length[\"<b>path_length</b>\"]\n    path_length --- distance[\"<b>distance</b>\"]\n    distance --- radial_distance[\"<b>radial_distance</b>\"]\n    length --- wavelength[\"<b>wavelength</b>\"]\n    length --- displacement[\"<b>displacement</b><br>{vector}\"]\n    displacement --- position_vector[\"<b>position_vector</b>\"]\n    radius --- radius_of_curvature[\"<b>radius_of_curvature</b>\"]

    Each of the above quantities expresses some kind of length, and each can be measured with si::metre. However, each of them has different properties, usage, and sometimes even requires a different representation type (notice that position_vector and displacement are vector quantities).

    Forming such a hierarchy helps us in defining arithmetics and conversion rules for various quantities of the same kind.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#defining-quantities","title":"Defining quantities","text":"

    In the mp-units library all the information about the quantity is provided with the quantity_spec class template. In order to define a specific quantity a user should inherit a strong type from such an instantiation.

    Tip

    Quantity specification definitions benefit from an explicit object parameter added in C++23 to remove the need for CRTP idiom, which significantly simplifies the code. However, as C++23 is far from being mainstream today, a portability macro QUANTITY_SPEC() is provided and used consistently through the library to allow the code to compile with C++20 compilers, thanks to the CRTP usage under the hood.

    See more in the C++ compiler support chapter.

    For example, here is how the above quantity kind tree can be modeled in the library:

    C++23C++20Portable
    inline constexpr struct length final : quantity_spec<dim_length> {} length;\ninline constexpr struct width final : quantity_spec<length> {} width;\ninline constexpr auto breadth = width;\ninline constexpr struct height final : quantity_spec<length> {} height;\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\ninline constexpr struct thickness final : quantity_spec<width> {} thickness;\ninline constexpr struct diameter final : quantity_spec<width> {} diameter;\ninline constexpr struct radius final : quantity_spec<width> {} radius;\ninline constexpr struct radius_of_curvature final : quantity_spec<radius> {} radius_of_curvature;\ninline constexpr struct path_length final : quantity_spec<length> {} path_length;\ninline constexpr auto arc_length = path_length;\ninline constexpr struct distance final : quantity_spec<path_length> {} distance;\ninline constexpr struct radial_distance final : quantity_spec<distance> {} radial_distance;\ninline constexpr struct wavelength final : quantity_spec<length> {} wavelength;\ninline constexpr struct displacement final : quantity_spec<length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final : quantity_spec<displacement> {} position_vector;\n
    inline constexpr struct length final : quantity_spec<length, dim_length> {} length;\ninline constexpr struct width final : quantity_spec<width, length> {} width;\ninline constexpr auto breadth = width;\ninline constexpr struct height final : quantity_spec<height, length> {} height;\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\ninline constexpr struct thickness final : quantity_spec<thickness, width> {} thickness;\ninline constexpr struct diameter final : quantity_spec<diameter, width> {} diameter;\ninline constexpr struct radius final : quantity_spec<radius, width> {} radius;\ninline constexpr struct radius_of_curvature final : quantity_spec<radius_of_curvature, radius> {} radius_of_curvature;\ninline constexpr struct path_length final : quantity_spec<path_length, length> {} path_length;\ninline constexpr auto arc_length = path_length;\ninline constexpr struct distance final : quantity_spec<distance, path_length> {} distance;\ninline constexpr struct radial_distance final : quantity_spec<radial_distance, distance> {} radial_distance;\ninline constexpr struct wavelength final : quantity_spec<wavelength, length> {} wavelength;\ninline constexpr struct displacement final : quantity_spec<displacement, length, quantity_character::vector> {} displacement;\ninline constexpr struct position_vector final : quantity_spec<position_vector, displacement> {} position_vector;\n
    QUANTITY_SPEC(length, dim_length);\nQUANTITY_SPEC(width, length);\ninline constexpr auto breadth = width;\nQUANTITY_SPEC(height, length);\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\nQUANTITY_SPEC(thickness, width);\nQUANTITY_SPEC(diameter, width);\nQUANTITY_SPEC(radius, width);\nQUANTITY_SPEC(radius_of_curvature, radius);\nQUANTITY_SPEC(path_length, length);\ninline constexpr auto arc_length = path_length;\nQUANTITY_SPEC(distance, path_length);\nQUANTITY_SPEC(radial_distance, distance);\nQUANTITY_SPEC(wavelength, length);\nQUANTITY_SPEC(displacement, length, quantity_character::vector);\nQUANTITY_SPEC(position_vector, displacement);\n

    Note

    More information on how to define a system of quantities can be found in the \"International System of Quantities (ISQ)\" chapter.

    "},{"location":"users_guide/framework_basics/systems_of_quantities/#comparing-adding-and-subtracting-quantities","title":"Comparing, adding, and subtracting quantities","text":"

    ISO 80000 explicitly states that width and height are quantities of the same kind, and as such they:

    • are mutually comparable,
    • can be added and subtracted.

    If we take the above for granted, the only reasonable result of 1 * width + 1 * height is 2 * length, where the result of length is known as a common quantity type. A result of such an equation is always the first common node in a hierarchy tree of the same kind. For example:

    static_assert(get_common_quantity_spec(isq::width, isq::height) == isq::length);\nstatic_assert(get_common_quantity_spec(isq::thickness, isq::radius) == isq::width);\nstatic_assert(get_common_quantity_spec(isq::distance, isq::path_length) == isq::path_length);\n
    "},{"location":"users_guide/framework_basics/systems_of_quantities/#converting-between-quantities","title":"Converting between quantities","text":"

    Based on the same hierarchy of quantities of kind length, we can define quantity conversion rules.

    1. Implicit conversions

      • every width is a length
      • every radius is a width
      static_assert(implicitly_convertible(isq::width, isq::length));\nstatic_assert(implicitly_convertible(isq::radius, isq::width));\nstatic_assert(implicitly_convertible(isq::radius, isq::length));\n
    2. Explicit conversions

      • not every length is a width
      • not every width is a radius
      static_assert(!implicitly_convertible(isq::length, isq::width));\nstatic_assert(!implicitly_convertible(isq::width, isq::radius));\nstatic_assert(!implicitly_convertible(isq::length, isq::radius));\nstatic_assert(explicitly_convertible(isq::length, isq::width));\nstatic_assert(explicitly_convertible(isq::width, isq::radius));\nstatic_assert(explicitly_convertible(isq::length, isq::radius));\n
    3. Explicit casts

      • height is not a width
      • both height and width are quantities of kind length
      static_assert(!implicitly_convertible(isq::height, isq::width));\nstatic_assert(!explicitly_convertible(isq::height, isq::width));\nstatic_assert(castable(isq::height, isq::width));\n
    4. No conversion

      • time has nothing in common with length
      static_assert(!implicitly_convertible(isq::time, isq::length));\nstatic_assert(!explicitly_convertible(isq::time, isq::length));\nstatic_assert(!castable(isq::time, isq::length));\n
    "},{"location":"users_guide/framework_basics/systems_of_quantities/#hierarchies-of-derived-quantities","title":"Hierarchies of derived quantities","text":"

    Derived quantity equations often do not automatically form a hierarchy tree. This is why it is sometimes not obvious what such a tree should look like. Also, ISO explicitly states:

    ISO/IEC Guide 99

    The division of \u2018quantity\u2019 according to \u2018kind of quantity\u2019 is, to some extent, arbitrary.

    The below presents some arbitrary hierarchy of derived quantities of kind energy:

    flowchart TD\n    energy[\"<b>energy</b><br><i>(mass * length<sup>2</sup> / time<sup>2</sup>)</i><br>[J]\"]\n    energy --- mechanical_energy[\"<b>mechanical_energy</b>\"]\n    mechanical_energy --- potential_energy[\"<b>potential_energy</b>\"]\n    potential_energy --- gravitational_potential_energy[\"<b>gravitational_potential_energy</b><br><i>(mass * acceleration_of_free_fall * height)</i>\"]\n    potential_energy --- elastic_potential_energy[\"<b>elastic_potential_energy</b><br><i>(spring_constant * amount_of_compression<sup>2</sup>)</i>\"]\n    mechanical_energy --- kinetic_energy[\"<b>kinetic_energy</b><br><i>(mass * speed<sup>2</sup>)</i>\"]\n    energy --- enthalpy[\"<b>enthalpy</b>\"]\n    enthalpy --- internal_energy[\"<b>internal_energy</b> / <b>thermodynamic_energy</b>\"]\n    internal_energy --- Helmholtz_energy[\"<b>Helmholtz_energy</b> / <b>Helmholtz_function</b>\"]\n    enthalpy --- Gibbs_energy[\"<b>Gibbs_energy</b> / <b>Gibbs_function</b>\"]\n    energy --- active_energy[\"<b>active_energy</b>\"]

    Notice, that even though all of those quantities have the same dimension and can be expressed in the same units, they have different quantity equations that can be used to create them implicitly:

    • energy is the most generic one and thus can be created from base quantities of mass, length, and time. As those are also the roots of quantities of their kinds and all other quantities from their trees are implicitly convertible to them (we agreed on that \"every width is a length\" already), it means that an energy can be implicitly constructed from any quantity of mass, length, and time:

      static_assert(implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time), isq::energy));\nstatic_assert(implicitly_convertible(isq::mass * pow<2>(isq::height) / pow<2>(isq::time), isq::energy));\n
    • mechanical energy is a more \"specialized\" quantity than energy (not every energy is a mechanical energy). It is why an explicit cast is needed to convert from either energy or the results of its quantity equation:

      static_assert(!implicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                      isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                     isq::mechanical_energy));\n
    • gravitational potential energy is not only even more specialized one but additionally, it is special in a way that it provides its own \"constrained\" quantity equation. Maybe not every mass * pow<2>(length) / pow<2>(time) is a gravitational potential energy, but every mass * acceleration_of_free_fall * height is.

      static_assert(!implicitly_convertible(isq::energy, gravitational_potential_energy));\nstatic_assert(explicitly_convertible(isq::energy, gravitational_potential_energy));\nstatic_assert(!implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                      gravitational_potential_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time),\n                                     gravitational_potential_energy));\nstatic_assert(implicitly_convertible(isq::mass * isq::acceleration_of_free_fall * isq::height,\n                                     gravitational_potential_energy));\n
    "},{"location":"users_guide/framework_basics/systems_of_quantities/#modeling-a-quantity-kind","title":"Modeling a quantity kind","text":"

    In the physical units library, we also need an abstraction describing an entire family of quantities of the same kind. Such quantities have not only the same dimension but also can be expressed in the same units.

    To annotate a quantity to represent its kind (and not just a hierarchy tree's root quantity) we introduced a kind_of<> specifier. For example, to express any quantity of length, we need to type kind_of<isq::length>.

    Important

    isq::length and kind_of<isq::length> are two different things.

    Such an entity behaves as any quantity of its kind. This means that it is implicitly convertible to any quantity in a tree.

    static_assert(!implicitly_convertible(isq::length, isq::height));\nstatic_assert(implicitly_convertible(kind_of<isq::length>, isq::height));\n

    Additionally, the result of operations on quantity kinds is also a quantity kind:

    static_assert(same_type<kind_of<isq::length> / kind_of<isq::time>, kind_of<isq::length / isq::time>>);\n

    However, if at least one equation's operand is not a quantity kind, the result becomes a \"strong\" quantity where all the kinds are converted to the hierarchy tree's root quantities:

    static_assert(!same_type<kind_of<isq::length> / isq::time, kind_of<isq::length / isq::time>>);\nstatic_assert(same_type<kind_of<isq::length> / isq::time, isq::length / isq::time>);\n

    Info

    Only a root quantity from the hierarchy tree or the one marked with is_kind specifier in the quantity_spec definition can be put as a template parameter to the kind_of specifier. For example, kind_of<isq::width> will fail to compile. However, we can call get_kind(q) to obtain a kind of any quantity:

    static_assert(get_kind(isq::width) == kind_of<isq::length>);\n
    "},{"location":"users_guide/framework_basics/systems_of_units/","title":"Systems of Units","text":"

    Modeling a system of units is probably the most important feature and a selling point of every physical units library. Thanks to that, the library can protect users from performing invalid operations on quantities and provide automated conversion factors between various compatible units.

    Probably all the libraries in the wild model the SI and many of them provide support for additional units belonging to various other systems (e.g., imperial, cgs, etc).

    "},{"location":"users_guide/framework_basics/systems_of_units/#systems-of-units-are-based-on-systems-of-quantities","title":"Systems of Units are based on Systems of Quantities","text":"

    Systems of quantities specify a set of quantities and equations relating to those quantities. Those equations do not take any unit or a numerical representation into account at all. To create a quantity, we need to add those missing pieces of information. This is where a system of units kicks in.

    The SI is explicitly stated to be based on the ISQ. Among others, it defines 7 base units, one for each base quantity. In the mp-units this is expressed by associating a quantity kind (that we discussed in detail in the previous chapter) with a unit that is used to express it:

    inline constexpr struct metre final : named_unit<\"m\", kind_of<isq::length>> {} metre;\n

    Important

    The kind_of<isq::length> above states explicitly that this unit has an associated quantity kind. In other words, si::metre (and scaled units based on it) can be used to express the amount of any quantity of kind length.

    "},{"location":"users_guide/framework_basics/systems_of_units/#units-compose","title":"Units compose","text":"

    One of the most vital points of the SI system is that its units compose. This allows providing thousands of different units for hundreds of various quantities with a tiny set of predefined units and prefixes.

    The same is modeled in the mp-units library, which also allows composing predefined units to create a nearly infinite number of different derived units. For example, one can write:

    quantity<si::metre / si::second> q;\n

    to express a quantity of speed. The resulting quantity type is implicitly inferred from the unit equation by repeating the same operations on the associated quantity kinds.

    "},{"location":"users_guide/framework_basics/systems_of_units/#many-shades-of-the-same-unit","title":"Many shades of the same unit","text":"

    The SI provides the names for 22 common coherent units of 22 derived quantities.

    Each such named derived unit is a result of a specific predefined unit equation. For example, a unit of power quantity is defined in the library as:

    inline constexpr struct watt final : named_unit<\"W\", joule / second> {} watt;\n

    However, a power quantity can be expressed in other units as well. For example, the following:

    auto q1 = 42 * W;\nstd::cout << q1 << \"\\n\";\nstd::cout << q1.in(J / s) << \"\\n\";\nstd::cout << q1.in(N * m / s) << \"\\n\";\nstd::cout << q1.in(kg * m2 / s3) << \"\\n\";\n

    prints:

    42 W\n42 J/s\n42 N m/s\n42 kg m\u00b2/s\u00b3\n

    All of the above quantities are equivalent and mean exactly the same.

    Note

    The above code example may give the impression that the order of components in a derived unit is determined by the multiplication order. This is not the case. As stated in Simplifying the resulting symbolic expressions, to be able to reason about and simplify units, the library needs to order them in an appropriate order. This will affect the order of components in a resulting type and text output.

    Please refer to our FAQ for more information.

    "},{"location":"users_guide/framework_basics/systems_of_units/#constraining-a-derived-unit-to-work-only-with-a-specific-derived-quantity","title":"Constraining a derived unit to work only with a specific derived quantity","text":"

    Some derived units are valid only for specific derived quantities. For example, SI specifies both hertz and becquerel derived units with the same unit equation 1 / s. However, it also explicitly states:

    SI Brochure

    The hertz shall only be used for periodic phenomena and the becquerel shall only be used for stochastic processes in activity referred to a radionuclide.

    The above means that the usage of becquerel as a unit of a frequency quantity is an error.

    The library allows constraining such units to work only with quantities of a specific kind in the following way:

    inline constexpr struct hertz final : named_unit<\"Hz\", one / second, kind_of<isq::frequency>> {} hertz;\ninline constexpr struct becquerel final : named_unit<\"Bq\", one / second, kind_of<isq::activity>> {} becquerel;\n

    With the above, hertz can only be used with frequencies, while becquerel should only be used with quantities of activity. This means that the following equation will not compile:

    auto q = 1 * Hz + 1 * Bq;   // Fails to compile\n

    This is exactly what we wanted to achieve to improve the type-safety of the library.

    "},{"location":"users_guide/framework_basics/systems_of_units/#prefixed-units","title":"Prefixed units","text":"

    Besides named units, the SI specifies also 24 prefixes (all being a power of 10) that can be prepended to all named units to obtain various scaled versions of them.

    Implementation of std::ratio provided by all major compilers is able to express only 16 of them. This is why, in the mp-units, we had to find an alternative way to represent unit magnitude in a more flexible way.

    Each prefix is implemented similarly to the following:

    template<PrefixableUnit U> struct quecto_ : prefixed_unit<\"q\", mag_power<10, -30>, U{}> {};\ntemplate<PrefixableUnit auto U> constexpr quecto_<decltype(U)> quecto;\n

    and then a PrefixableUnit can be prefixed in the following way:

    inline constexpr auto qm = quecto<metre>;\n

    The usage of mag_power not only enables providing support for SI prefixes, but it can also efficiently represent any rational magnitude. For example, IEC 80000 prefixes used in the IT industry can be implemented as:

    template<PrefixableUnit U> struct yobi_ : prefixed_unit<\"Yi\", mag_power<2, 80>, U{}> {};\ntemplate<PrefixableUnit auto U> constexpr yobi_<decltype(U)> yobi;\n
    "},{"location":"users_guide/framework_basics/systems_of_units/#scaled-units","title":"Scaled units","text":"

    In the SI, all units are either base or derived units or prefixed versions of those. However, those are only some of the options possible.

    For example, there is a list of off-system units accepted for use with SI. Those are scaled versions of the SI units with ratios that can't be explicitly expressed with predefined SI prefixes. Those include units like minute, hour, or electronvolt:

    inline constexpr struct minute final : named_unit<\"min\", mag<60> * si::second> {} minute;\ninline constexpr struct hour final : named_unit<\"h\", mag<60> * minute> {} hour;\ninline constexpr struct electronvolt final : named_unit<\"eV\", mag_ratio<1'602'176'634, 1'000'000'000> * mag_power<10, -19> * si::joule> {} electronvolt;\n

    Also, units of other systems of units are often defined in terms of scaled versions of the SI units. For example, the international yard is defined as:

    inline constexpr struct yard final : named_unit<\"yd\", mag_ratio<9'144, 10'000> * si::metre> {} yard;\n

    For some units, a magnitude might also be irrational. The best example here is a degree which is defined using a floating-point magnitude having a factor of the number \u03c0 (Pi):

    inline constexpr struct pi final : mag_constant<symbol_text{u8\"\u03c0\", \"pi\"}, std::numbers::pi_v<long double>> {} pi;\ninline constexpr auto \u03c0 = pi;\n
    inline constexpr struct degree final : named_unit<{u8\"\u00b0\", \"deg\"}, mag<\u03c0> / mag<180> * si::radian> {} degree;\n
    "},{"location":"users_guide/framework_basics/systems_of_units/#unit-symbols","title":"Unit symbols","text":"

    Units are available via their full names or through their short symbols. To use a long version, it is enough to type:

    quantity q1 = 42 * si::metre / si::second;\nquantity q2 = 42 * si::kilo<si::metre> / si::hour;\n

    To simplify how we spell it a short, user-friendly symbols are provided in a dedicated subnamespace in systems definitions:

    namespace si::unit_symbols {\n\nconstexpr auto m = si::metre;\nconstexpr auto km = si::kilo<si::metre>;\nconstexpr auto s = si::second;\nconstexpr auto h = si::hour;\n\n}\n

    Unit symbols introduce a lot of short identifiers into the current namespace. This is why they are opt-in. A user has to explicitly \"import\" them from a dedicated unit_symbols namespace:

    using-declarationusing-directive
    using namespace si::unit_symbols;\n\nquantity q1 = 42 * m / s;\nquantity q2 = 42 * km / h;\n
    using si::unit_symbols::m;\nusing si::unit_symbols::km;\nusing si::unit_symbols::s;\nusing si::unit_symbols::h;\n\nquantity q1 = 42 * m / s;\nquantity q2 = 42 * km / h;\n

    We also provide alternative object identifiers using UTF-8 characters in their names for most unit symbols. The code using UTF-8 looks nicer, but it is harder to type on the keyboard. This is why we provide both versions of identifiers for such units.

    PortableWith UTF-8 glyphs
    quantity resistance = 60 * kohm;\nquantity capacitance = 100 * uF;\n
    quantity resistance = 60 * k\u03a9;\nquantity capacitance = 100 * \u00b5F;\n
    "},{"location":"users_guide/framework_basics/systems_of_units/#common-units","title":"Common units","text":"

    Adding, subtracting, or comparing two quantities of different units will force the library to find a common unit for those. This is to prevent data truncation. For the cases when one of the units is an integral multiple of the another, the resulting quantity will use a \"smaller\" one in its result. For example:

    static_assert((1 * kg + 1 * g).unit == g);\nstatic_assert((1 * km + 1 * mm).unit == mm);\nstatic_assert((1 * yd + 1 * mi).unit == yd);\n

    However, in many cases an arithmetic operation on quantities of different units will result in a yet another unit. This happens when none of the source units is an integral multiple of another. In such cases, the library returns a special type that denotes that we are dealing with a common unit of such an equation:

    quantity q1 = 1 * km + 1 * mi;     // quantity<common_unit<international::mile, si::kilo_<si::metre>>{}, int>\nquantity q2 = 1. * rad + 1. * deg; // quantity<common_unit<si::degree, si::radian>{}, double>\n

    Note

    A user should never explicitly instantiate a common_unit class template. The library's framework will do it based on the provided quantity equation.

    "},{"location":"users_guide/framework_basics/text_output/","title":"Text Output","text":"

    Besides providing dimensional analysis and unit conversions, the library also tries hard to print any quantity in the most user-friendly way. We can print the entire quantity or its selected parts (numerical value, unit, or dimension).

    Note

    The library does not provide a text output for quantity points. The quantity stored inside is just an implementation detail of this type. It is a vector from a specific origin. Without the knowledge of the origin, the vector by itself is useless as we can't determine which point it describes.

    In the current library design, point origin does not provide any text in its definition. Even if we could add such information to the point's definition, we would not know how to output it in the text. There may be many ways to do it. For example, should we prepend or append the origin part to the quantity text?

    For example, the text output of 42 m for a quantity point may mean many things. It may be an offset from the mountain top, sea level, or maybe the center of Mars. Printing 42 m AMSL for altitudes above mean sea level is a much better solution, but the library does not have enough information to print it that way by itself.

    Please let us know if you have a good idea of how to solve this issue.

    "},{"location":"users_guide/framework_basics/text_output/#predefined-symbols","title":"Predefined symbols","text":"

    The definitions of dimensions, units, prefixes, and constants require assigning text symbols for each entity. Those symbols will be composed by the library's framework to express dimensions and units of derived quantities.

    DimensionsUnitsPrefixesConstants
    inline constexpr struct dim_length final : base_dimension<\"L\"> {} dim_length;\ninline constexpr struct dim_mass final : base_dimension<\"M\"> {} dim_mass;\ninline constexpr struct dim_time final : base_dimension<\"T\"> {} dim_time;\ninline constexpr struct dim_electric_current final : base_dimension<\"I\"> {} dim_electric_current;\ninline constexpr struct dim_thermodynamic_temperature final : base_dimension<{u8\"\u0398\", \"O\"}> {} dim_thermodynamic_temperature;\ninline constexpr struct dim_amount_of_substance final : base_dimension<\"N\"> {} dim_amount_of_substance;\ninline constexpr struct dim_luminous_intensity final : base_dimension<\"J\"> {} dim_luminous_intensity;\n
    inline constexpr struct second final : named_unit<\"s\", kind_of<isq::time>> {} second;\ninline constexpr struct metre final : named_unit<\"m\", kind_of<isq::length>> {} metre;\ninline constexpr struct gram final : named_unit<\"g\", kind_of<isq::mass>> {} gram;\ninline constexpr auto kilogram = kilo<gram>;\n\ninline constexpr struct newton final : named_unit<\"N\", kilogram * metre / square(second)> {} newton;\ninline constexpr struct joule final : named_unit<\"J\", newton * metre> {} joule;\ninline constexpr struct watt final : named_unit<\"W\", joule / second> {} watt;\ninline constexpr struct coulomb final : named_unit<\"C\", ampere * second> {} coulomb;\ninline constexpr struct volt final : named_unit<\"V\", watt / ampere> {} volt;\ninline constexpr struct farad final : named_unit<\"F\", coulomb / volt> {} farad;\ninline constexpr struct ohm final : named_unit<{u8\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n
    template<PrefixableUnit U> struct micro_ : prefixed_unit<{u8\"\u00b5\", \"u\"}, mag_power<10, -6>, U{}> {};\ntemplate<PrefixableUnit U> struct milli_ : prefixed_unit<\"m\", mag_power<10, -3>, U{}> {};\ntemplate<PrefixableUnit U> struct centi_ : prefixed_unit<\"c\", mag_power<10, -2>, U{}> {};\ntemplate<PrefixableUnit U> struct deci_  : prefixed_unit<\"d\", mag_power<10, -1>, U{}> {};\ntemplate<PrefixableUnit U> struct deca_  : prefixed_unit<\"da\", mag_power<10, 1>, U{}> {};\ntemplate<PrefixableUnit U> struct hecto_ : prefixed_unit<\"h\", mag_power<10, 2>, U{}> {};\ntemplate<PrefixableUnit U> struct kilo_  : prefixed_unit<\"k\", mag_power<10, 3>, U{}> {};\ntemplate<PrefixableUnit U> struct mega_  : prefixed_unit<\"M\", mag_power<10, 6>, U{}> {};\n
    inline constexpr struct hyperfine_structure_transition_frequency_of_cs final : named_unit<{u8\"\u0394\u03bd_Cs\", \"dv_Cs\"}, mag<9'192'631'770> * hertz> {} hyperfine_structure_transition_frequency_of_cs;\ninline constexpr struct speed_of_light_in_vacuum final : named_unit<\"c\", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum;\ninline constexpr struct planck_constant final : named_unit<\"h\", mag_ratio<662'607'015, 100'000'000> * mag_power<10, -34> * joule * second> {} planck_constant;\ninline constexpr struct elementary_charge final : named_unit<\"e\", mag_ratio<1'602'176'634, 1'000'000'000> * mag_power<10, -19> * coulomb> {} elementary_charge;\ninline constexpr struct boltzmann_constant final : named_unit<\"k\", mag_ratio<1'380'649, 1'000'000> * mag_power<10, -23> * joule / kelvin> {} boltzmann_constant;\ninline constexpr struct avogadro_constant final : named_unit<\"N_A\", mag_ratio<602'214'076, 100'000'000> * mag_power<10, 23> / mole> {} avogadro_constant;\ninline constexpr struct luminous_efficacy final : named_unit<\"K_cd\", mag<683> * lumen / watt> {} luminous_efficacy;\n

    Important

    Two symbols always have to be provided if the primary symbol contains characters outside of the basic literal character set. The first must be provided as a UTF-8 literal and may contain any Unicode characters. The second one must provide an alternative spelling and only use characters from within of basic literal character set.

    Note

    Unicode provides only a minimal set of characters available as subscripts, which are often used to differentiate various constants and quantities of the same kind. To workaround this issue, mp-units uses the '_' character to specify that the following characters should be considered a subscript of the symbol.

    Tip

    For older compilers, it might be required to specify a symbol_text class explicitly template name to initialize it with two symbols:

    inline constexpr struct ohm final : named_unit<symbol_text{u8\"\u03a9\", \"ohm\"}, volt / ampere> {} ohm;\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-for-derived-entities","title":"Symbols for derived entities","text":""},{"location":"users_guide/framework_basics/text_output/#text_encoding","title":"text_encoding","text":"

    ISQ and SI standards always specify symbols using UTF-8 encoding. This is why it is a default and primary target for text output. However, in some applications or environments, a standard portable text output using only the characters from the basic literal character set can be preferred by users.

    This is why the library provides an option to change the default encoding to the portable one with:

    enum class text_encoding : std::int8_t {\n  utf8,       // \u00b5s; m\u00b3;  L\u00b2MT\u207b\u00b3\n  portable,   // us; m^3; L^2MT^-3\n  default_encoding = utf8\n};\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-of-derived-dimensions","title":"Symbols of derived dimensions","text":""},{"location":"users_guide/framework_basics/text_output/#dimension_symbol_formatting","title":"dimension_symbol_formatting","text":"

    dimension_symbol_formatting is a data type describing the configuration of the symbol generation algorithm.

    struct dimension_symbol_formatting {\n  text_encoding encoding = text_encoding::default_encoding;\n};\n
    "},{"location":"users_guide/framework_basics/text_output/#dimension_symbol","title":"dimension_symbol()","text":"

    Returns a std::string_view with the symbol of a dimension for the provided configuration:

    template<dimension_symbol_formatting fmt = dimension_symbol_formatting{}, typename CharT = char, Dimension D>\n[[nodiscard]] consteval std::string_view dimension_symbol(D);\n

    For example:

    static_assert(dimension_symbol<{.encoding = text_encoding::portable}>(isq::power.dimension) == \"L^2MT^-3\");\n

    Note

    std::string_view is returned only when C++23 is available. Otherwise, an instance of a basic_fixed_string is being returned.

    "},{"location":"users_guide/framework_basics/text_output/#dimension_symbol_to","title":"dimension_symbol_to()","text":"

    Inserts the generated dimension symbol into the output text iterator at runtime.

    template<typename CharT = char, std::output_iterator<CharT> Out, Dimension D>\nconstexpr Out dimension_symbol_to(Out out, D d, dimension_symbol_formatting fmt = dimension_symbol_formatting{});\n

    For example:

    std::string txt;\ndimension_symbol_to(std::back_inserter(txt), isq::power.dimension, {.encoding = text_encoding::portable});\nstd::cout << txt << \"\\n\";\n

    The above prints:

    L^2MT^-3\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-of-derived-units","title":"Symbols of derived units","text":""},{"location":"users_guide/framework_basics/text_output/#unit_symbol_formatting","title":"unit_symbol_formatting","text":"

    unit_symbol_formatting is a data type describing the configuration of the symbol generation algorithm. It contains three orthogonal fields, each with a default value.

    enum class unit_symbol_solidus : std::int8_t {\n  one_denominator,  // m/s;   kg m\u207b\u00b9 s\u207b\u00b9\n  always,           // m/s;   kg/(m s)\n  never,            // m s\u207b\u00b9; kg m\u207b\u00b9 s\u207b\u00b9\n  default_denominator = one_denominator\n};\n\nenum class unit_symbol_separator : std::int8_t {\n  space,          // kg m\u00b2/s\u00b2\n  half_high_dot,  // kg\u22c5m\u00b2/s\u00b2  (valid only for utf8 encoding)\n  default_separator = space\n};\n\nstruct unit_symbol_formatting {\n  text_encoding encoding = text_encoding::default_encoding;\n  unit_symbol_solidus solidus = unit_symbol_solidus::default_denominator;\n  unit_symbol_separator separator = unit_symbol_separator::default_separator;\n};\n

    unit_symbol_solidus impacts how the division of unit symbols is being presented in the text output. By default, the '/' will be printed if only one unit component is in the denominator. Otherwise, the exponent syntax will be used.

    unit_symbol_separator specifies how multiple multiplied units should be separated from each other. By default, the space (' ') will be used as a separator.

    "},{"location":"users_guide/framework_basics/text_output/#unit_symbol","title":"unit_symbol()","text":"

    Returns a std::string_view with the symbol of a unit for the provided configuration:

    template<unit_symbol_formatting fmt = unit_symbol_formatting{}, typename CharT = char, Unit U>\n[[nodiscard]] consteval std::string_view unit_symbol(U);\n

    For example:

    static_assert(unit_symbol<{.solidus = unit_symbol_solidus::never,\n                           .separator = unit_symbol_separator::half_high_dot}>(kg * m / s2) == \"kg\u22c5m\u22c5s\u207b\u00b2\");\n
    "},{"location":"users_guide/framework_basics/text_output/#unit_symbol_to","title":"unit_symbol_to()","text":"

    Inserts the generated unit symbol into the output text iterator at runtime.

    template<typename CharT = char, std::output_iterator<CharT> Out, Unit U>\nconstexpr Out unit_symbol_to(Out out, U u, unit_symbol_formatting fmt = unit_symbol_formatting{});\n

    For example:

    std::string txt;\nunit_symbol_to(std::back_inserter(txt), kg * m / s2,\n               {.solidus = unit_symbol_solidus::never, .separator = unit_symbol_separator::half_high_dot});\nstd::cout << txt << \"\\n\";\n

    The above prints:

    kg\u22c5m\u22c5s\u207b\u00b2\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-of-scaled-units","title":"Symbols of scaled units","text":"

    In most cases scaled units are hidden behind named units. However, there are a few real-life where a user directly faces a scaled unit. For example:

    constexpr Unit auto L_per_100km = L / (mag<100> * km);\n

    The above is a derived unit of litre divided by a scaled unit of 100 kilometers. As we can see a scaled unit has a magnitude and a reference unit. To denote the scope of such a unit, we enclose it in [...]. For example, the following:

    std::cout << 6.7 * L_per_100km << \"\\n\";\n

    prints:

    6.7 L/[100 km]\n
    "},{"location":"users_guide/framework_basics/text_output/#symbols-of-common-units","title":"Symbols of common units","text":"

    Some common units expressed with a specialization of the common_unit class template need special printing rules for their symbols. As they represent a minimum set of equivalent common units resulting from the addition or subtraction of multiple quantities, we print all of them as a scaled version of the source unit. For example, the following:

    std::cout << 1 * km + 1 * mi << \"\\n\";\nstd::cout << 1 * nmi + 1 * mi << \"\\n\";\nstd::cout << 1 * km / h + 1 * m / s << \"\\n\";\nstd::cout << 1 * rad + 1 * deg << \"\\n\";\n

    prints:

    40771 EQUIV{[1/25146 mi], [1/15625 km]}\n108167 EQUIV{[1/50292 mi], [1/57875 nmi]}\n23 EQUIV{[1/5 km/h], [1/18 m/s]}\n183.142 EQUIV{[1/\u03c0\u00b0], [1/180 rad]}\n

    Thanks to the above, it might be easier for the user to reason about the magnitude of the resulting unit and its impact on the value stored in the quantity.

    "},{"location":"users_guide/framework_basics/text_output/#space_before_unit_symbol-customization-point","title":"space_before_unit_symbol customization point","text":"

    The SI Brochure says:

    SI Brochure

    The numerical value always precedes the unit and a space is always used to separate the unit from the number. ... The only exceptions to this rule are for the unit symbols for degree, minute and second for plane angle, \u00b0, \u2032 and \u2033, respectively, for which no space is left between the numerical value and the unit symbol.

    There are more units with such properties. For example, percent (%) and per mille(\u2030).

    To support the above and other similar cases, the library exposes space_before_unit_symbol customization point. By default, its value is true for all the units, so the space between a number and a unit will be inserted in the output text. To change this behavior, we have to provide a partial specialization for a specific unit:

    template<>\nconstexpr bool space_before_unit_symbol<non_si::degree> = false;\n

    Note

    The above works only for the default formatting or for the format strings that use %? placement field (std::format(\"{}\", q) is equivalent to std::format(\"{:%N%?%U}\", q)).

    In case a user provides custom format specification (e.g., std::format(\"{:%N %U}\", q)), the library will always obey this specification for all the units (no matter what the actual value of the space_before_unit_symbol customization point is) and the separating space will always be used in this case.

    "},{"location":"users_guide/framework_basics/text_output/#output-streams","title":"Output streams","text":"

    Tip

    The output streaming support is opt-in and can be enabled by including the <mp-units/ostream.h> header file.

    The easiest way to print a dimension, unit, or quantity is to provide its object to the output stream:

    const QuantityOf<isq::speed> auto v1 = avg_speed(220. * km, 2 * h);\nconst QuantityOf<isq::speed> auto v2 = avg_speed(140. * mi, 2 * h);\nstd::cout << v1 << '\\n';            // 110 km/h\nstd::cout << v2 << '\\n';            // 70 mi/h\nstd::cout << v2.unit << '\\n';       // mi/h\nstd::cout << v2.dimension << '\\n';  // LT\u207b\u00b9\n

    The text output will always print the value using the default formatting for this entity.

    Important: Don't assume a unit

    Remember that when we deal with a quantity of an \"unknown\" (e.g., auto) type, it is a good practice to always convert the unit to the expected one before passing it to the text output:

    std::cout << v1.in(km / h) << '\\n';       // 110 km/h\nstd::cout << v1.force_in(m / s) << '\\n';  // 30.5556 m/s\n
    "},{"location":"users_guide/framework_basics/text_output/#output-stream-formatting","title":"Output stream formatting","text":"

    Only basic formatting can be applied to output streams. It includes control over width, fill, and alignment.

    The numerical value of the quantity will be printed according to the current stream state and standard manipulators may be used to customize that (assuming that the underlying representation type respects them).

    std::cout << \"|\" << std::setw(10) << 123 * m << \"|\\n\";                       // |     123 m|\nstd::cout << \"|\" << std::setw(10) << std::left << 123 * m << \"|\\n\";          // |123 m     |\nstd::cout << \"|\" << std::setw(10) << std::setfill('*') << 123 * m << \"|\\n\";  // |123 m*****|\n

    Note

    To have more control over the formatting of the quantity that is printed with the output stream just use std::cout << std::format(...).

    "},{"location":"users_guide/framework_basics/text_output/#text-formatting","title":"Text formatting","text":"

    The library provides custom formatters for std::format facility, which allows fine-grained control over what and how it is being printed in the text output.

    Tip

    The text formatting facility support is opt-in and can be enabled by including the <mp-units/format.h> header file.

    "},{"location":"users_guide/framework_basics/text_output/#controlling-width-fill-and-alignment","title":"Controlling width, fill, and alignment","text":"

    Formatting grammar for all the entities provides control over width, fill, and alignment. The C++ standard grammar tokens fill-and-align and width are being used. They treat the entity as a contiguous text to be aligned. For example, here are a few examples of the quantity numerical value and symbol formatting:

    std::println(\"|{:0}|\", 123 * m);     // |123 m|\nstd::println(\"|{:10}|\", 123 * m);    // |     123 m|\nstd::println(\"|{:<10}|\", 123 * m);   // |123 m     |\nstd::println(\"|{:>10}|\", 123 * m);   // |     123 m|\nstd::println(\"|{:^10}|\", 123 * m);   // |  123 m   |\nstd::println(\"|{:*<10}|\", 123 * m);  // |123 m*****|\nstd::println(\"|{:*>10}|\", 123 * m);  // |*****123 m|\nstd::println(\"|{:*^10}|\", 123 * m);  // |**123 m***|\n

    It is important to note that in the second line above, the quantity text is aligned to the right by default, which is consistent with the formatting of numeric types. Units and dimensions behave as text and, thus, are aligned to the left by default.

    Note

    std::println is a C++23 facility. In case we do not have access to C++23, we can obtain the same output with:

    std::cout << std::format(\"<format-string>\\n\", <format-args>);\n
    "},{"location":"users_guide/framework_basics/text_output/#dimension-formatting","title":"Dimension formatting","text":"
    dimension-format-spec = [fill-and-align], [width], [dimension-spec];\ndimension-spec        = [character-set];\ncharacter-set         = 'U' | 'P';\n

    In the above grammar:

    • fill-and-align and width tokens are defined in the format.string.std chapter of the C++ standard specification,
    • character-set token specifies the symbol text encoding:
      • U (default) uses the UTF-8 symbols defined by [@ISO80000] (e.g., LT\u207b\u00b2),
      • P forces non-standard portable output (e.g., LT^-2).

    Dimension symbols of some quantities are specified to use Unicode signs by the ISQ (e.g., \u0398 symbol for the thermodynamic temperature dimension). The library follows this by default. From the engineering point of view, sometimes Unicode text might not be the best solution, as terminals of many (especially embedded) devices can output only letters from the basic literal character set. In such a case, the dimension symbol can be forced to be printed using such characters thanks to character-set token:

    std::println(\"{}\", isq::dim_thermodynamic_temperature);   // \u0398\nstd::println(\"{:P}\", isq::dim_thermodynamic_temperature); // O\nstd::println(\"{}\", isq::power.dimension);                 // L\u00b2MT\u207b\u00b3\nstd::println(\"{:P}\", isq::power.dimension);               // L^2MT^-3\n
    "},{"location":"users_guide/framework_basics/text_output/#unit-formatting","title":"Unit formatting","text":"
    unit-format-spec      = [fill-and-align], [width], [unit-spec];\nunit-spec             = [character-set], [unit-symbol-solidus], [unit-symbol-separator], [L]\n                      | [character-set], [unit-symbol-separator], [unit-symbol-solidus], [L]\n                      | [unit-symbol-solidus], [character-set], [unit-symbol-separator], [L]\n                      | [unit-symbol-solidus], [unit-symbol-separator], [character-set], [L]\n                      | [unit-symbol-separator], [character-set], [unit-symbol-solidus], [L]\n                      | [unit-symbol-separator], [unit-symbol-solidus], [character-set], [L];\nunit-symbol-solidus   = '1' | 'a' | 'n';\nunit-symbol-separator = 's' | 'd';\n

    In the above grammar:

    • fill-and-align and width tokens are defined in the format.string.std chapter of the C++ standard specification,
    • unit-symbol-solidus token specifies how the division of units should look like:
      • '1' (default) outputs / only when there is only one unit in the denominator, otherwise negative exponents are printed (e.g., m/s, kg m\u207b\u00b9 s\u207b\u00b9)
      • 'a' always uses solidus (e.g., m/s, kg/(m s))
      • 'n' never prints solidus, which means that negative exponents are always used (e.g., m s\u207b\u00b9, kg m\u207b\u00b9 s\u207b\u00b9)
    • unit-symbol-separator token specifies how multiplied unit symbols should be separated:
      • 's' (default) uses space as a separator (e.g., kg m\u00b2/s\u00b2)
      • 'd' uses half-high dot (\u22c5) as a separator (e.g., kg\u22c5m\u00b2/s\u00b2) (requires the UTF-8 encoding)
    • 'L' is reserved for possible future localization use in case the C++ standard library gets access to the ICU-like database.

    Note

    The above grammar intended that the elements of unit-spec can appear in any order as they have unique characters. Users shouldn't have to remember the order of those tokens to control the formatting of a unit symbol.

    Unit symbols of some quantities are specified to use Unicode signs by the SI (e.g., \u03a9 symbol for the resistance quantity). The library follows this by default. From the engineering point of view, Unicode text might not be the best solution sometimes, as terminals of many (especially embedded) devices can output only letters from the basic literal character set. In such a case, the unit symbol can be forced to be printed using such characters thanks to character-set token:

    std::println(\"{}\", si::ohm);      // \u03a9\nstd::println(\"{:P}\", si::ohm);    // ohm\nstd::println(\"{}\", us);           // \u00b5s\nstd::println(\"{:P}\", us);         // us\nstd::println(\"{}\", m / s2);       // m/s\u00b2\nstd::println(\"{:P}\", m / s2);     // m/s^2\n

    Additionally, both ISO 80000 and SI leave some freedom on how to print unit symbols. This is why two additional tokens were introduced.

    unit-symbol-solidus specifies how the division of units should look like. By default, / will be used only when the denominator contains only one unit. However, with the 'a' or 'n' options, we can force the facility to print the / character always (even when there are more units in the denominator), or never, in which case a parenthesis will be added to enclose all denominator units.

    std::println(\"{}\", m / s);          // m/s\nstd::println(\"{}\", kg / m / s2);    // kg m\u207b\u00b9 s\u207b\u00b2\nstd::println(\"{:a}\", m / s);        // m/s\nstd::println(\"{:a}\", kg / m / s2);  // kg/(m s\u00b2)\nstd::println(\"{:n}\", m / s);        // m s\u207b\u00b9\nstd::println(\"{:n}\", kg / m / s2);  // kg m\u207b\u00b9 s\u207b\u00b2\n

    The unit-symbol-separator token allows us to obtain the following outputs:

    std::println(\"{}\", kg * m2 / s2);    // kg m\u00b2/s\u00b2\nstd::println(\"{:d}\", kg * m2 / s2);  // kg\u22c5m\u00b2/s\u00b2\n

    Note

    'd' requires the UTF-8 encoding to be set.

    "},{"location":"users_guide/framework_basics/text_output/#quantity-formatting","title":"Quantity formatting","text":"
    quantity-format-spec        = [fill-and-align], [width], [quantity-specs], [defaults-specs];\nquantity-specs              = conversion-spec;\n                            | quantity-specs, conversion-spec;\n                            | quantity-specs, literal-char;\nliteral-char                = ? any character other than '{', '}', or '%' ?;\nconversion-spec             = '%', placement-type;\nplacement-type              = subentity-id | '?' | '%';\ndefaults-specs              = ':', default-spec-list;\ndefault-spec-list           = default-spec;\n                            | default-spec-list, default-spec;\ndefault-spec                = subentity-id, '[' format-spec ']';\nsubentity-id                = 'N' | 'U' | 'D';\nformat-spec                 = ? as specified by the formatter for the argument type ?;\n

    In the above grammar:

    • fill-and-align and width tokens are defined in the format.string.std chapter of the C++ standard specification,
    • placement-type token specifies which entity should be put and where:
      • 'N' inserts a default-formatted numerical value of the quantity,
      • 'U' inserts a default-formatted unit of the quantity,
      • 'D' inserts a default-formatted dimension of the quantity,
      • '?' inserts an optional separator between the number and a unit based on the value of space_before_unit_symbol for this unit,
      • '%' just inserts '%' character.
    • defaults-specs token allows overwriting defaults for the underlying formatters with the custom format string. Each override starts with a subentity identifier ('N', 'U', or 'D') followed by the format string enclosed in square brackets.
    "},{"location":"users_guide/framework_basics/text_output/#default-formatting","title":"Default formatting","text":"

    To format quantity values, the formatting facility uses quantity-format-spec. If left empty, the default formatting is applied. The same default formatting is also applied to the output streams. This is why the following code lines produce the same output:

    std::cout << \"Distance: \" << 123 * km << \"\\n\";\nstd::cout << std::format(\"Distance: {}\\n\", 123 * km);\nstd::cout << std::format(\"Distance: {:%N%?%U}\\n\", 123 * km);\n

    Note

    For some quantities, the {:%N %U} format may provide a different output than the default one, as some units have space_before_unit_symbol customization point explicitly set to false (e.g., % and \u00b0).

    "},{"location":"users_guide/framework_basics/text_output/#quantity-numerical-value-unit-symbol-or-both","title":"Quantity numerical value, unit symbol, or both?","text":"

    Thanks to the grammar provided above, the user can easily decide to either:

    • print a whole quantity:

      std::println(\"Speed: {}\", 120 * km / h);\n
      Speed: 120 km/h\n
    • provide custom quantity formatting:

      std::println(\"Speed: {:%N in %U}\", 120 * km / h);\n
      Speed: 120 in km/h\n
    • provide custom formatting for components:

      std::println(\"Speed: {::N[.2f]U[n]}\", 100. * km / (3 * h));\n
      Speed: 33.33 km h\u207b\u00b9\n
    • print only specific components (numerical value, unit, or dimension):

      std::println(\"Speed:\\n- number: {0:%N}\\n- unit: {0:%U}\\n- dimension: {0:%D}\", 120 * km / h);\n
      Speed:\n- number: 120\n- unit: km/h\n- dimension: LT\u207b\u00b9\n
    "},{"location":"users_guide/framework_basics/text_output/#formatting-of-the-quantity-numerical-value","title":"Formatting of the quantity numerical value","text":"

    The representation type used as a numerical value of a quantity must provide its own formatter specialization. It will be called by the quantity formatter with the format-spec provided by the user in the N defaults specification.

    In case we use C++ fundamental arithmetic types with our quantities the standard formatter specified in format.string.std will be used. The rest of this chapter assumes that it is the case and provides some usage examples.

    sign token allows us to specify how the value's sign is being printed:

    std::println(\"{0},{0::N[+]},{0::N[-]},{0::N[ ]}\", 1 * m);   // 1 m,+1 m,1 m, 1 m\nstd::println(\"{0},{0::N[+]},{0::N[-]},{0::N[ ]}\", -1 * m);  // -1 m,-1 m,-1 m,-1 m\n

    where:

    • + indicates that a sign should be used for both non-negative and negative numbers,
    • - indicates that a sign should be used for negative numbers and negative zero only (this is the default behavior),
    • <space> indicates that a leading space should be used for non-negative numbers other than negative zero, and a minus sign for negative numbers and negative zero.

    precision token is allowed only for floating-point representation types:

    std::println(\"{::N[.0]}\", 1.2345 * m);   // 1 m\nstd::println(\"{::N[.1]}\", 1.2345 * m);   // 1 m\nstd::println(\"{::N[.2]}\", 1.2345 * m);   // 1.2 m\nstd::println(\"{::N[.3]}\", 1.2345 * m);   // 1.23 m\nstd::println(\"{::N[.0f]}\", 1.2345 * m);  // 1 m\nstd::println(\"{::N[.1f]}\", 1.2345 * m);  // 1.2 m\nstd::println(\"{::N[.2f]}\", 1.2345 * m);  // 1.23 m\n

    type specifies how a value of the representation type is being printed. For integral types:

    std::println(\"{::N[b]}\", 42 * m);    // 101010 m\nstd::println(\"{::N[B]}\", 42 * m);    // 101010 m\nstd::println(\"{::N[d]}\", 42 * m);    // 42 m\nstd::println(\"{::N[o]}\", 42 * m);    // 52 m\nstd::println(\"{::N[x]}\", 42 * m);    // 2a m\nstd::println(\"{::N[X]}\", 42 * m);    // 2A m\n

    The above can be printed in an alternate version thanks to the # token:

    std::println(\"{::N[#b]}\", 42 * m);   // 0b101010 m\nstd::println(\"{::N[#B]}\", 42 * m);   // 0B101010 m\nstd::println(\"{::N[#o]}\", 42 * m);   // 052 m\nstd::println(\"{::N[#x]}\", 42 * m);   // 0x2a m\nstd::println(\"{::N[#X]}\", 42 * m);   // 0X2A m\n

    For floating-point values, the type token works as follows:

    std::println(\"{::N[a]}\",   1.2345678 * m);      // 1.3c0ca2a5b1d5dp+0 m\nstd::println(\"{::N[.3a]}\", 1.2345678 * m);      // 1.3c1p+0 m\nstd::println(\"{::N[A]}\",   1.2345678 * m);      // 1.3C0CA2A5B1D5DP+0 m\nstd::println(\"{::N[.3A]}\", 1.2345678 * m);      // 1.3C1P+0 m\nstd::println(\"{::N[e]}\",   1.2345678 * m);      // 1.234568e+00 m\nstd::println(\"{::N[.3e]}\", 1.2345678 * m);      // 1.235e+00 m\nstd::println(\"{::N[E]}\",   1.2345678 * m);      // 1.234568E+00 m\nstd::println(\"{::N[.3E]}\", 1.2345678 * m);      // 1.235E+00 m\nstd::println(\"{::N[g]}\",   1.2345678 * m);      // 1.23457 m\nstd::println(\"{::N[g]}\",   1.2345678e8 * m);    // 1.23457e+08 m\nstd::println(\"{::N[.3g]}\", 1.2345678 * m);      // 1.23 m\nstd::println(\"{::N[.3g]}\", 1.2345678e8 * m);    // 1.23e+08 m\nstd::println(\"{::N[G]}\",   1.2345678 * m);      // 1.23457 m\nstd::println(\"{::N[G]}\",   1.2345678e8 * m);    // 1.23457E+08 m\nstd::println(\"{::N[.3G]}\", 1.2345678 * m);      // 1.23 m\nstd::println(\"{::N[.3G]}\", 1.2345678e8 * m);    // 1.23E+08 m\n
    "},{"location":"users_guide/framework_basics/the_affine_space/","title":"The Affine Space","text":"

    The affine space has two types of entities:

    • Point - a position specified with coordinate values (e.g., location, address, etc.)
    • Displacement vector - the difference between two points (e.g., shift, offset, displacement, duration, etc.)

    In the following subchapters, we will often refer to displacement vectors simply as vectors for brevity.

    Note

    The displacement vector described here is specific to the affine space theory and is not the same thing as the quantity of a vector character that we discussed in the \"Scalars, vectors, and tensors\" chapter (although, in some cases, those terms may overlap).

    "},{"location":"users_guide/framework_basics/the_affine_space/#operations-in-the-affine-space","title":"Operations in the affine space","text":"

    Here are the primary operations one can do in the affine space:

    • vector + vector -> vector
    • vector - vector -> vector
    • -vector -> vector
    • vector * scalar -> vector
    • scalar * vector -> vector
    • vector / scalar -> vector
    • point - point -> vector
    • point + vector -> point
    • vector + point -> point
    • point - vector -> point

    Important

    It is not possible to:

    • add two points,
    • subtract a point from a vector,
    • multiply nor divide points with anything else.
    "},{"location":"users_guide/framework_basics/the_affine_space/#points-are-more-common-than-most-of-us-imagine","title":"Points are more common than most of us imagine","text":"

    Point abstractions should be used more often in the C++ software. They are not only about temperature or time. Points are everywhere around us and should become more popular in the products we implement. They can be used to implement:

    • temperature points,
    • timestamps,
    • daily mass readouts from the scale,
    • altitudes of mountain peaks on a map,
    • current path length measured by the car's odometer,
    • today's price of instruments on the market,
    • and many more.

    Improving the affine space's Points intuition will allow us to write better and safer software.

    "},{"location":"users_guide/framework_basics/the_affine_space/#displacement-vector-is-modeled-by-quantity","title":"Displacement vector is modeled by quantity","text":"

    Up until now, each time we used a quantity in our code, we were modeling some kind of a difference between two things:

    • the distance between two points,
    • duration between two time points,
    • the difference in speed (even if relative to zero).

    As we already know, a quantity type provides all operations required for a displacement vector abstraction in the affine space. It can be constructed with:

    • the multiply syntax (works for most of the units),
    • delta<Reference> construction helper (e.g., delta<isq::height[m]>(42), delta<deg_C>(3)),
    • two-parameter constructor taking a number and a quantity reference/unit.

    Note

    The multiply syntax support is disabled for units that provide a point origin in their definition (i.e., units of temperature like K, deg_C, and deg_F).

    "},{"location":"users_guide/framework_basics/the_affine_space/#point-is-modeled-by-quantity_point-and-pointorigin","title":"Point is modeled by quantity_point and PointOrigin","text":"

    In the mp-units library, the Point abstraction is modelled by:

    • PointOrigin concept that specifies measurement origin, and
    • quantity_point class template that specifies a Point relative to a specific predefined origin.
    "},{"location":"users_guide/framework_basics/the_affine_space/#quantity_point","title":"quantity_point","text":"

    The quantity_point class template specifies an absolute quantity measured from a predefined origin:

    template<Reference auto R,\n         PointOriginFor<get_quantity_spec(R)> auto PO = default_point_origin(R),\n         RepresentationOf<get_quantity_spec(R)> Rep = double>\nclass quantity_point;\n

    As we can see above, the quantity_point class template exposes one additional parameter compared to quantity. The PO parameter satisfies a PointOriginFor concept and specifies the origin of our measurement scale.

    Each quantity_point internally stores a quantity object, which represents a displacement vector from the predefined origin. Thanks to this, an instantiation of a quantity_point can be considered as a model of a vector space from such an origin.

    Forcing the user to manually predefine an origin for every domain may be cumbersome and discourage users from using such abstractions at all. This is why, by default, the PO template parameter is initialized with the default_point_origin(R) that provides the quantity points' scale zeroth point using the following rules:

    • if the measurement unit of a quantity specifies its point origin in its definition (e.g., degree Celsius), then this origin is being used,
    • otherwise, an instantiation of zeroth_point_origin<QuantitySpec> is being used which provides a well-established zeroth point for a specific quantity type.

    Quantity points with default point origins may be constructed with the point construction helper or forcing an explicit conversion from the quantity:

    // quantity_point qp1 = 42 * m;           // Compile-time error\n// quantity_point qp2 = 42 * K;           // Compile-time error\n// quantity_point qp3 = delta<deg_C>(42); // Compile-time error\nquantity_point qp4(42 * m);\nquantity_point qp5(42 * K);\nquantity_point qp6(delta<deg_C>(42));\nquantity_point qp7 = point<m>(42);\nquantity_point qp8 = point<K>(42);\nquantity_point qp9 = point<deg_C>(42);\n

    Tip

    The quantity_point definition can be found in the mp-units/quantity_point.h header file.

    "},{"location":"users_guide/framework_basics/the_affine_space/#zeroth_point_originquantityspec","title":"zeroth_point_origin<QuantitySpec>","text":"

    zeroth_point_origin<QuantitySpec> is meant to be used in cases where the specific domain has a well-established, non-controversial, and unique zeroth point on the measurement scale. This saves the user from the need to write a boilerplate code that would predefine such a type for this domain.

    quantity_point<isq::distance[si::metre]> qp1(100 * m);\nquantity_point<isq::distance[si::metre]> qp2 = point<m>(120);\n\nassert(qp1.quantity_from_zero() == 100 * m);\nassert(qp2.quantity_from_zero() == 120 * m);\nassert(qp2.quantity_from(qp1) == 20 * m);\nassert(qp1.quantity_from(qp2) == -20 * m);\n\nassert(qp2 - qp1 == 20 * m);\nassert(qp1 - qp2 == -20 * m);\n\n// auto res = qp1 + qp2;   // Compile-time error\n

    In the above code 100 * m and 120 * m still create two quantities that serve as displacement vectors here. Quantity point objects can be explicitly constructed from such quantities only when their origin is an instantiation of the zeroth_point_origin<QuantitySpec>.

    It is really important to understand that even though we can use .quantity_from_zero() to obtain the displacement vector of a point from the origin, the point by itself does not represent or have any associated physical value. It is just a point in some space. The same point can be expressed with different displacement vectors from different origins.

    It is also worth mentioning that simplicity comes with a safety cost here. For some users, it might be surprising that the usage of zeroth_point_origin<QuantitySpec> makes various quantity point objects compatible as long as quantity types used in the origin and reference are compatible:

    quantity_point<si::metre> qp1{isq::distance(100 * m)};\nquantity_point<si::metre> qp2 = point<isq::height[m]>(120);\n\nassert(qp2.quantity_from(qp1) == 20 * m);\nassert(qp1.quantity_from(qp2) == -20 * m);\nassert(qp2 - qp1 == 20 * m);\nassert(qp1 - qp2 == -20 * m);\n
    "},{"location":"users_guide/framework_basics/the_affine_space/#absolute-point-origin","title":"Absolute point origin","text":"

    In cases where we want to implement an isolated independent space in which points are not compatible with other spaces, even of the same quantity type, we should manually predefine an absolute point origin.

    inline constexpr struct origin final : absolute_point_origin<isq::distance> {} origin;\n\n// quantity_point<si::metre, origin> qp1{100 * m};        // Compile-time error\n// quantity_point<si::metre, origin> qp2{delta<m>(120)};  // Compile-time error\nquantity_point<si::metre, origin> qp1 = origin + 100 * m;\nquantity_point<si::metre, origin> qp2 = 120 * m + origin;\n\n// assert(qp1.quantity_from_zero() == 100 * m);   // Compile-time error\n// assert(qp2.quantity_from_zero() == 120 * m);   // Compile-time error\nassert(qp1.quantity_from(origin) == 100 * m);\nassert(qp2.quantity_from(origin) == 120 * m);\nassert(qp2.quantity_from(qp1) == 20 * m);\nassert(qp1.quantity_from(qp2) == -20 * m);\n\nassert(qp1 - origin == 100 * m);\nassert(qp2 - origin == 120 * m);\nassert(qp2 - qp1 == 20 * m);\nassert(qp1 - qp2 == -20 * m);\n\nassert(origin - qp1 == -100 * m);\nassert(origin - qp2 == -120 * m);\n\n// assert(origin - origin == 0 * m);   // Compile-time error\n

    We can't construct a quantity point directly from the quantity anymore when a custom, named origin is used. To prevent potential safety and maintenance issues, we always need to explicitly provide both a compatible origin and a quantity measured from it to construct a quantity point.

    Said otherwise, a quantity point defined in terms of a specific origin is the result of adding the origin and the displacement vector measured from it to the point we create.

    Info

    A rationale for this longer construction syntax can be found in the Why can't I create a quantity by passing a number to a constructor? chapter.

    Similarly to creation of a quantity, if someone does not like the operator-based syntax to create a quantity_point, the same results can be achieved with a two-parameter constructor:

    quantity_point qp1{100 * m, origin};\n

    Again, CTAD always helps to use precisely the type we need in a current case.

    Additionally, if a quantity point is defined in terms of a custom, named origin, then we can't use a quantity_from_zero() member function anymore. This is to prevent surprises, as our origin may not necessarily be perceived as an absolute zero in the domain we model. Also, as we will learn soon, we can define several related origins in one space, and then it gets harder to understand which one is the \"zero\" one. This is why, to be specific and always correct about the points we use, a quantity_from(QP) member function can be used (where QP can either be an origin or another quantity point).

    Finally, please note that it is not allowed to subtract two point origins defined in terms of absolute_point_origin (e.g., origin - origin) as those do not contain information about the unit, so we cannot determine a resulting quantity type.

    "},{"location":"users_guide/framework_basics/the_affine_space/#modeling-independent-spaces-in-one-domain","title":"Modeling independent spaces in one domain","text":"

    Absolute point origins are also perfect for establishing independent spaces even if the same quantity type and unit is being used:

    inline constexpr struct origin1 final : absolute_point_origin<isq::distance> {} origin1;\ninline constexpr struct origin2 final : absolute_point_origin<isq::distance> {} origin2;\n\nquantity_point qp1 = origin1 + 100 * m;\nquantity_point qp2 = origin2 + 120 * m;\n\nassert(qp1.quantity_from(origin1) == 100 * m);\nassert(qp2.quantity_from(origin2) == 120 * m);\n\nassert(qp1 - origin1 == 100 * m);\nassert(qp2 - origin2 == 120 * m);\nassert(origin1 - qp1 == -100 * m);\nassert(origin2 - qp2 == -120 * m);\n\n// assert(qp2 - qp1 == 20 * m);                    // Compile-time error\n// assert(qp1 - origin2 == 100 * m);               // Compile-time error\n// assert(qp2 - origin1 == 120 * m);               // Compile-time error\n// assert(qp2.quantity_from(qp1) == 20 * m);       // Compile-time error\n// assert(qp1.quantity_from(origin2) == 100 * m);  // Compile-time error\n// assert(qp2.quantity_from(origin1) == 120 * m);  // Compile-time error\n
    "},{"location":"users_guide/framework_basics/the_affine_space/#relative-point-origin","title":"Relative Point origin","text":"

    We often do not have only one ultimate \"zero\" point when we measure things. Often, we have one common scale, but we measure various quantities relative to different points and expect those points to be compatible. There are many examples here, but probably the most common are temperatures, timestamps, and altitudes.

    For such cases, relative point origins should be used:

    inline constexpr struct A final : absolute_point_origin<isq::distance> {} A;\ninline constexpr struct B final : relative_point_origin<A + 10 * m> {} B;\ninline constexpr struct C final : relative_point_origin<B + 10 * m> {} C;\ninline constexpr struct D final : relative_point_origin<A + 30 * m> {} D;\n\nquantity_point qp1 = C + 100 * m;\nquantity_point qp2 = D + 120 * m;\n\nassert(qp1.quantity_ref_from(qp1.point_origin) == 100 * m);\nassert(qp2.quantity_ref_from(qp2.point_origin) == 120 * m);\n\nassert(qp2.quantity_from(qp1) == 30 * m);\nassert(qp1.quantity_from(qp2) == -30 * m);\nassert(qp2 - qp1 == 30 * m);\nassert(qp1 - qp2 == -30 * m);\n\nassert(qp1.quantity_from(A) == 120 * m);\nassert(qp1.quantity_from(B) == 110 * m);\nassert(qp1.quantity_from(C) == 100 * m);\nassert(qp1.quantity_from(D) == 90 * m);\nassert(qp1 - A == 120 * m);\nassert(qp1 - B == 110 * m);\nassert(qp1 - C == 100 * m);\nassert(qp1 - D == 90 * m);\n\nassert(qp2.quantity_from(A) == 150 * m);\nassert(qp2.quantity_from(B) == 140 * m);\nassert(qp2.quantity_from(C) == 130 * m);\nassert(qp2.quantity_from(D) == 120 * m);\nassert(qp2 - A == 150 * m);\nassert(qp2 - B == 140 * m);\nassert(qp2 - C == 130 * m);\nassert(qp2 - D == 120 * m);\n\nassert(B - A == 10 * m);\nassert(C - A == 20 * m);\nassert(D - A == 30 * m);\nassert(D - C == 10 * m);\n\nassert(B - B == 0 * m);\n// assert(A - A == 0 * m);  // Compile-time error\n

    Note

    Even though we can't subtract two absolute point origins from each other, it is possible to subtract relative ones or relative and absolute ones.

    "},{"location":"users_guide/framework_basics/the_affine_space/#converting-between-different-representations-of-the-same-point","title":"Converting between different representations of the same point","text":"

    As we might represent the same point with displacement vectors from various origins, the library provides facilities to convert the same point to the quantity_point class templates expressed in terms of different origins.

    For this purpose, we can use either:

    • A converting constructor:

      quantity_point<si::metre, C> qp2C = qp2;\nassert(qp2C.quantity_ref_from(qp2C.point_origin) == 130 * m);\n
    • A dedicated conversion interface:

      quantity_point qp2B = qp2.point_for(B);\nquantity_point qp2A = qp2.point_for(A);\n\nassert(qp2B.quantity_ref_from(qp2B.point_origin) == 140 * m);\nassert(qp2A.quantity_ref_from(qp2A.point_origin) == 150 * m);\n

    It is important to understand that all such translations still describe exactly the same point (e.g., all of them compare equal):

    assert(qp2 == qp2C);\nassert(qp2 == qp2B);\nassert(qp2 == qp2A);\n

    Important

    It is only allowed to convert between various origins defined in terms of the same absolute_point_origin. Even if it is possible to express the same point as a displacement vector from another absolute_point_origin, the library will not provide such a conversion. A custom user-defined conversion function will be needed to add such a functionality.

    Said another way, in the library, there is no way to spell how two distinct absolute_point_origin types relate to each other.

    "},{"location":"users_guide/framework_basics/the_affine_space/#temperature-support","title":"Temperature support","text":"

    Support for temperature quantity points is probably one of the most common examples of relative point origins in action that we use in daily life.

    The SI definition in the library provides a few predefined point origins for this purpose:

    namespace si {\n\ninline constexpr struct absolute_zero final : absolute_point_origin<isq::thermodynamic_temperature> {} absolute_zero;\ninline constexpr auto zeroth_kelvin = absolute_zero;\n\ninline constexpr struct ice_point final : relative_point_origin<point<milli<kelvin>>(273'150)}> {} ice_point;\ninline constexpr auto zeroth_degree_Celsius = ice_point;\n\n}\n\nnamespace usc {\n\ninline constexpr struct zeroth_degree_Fahrenheit final :\n  relative_point_origin<point<mag_ratio<5, 9> * si::degree_Celsius>(-32)> {} zeroth_degree_Fahrenheit;\n\n}\n

    The above is a great example of how point origins can be stacked on top of each other:

    • usc::zeroth_degree_Fahrenheit is defined relative to si::zeroth_degree_Celsius
    • si::zeroth_degree_Celsius is defined relative to si::zeroth_kelvin.

    Note

    Notice that while stacking point origins, we can use different representation types and units for origins and a point. In the above example, the relative point origin for degree Celsius is defined in terms of si::kelvin, while the quantity point for it will use si::degree_Celsius as a unit.

    The temperature point origins defined above are provided explicitly in the respective units' definitions:

    namespace si {\n\ninline constexpr struct kelvin final :\n    named_unit<\"K\", kind_of<isq::thermodynamic_temperature>, zeroth_kelvin> {} kelvin;\ninline constexpr struct degree_Celsius final :\n    named_unit<{u8\"\u2103\", \"`C\"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;\n\n}\n\nnamespace usc {\n\ninline constexpr struct degree_Fahrenheit final :\n    named_unit<{u8\"\u2109\", \"`F\"}, mag_ratio<5, 9> * si::degree_Celsius,\n               zeroth_degree_Fahrenheit> {} degree_Fahrenheit;\n\n}\n

    As it was described above, default_point_origin(R) returns a zeroth_point_origin<QuantitySpec> when a unit does not provide any origin in its definition. As of today, the units of temperature are the only ones in the entire mp-units library that provide such origins.

    Now, let's see how we can benefit from the above definitions. We have quite a few alternatives to choose from here. Depending on our needs or tastes, we can:

    • be explicit about the unit and origin:

      quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q1 = si::zeroth_degree_Celsius + delta<deg_C>(20.5);\nquantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q2{delta<deg_C>(20.5), si::zeroth_degree_Celsius};\nquantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q3{delta<deg_C>(20.5)};\nquantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q4 = point<deg_C>(20.5);\n
    • specify a unit and use its zeroth point origin implicitly:

      quantity_point<si::degree_Celsius> q5 = si::zeroth_degree_Celsius + delta<deg_C>(20.5);\nquantity_point<si::degree_Celsius> q6{delta<deg_C>(20.5), si::zeroth_degree_Celsius};\nquantity_point<si::degree_Celsius> q7{delta<deg_C>(20.5)};\nquantity_point<si::degree_Celsius> q8 = point<deg_C>(20.5);\n
    • benefit from CTAD:

      quantity_point q9 = si::zeroth_degree_Celsius + delta<deg_C>(20.5);\nquantity_point q10{delta<deg_C>(20.5), si::zeroth_degree_Celsius};\nquantity_point q11{delta<deg_C>(20.5)};\nquantity_point q12 = point<deg_C>(20.5);\n

    In all of the above cases, we end up with the quantity_point of the same type and value.

    To play a bit more with temperatures, we can implement a simple room AC temperature controller in the following way:

    constexpr struct room_reference_temp final : relative_point_origin<point<deg_C>(21)> {} room_reference_temp;\nusing room_temp = quantity_point<isq::Celsius_temperature[deg_C], room_reference_temp>;\n\nconstexpr auto step_delta = delta<isq::Celsius_temperature<deg_C>>(0.5);\nconstexpr int number_of_steps = 6;\n\nroom_temp room_ref{};\nroom_temp room_low = room_ref - number_of_steps * step_delta;\nroom_temp room_high = room_ref + number_of_steps * step_delta;\n\nstd::println(\"Room reference temperature: {} ({}, {::N[.2f]})\\n\",\n             room_ref.quantity_from_zero(),\n             room_ref.in(usc::degree_Fahrenheit).quantity_from_zero(),\n             room_ref.in(si::kelvin).quantity_from_zero());\n\nstd::println(\"| {:<18} | {:^18} | {:^18} | {:^18} |\",\n             \"Temperature delta\", \"Room reference\", \"Ice point\", \"Absolute zero\");\nstd::println(\"|{0:=^20}|{0:=^20}|{0:=^20}|{0:=^20}|\", \"\");\n\nauto print_temp = [&](std::string_view label, auto v) {\n  std::println(\"| {:<14} | {:^18} | {:^18} | {:^18:N[.2f]} |\", label,\n               v - room_reference_temp, (v - si::ice_point).in(deg_C), (v - si::absolute_zero).in(deg_C));\n};\n\nprint_temp(\"Lowest\", room_low);\nprint_temp(\"Default\", room_ref);\nprint_temp(\"Highest\", room_high);\n

    The above prints:

    Room reference temperature: 21 \u2103 (69.8 \u2109, 294.15 K)\n\n| Temperature delta  |   Room reference   |     Ice point      |   Absolute zero    |\n|====================|====================|====================|====================|\n| Lowest             |       -3 \u2103        |       18 \u2103        |     291.15 \u2103      |\n| Default            |        0 \u2103        |       21 \u2103        |     294.15 \u2103      |\n| Highest            |        3 \u2103        |       24 \u2103        |     297.15 \u2103      |\n
    "},{"location":"users_guide/framework_basics/the_affine_space/#no-text-output-for-points","title":"No text output for Points","text":"

    The library does not provide a text output for quantity points. The quantity stored inside is just an implementation detail of this type. It is a vector from a specific origin. Without the knowledge of the origin, the vector by itself is useless as we can't determine which point it describes.

    In the current library design, point origin does not provide any text in its definition. Even if we could add such information to the point's definition, we would not know how to output it in the text. There may be many ways to do it. For example, should we prepend or append the origin part to the quantity text?

    For example, the text output of 42 m for a quantity point may mean many things. It may be an offset from the mountain top, sea level, or maybe the center of Mars. Printing 42 m AMSL for altitudes above mean sea level is a much better solution, but the library does not have enough information to print it that way by itself.

    "},{"location":"users_guide/framework_basics/the_affine_space/#the-affine-space-is-about-type-safety","title":"The affine space is about type-safety","text":"

    The following operations are not allowed in the affine space:

    • adding two quantity_point objects
      • It is physically impossible to add positions of home and Denver airports.
    • subtracting a quantity_point from a quantity
      • What would it mean to subtract the DEN airport location from the distance to it?
    • multiplying/dividing a quantity_point with a scalar
      • What is the position of 2 * DEN airport location?
    • multiplying/dividing a quantity_point with a quantity
      • What would multiplying the distance with the DEN airport location mean?
    • multiplying/dividing two quantity_point objects
      • What would multiplying home and DEN airport location mean?
    • mixing quantity_points of different quantity kinds
      • It is physically impossible to subtract time from length.
    • mixing quantity_points of inconvertible quantities
      • What does subtracting a distance point to DEN airport from the Mount Everest base camp altitude mean?
    • mixing quantity_points of convertible quantities but with unrelated origins
      • How do we subtract a point on our trip to CppCon measured relatively to our home location from a point measured relative to the center of the Solar System?

    Important: The affine space improves safety

    The usage of quantity_point and affine space types, in general, improves expressiveness and type-safety of the code we write.

    "},{"location":"users_guide/framework_basics/value_conversions/","title":"Value Conversions","text":""},{"location":"users_guide/framework_basics/value_conversions/#value-preserving-conversions","title":"Value-preserving conversions","text":"
    auto q1 = 5 * km;\nstd::cout << q1.in(m) << '\\n';\nquantity<si::metre, int> q2 = q1;\n

    The second line above converts the current quantity to the one expressed in meters and prints its contents. The third line converts the quantity expressed in kilometers into the one measured in meters.

    In case a user would like to perform an opposite transformation:

    auto q1 = 5 * m;\nstd::cout << q1.in(km) << '\\n';\nquantity<si::kilo<si::metre>, int> q2 = q1;\n

    Both conversions will fail to compile.

    There are two ways to make the above work. The first solution is to use a floating-point representation type:

    auto q1 = 5. * m;\nstd::cout << q1.in(km) << '\\n';\nquantity<si::kilo<si::metre>> q2 = q1;\n

    or

    auto q1 = 5 * m;\nstd::cout << value_cast<double>(q1).in(km) << '\\n';\nquantity<si::kilo<si::metre>> q2 = q1;  // double by default\n

    Important

    The mp-units library follows std::chrono::duration logic and treats floating-point types as value-preserving.

    "},{"location":"users_guide/framework_basics/value_conversions/#value-truncating-conversions","title":"Value-truncating conversions","text":"

    The second solution is to force a truncating conversion:

    auto q1 = 5 * m;\nstd::cout << value_cast<km>(q1) << '\\n';\nquantity<si::kilo<si::metre>, int> q2 = q1.force_in(km);\n

    This explicit cast makes it clear that something unsafe is going on. It is easy to spot in code reviews or while chasing a bug in the source code.

    Note

    q.force_in(U) is just a shortcut to run value_cast<U>(q). There is no difference in behavior between those two interfaces. q.force_in(U) was added for consistency with q.in(U) and q.force_numerical_value_in(U).

    Another place where this cast is useful is when a user wants to convert a quantity with a floating-point representation to the one using an integral one. Again, this is a truncating conversion, so an explicit cast is needed:

    quantity<si::metre, int> q3 = value_cast<int>(3.14 * m);\n

    Info

    It is often OK to use an integral as a representation type, but in general, floating-point types provide better precision and are privileged in the library as they are considered to be value-preserving.

    In some cases, a unit and a representation type should be changed simultaneously. Moreover, sometimes, the order of doing those operations matters. In such cases, the library provides the value_cast<U, Rep>(q) which always returns the most precise result:

    C++23C++20Portable
    inline constexpr struct dim_currency final : base_dimension<\"$\"> {} dim_currency;\ninline constexpr struct currency final : quantity_spec<dim_currency> {} currency;\n\ninline constexpr struct us_dollar final : named_unit<\"USD\", kind_of<currency>> {} us_dollar;\ninline constexpr struct scaled_us_dollar final : named_unit<\"USD_s\", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;\n\nnamespace unit_symbols {\n\ninline constexpr auto USD = us_dollar;\ninline constexpr auto USD_s = scaled_us_dollar;\n\n}  // namespace unit_symbols\n\nusing Price = quantity_point<currency[us_dollar]>;\nusing Scaled = quantity_point<currency[scaled_us_dollar], zeroth_point_origin<currency>, std::int64_t>;\n
    inline constexpr struct dim_currency final : base_dimension<\"$\"> {} dim_currency;\ninline constexpr struct currency final : quantity_spec<currency, dim_currency> {} currency;\n\ninline constexpr struct us_dollar final : named_unit<\"USD\", kind_of<currency>> {} us_dollar;\ninline constexpr struct scaled_us_dollar final : named_unit<\"USD_s\", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;\n\nnamespace unit_symbols {\n\ninline constexpr auto USD = us_dollar;\ninline constexpr auto USD_s = scaled_us_dollar;\n\n}  // namespace unit_symbols\n\nusing Price = quantity_point<currency[us_dollar]>;\nusing Scaled = quantity_point<currency[scaled_us_dollar], zeroth_point_origin<currency>, std::int64_t>;\n
    inline constexpr struct dim_currency final : base_dimension<\"$\"> {} dim_currency;\nQUANTITY_SPEC(currency, dim_currency);\n\ninline constexpr struct us_dollar final : named_unit<\"USD\", kind_of<currency>> {} us_dollar;\ninline constexpr struct scaled_us_dollar final : named_unit<\"USD_s\", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;\n\nnamespace unit_symbols {\n\ninline constexpr auto USD = us_dollar;\ninline constexpr auto USD_s = scaled_us_dollar;\n\n}  // namespace unit_symbols\n\nusing Price = quantity_point<currency[us_dollar]>;\nusing Scaled = quantity_point<currency[scaled_us_dollar], zeroth_point_origin<currency>, std::int64_t>;\n
    using namespace unit_symbols;\nPrice price{12.95 * USD};\nScaled spx = value_cast<USD_s, std::int64_t>(price);\n

    As a shortcut, instead of providing a unit and a representation type to value_cast, you may also provide a Quantity type directly, from which unit and representation type are taken. However, value_cast<Quantity>, still only allows for changes in unit and representation type, but not changing the type of the quantity. For that, you will have to use a quantity_cast instead.

    Overloads are also provided for instances of quantity_point. All variants of value_cast<...>(q) that apply to instances of quantity have a corresponding version applicable to quantity_point, where the point_origin remains untouched, and the cast changes how the \"offset\" from the origin is represented. Specifically, for any quantity_point instance qp, all of the following equivalences hold:

    static_assert(value_cast<Rep>(qp) == quantity_point{value_cast<Rep>(qp.quantity_from(qp.point_origin)), qp.point_origin});\nstatic_assert(value_cast<U>(qp) == quantity_point{value_cast<U>(qp.quantity_from(qp.point_origin)), qp.point_origin});\nstatic_assert(value_cast<U, Rep>(qp) == quantity_point{value_cast<U, Rep>(qp.quantity_from(qp.point_origin)), qp.point_origin});\nstatic_assert(value_cast<Q>(qp) == quantity_point{value_cast<Q>(qp.quantity_from(qp.point_origin)), qp.point_origin});\n

    Furthermore, there is one additional overload value_cast<ToQP>(qp). This overload permits to additionally replace the point_origin with another compatible one, while still representing the same point in the affine space. Thus, it is roughly equivalent to value_cast<ToQP::unit, ToQP::rep>(qp).point_for(ToQP::point_origin). In contrast to a separate value_cast followed by point_for (or vice-versa), the combined value_cast tries to choose the order of the individual conversion steps in a way to avoid both overflow and unnecessary loss of precision. Overflow is a risk because the change of origin point may require an addition of a potentially large offset (the difference between the origin points), which may well be outside the range of one or both quantity types.

    "},{"location":"users_guide/framework_basics/value_conversions/#value-conversions-summary","title":"Value conversions summary","text":"

    The table below provides all the value conversion functions that may be run on x being the instance of either quantity or quantity_point:

    Forcing Representation Unit Member function Non-member function No Same u x.in(u) No T Same x.in<T>() No T u x.in<T>(u) Yes Same u x.force_in(u) value_cast<u>(x) Yes T Same x.force_in<T>() value_cast<T>(x) Yes T u x.force_in<T>(u) value_cast<u, T>(x) or value_cast<T, u>(x)"},{"location":"users_guide/systems/strong_angular_system/","title":"Strong Angular System","text":""},{"location":"users_guide/systems/strong_angular_system/#some-background-information","title":"Some background information","text":"

    As per today's SI, both radian and steradian are dimensionless. This forces the convention to set the angle 1 radian equal to the number 1 within equations (similar to what natural units system does for c constant).

    Following Wikipedia:

    Wikipedia: Radian - Dimensional analysis

    Giacomo Prando says \"the current state of affairs leads inevitably to ghostly appearances and disappearances of the radian in the dimensional analysis of physical equations.\" For example, a mass hanging by a string from a pulley will rise or drop by \\(y=r\u03b8\\) centimeters, where \\(r\\) is the radius of the pulley in centimeters and \\(\u03b8\\) is the angle the pulley turns in radians. When multiplying \\(r\\) by \\(\u03b8\\) the unit of radians disappears from the result. Similarly in the formula for the angular velocity of a rolling wheel, \\(\u03c9=v/r\\), radians appear in the units of \\(\u03c9\\) but not on the right hand side. Anthony French calls this phenomenon \"a perennial problem in the teaching of mechanics\". Oberhofer says that the typical advice of ignoring radians during dimensional analysis and adding or removing radians in units according to convention and contextual knowledge is \"pedagogically unsatisfying\".

    At least a dozen scientists have made proposals to treat the radian as a base unit of measure defining its own dimension of \"angle\", as early as 1936 and as recently as 2022. This would bring the advantages of a physics-based, consistent, and logically-robust unit system, with unambiguous units for all physical quantities. At the same time the only notable changes for typical end-users would be: improved units for the quantities torque, angular momentum, and moment of inertia.

    Paul Quincey in his proposal \"Angles in the SI: a detailed proposal for solving the problem\" states:

    Paul Quincey: Angles in the SI: a detailed proposal for solving the problem

    The familiar units assigned to some angular quantities are based on equations that have adopted the radian convention, and so are missing rads that would be present if the complete equation is used. The physically-correct units are those with the rads reinstated. Numerical values would not change, and the physical meanings of all quantities would also be unaffected.

    He proposes the following changes:

    • The radian would become either a new base unit or a 'complementary' unit
    • The steradian would become a derived unit equal to \\(1\\:rad^2\\)
    • The SI units for

      • Torque would change from \\(N\\:m\\) (\\(=J\\)) to \\(J\\:rad^{-1}\\)
      • Angular momentum would change from \\(J\\:s\\) to \\(J\\:s\\:rad^{-1}\\) (i.e. \\(J/(rad/s)\\))
      • Moment of inertia would change from \\(kg\\:m^2\\) to \\(kg\\:m^2\\:rad^{-2}\\) (i.e. \\(J/(rad/s)^2\\))
    • The option to omit the radian from the SI units for angle, angular velocity, angular frequency, angular acceleration, and angular wavenumber would be removed, the only correct SI units being \\(rad\\), \\(rad/s\\), \\(rad/s\\), \\(rad/s^2\\) and \\(rad/m\\) respectively.

    Paul Quincey summarizes that with the above in action:

    Paul Quincey: Angles in the SI: a detailed proposal for solving the problem

    However, the physical clarity this would build into the SI should be recognised very quickly. The units would tell us that \\(torque \\times angle = energy\\), and \\(angular\\:momentum \\times angle = action\\), for example, in the same way that they do for \\(force \\times distance = energy\\), \\(linear\\:momentum \\times distance = action\\), and \\(radiant\\:intensity \\times solid\\:angle = radiant\\:flux\\). Dimensional analysis could be used to its full extent. Software involving angular quantities would be rationalised. Arguments about the correct units for frequency and angular frequency, and the meaning of the unit \\(Hz\\), could be left behind. The explanation of these changes would be considerably easier and more rewarding than explaining how a kilogram-sized mass can be measured in terms of the Planck constant.

    "},{"location":"users_guide/systems/strong_angular_system/#angular-quantities-in-the-si","title":"Angular quantities in the SI","text":"

    Even though the SI somehow ignores the dimensionality of angle:

    SI Brochure

    Plane and solid angles, when expressed in radians and steradians respectively, are in effect also treated within the SI as quantities with the unit one. The symbols \\(rad\\) and \\(sr\\) are written explicitly where appropriate, in order to emphasize that, for radians or steradians, the quantity being considered is, or involves the plane angle or solid angle respectively. For steradians it emphasizes the distinction between units of flux and intensity in radiometry and photometry for example. However, it is a long-established practice in mathematics and across all areas of science to make use of \\(rad = 1\\) and \\(sr = 1\\). For historical reasons the radian and steradian are treated as derived units.

    It also explicitly states:

    SI Brochure

    The SI unit of frequency is hertz, the SI unit of angular velocity and angular frequency is radian per second, and the SI unit of activity is becquerel, implying counts per second. Although it is formally correct to write all three of these units as the reciprocal second, the use of the different names emphasizes the different nature of the quantities concerned. It is especially important to carefully distinguish frequencies from angular frequencies, because by definition their numerical values differ by a factor of \\(2\\pi\\). Ignoring this fact may cause an error of \\(2\\pi\\). Note that in some countries, frequency values are conventionally expressed using \u201ccycle/s\u201d or \u201ccps\u201d instead of the SI unit \\(Hz\\), although \u201ccycle\u201d and \u201ccps\u201d are not units in the SI. Note also that it is common, although not recommended, to use the term frequency for quantities expressed in \\(rad/s\\). Because of this, it is recommended that quantities called \u201cfrequency\u201d, \u201cangular frequency\u201d, and \u201cangular velocity\u201d always be given explicit units of \\(Hz\\) or \\(rad/s\\) and not \\(s^{-1}\\).

    "},{"location":"users_guide/systems/strong_angular_system/#strong-angular-extensions-in-the-library","title":"Strong Angular extensions in the library","text":"

    The mp-units library strives to define physically-correct quantities and their units to provide maximum help to its users. As treating angle as a dimensional quantity can lead to many \"trivial\" mistakes in dimensional analysis and calculation, it was decided to provide additional experimental systems of quantities and units that follow the above approach and treat angle as a base quantity with a base unit of radian and solid angle as its derived quantity.

    As those (at least for now) are not a part of SI, the plain angle and solid angle definitions can be found in a dedicated angular system. Those definitions are also used in the isq_angle system of quantities to make the recipes for angle-based quantities like torque or angular velocity physically correct:

    using namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\nusing mp_units::angular::unit_symbols::deg;\nusing mp_units::angular::unit_symbols::rad;\n\nconst quantity lever = isq_angle::position_vector(20 * cm);\nconst quantity force = isq_angle::force(500 * N);\nconst quantity angle = isq_angle::angular_measure(90. * deg);\nconst quantity torque = isq_angle::torque(lever * force * angular::sin(angle) / (1 * isq_angle::cotes_angle));\n\nstd::cout << \"Applying a perpendicular force of \" << force << \" to a \" << lever << \" long lever results in \"\n          << torque.in(N * m / rad) << \" of torque.\\n\";\n

    The above program prints:

    Applying a perpendicular force of 500 N to a 20 cm long lever results in 100 N m/rad of torque.\n

    Note

    cotes_angle is a constant which represents an angle with the value of exactly 1 radian. You can find more information about this constant in Quincey.

    Try it on Compiler Explorer

    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/","title":"Interoperability with Other Libraries","text":"

    mp-units makes it easy to cooperate with similar entities of other libraries. No matter if we want to provide interoperability with a simple home-grown strongly typed wrapper type (e.g., Meter, Timestamp, ...) or with a feature-rich quantities and units library, we have to provide specializations of:

    • a quantity_like_traits for external quantity-like type,
    • a quantity_point_like_traits for external quantity_point-like type.
    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/#specifying-a-conversion-kind","title":"Specifying a conversion kind","text":"

    Before we delve into the template specialization details, let's first decide if we want the conversions to happen implicitly or if explicit ones would be a better choice. Or maybe the conversion should be implicit in one direction only (e.g., into mp-units abstractions) while the explicit conversions in the other direction should be preferred?

    There is no one unified answer to the above questions. Everything depends on the use case.

    Typically, the implicit conversions are allowed in cases where:

    • both abstractions mean exactly the same, and interchanging them in the code should not change its logic,
    • there is no significant runtime overhead introduced by such a conversion (e.g., no need for dynamic allocation or copying of huge internal buffers),
    • the target type of the conversion provides the same or better safety to the users,
    • we prefer the simplicity of implicit conversions over safety during the (hopefully short) transition period of refactoring our code base from the usage of one library to the other.

    In all other scenarios, we should probably enforce explicit conversions.

    The kinds of inter-library conversions can be easily configured in partial specializations of conversion traits in the mp-units library. Conversion traits should provide a static data member convertible to bool. If the value is true, then the conversion is explicit. Otherwise, if the value is false, implicit conversions will be allowed. The names of the flags are as follows:

    • explicit_import to describe conversion from the external entity to the one in this library (import case),
    • explicit_export to describe conversion from the entity in this library to the external one (export case).
    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/#quantities-conversions","title":"Quantities conversions","text":"

    For example, let's assume that some company has its own Meter strong-type wrapper:

    struct Meter {\n  int value;\n};\n

    As every usage of Meter is at least as good and safe as the usage of quantity<si::metre, int>, and as there is no significant runtime performance penalty, we would like to allow the conversion to mp_units::quantity to happen implicitly.

    On the other hand, the quantity type is much safer than the Meter, and that is why we would prefer to see the opposite conversions stated explicitly in our code.

    To enable such interoperability, we must define a partial specialization of the quantity_like_traits<T> type trait. Such specialization should provide:

    • reference static data member that provides the quantity reference (e.g., unit),
    • rep type that specifies the underlying storage type,
    • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity type should happen explicitly (if true),
    • explicit_export static data member convertible to bool that specifies that the conversion from a quantity type to T should happen explicitly (if true),
    • to_numerical_value(T) static member function returning a quantity's raw value of rep type,
    • from_numerical_value(rep) static member function returning T.

    For example, for our Meter type, we could provide the following:

    template<>\nstruct mp_units::quantity_like_traits<Meter> {\n  static constexpr auto reference = si::metre;\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = true;\n  using rep = decltype(Meter::value);\n  static constexpr rep to_numerical_value(Meter m) { return m.value; }\n  static constexpr Meter from_numerical_value(rep v) { return Meter{v}; }\n};\n

    After that, we can check that the QuantityLike concept is satisfied:

    static_assert(mp_units::QuantityLike<Meter>);\n

    and we can write the following:

    void print(Meter m) { std::cout << m.value << \" m\\n\"; }\n\nint main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si::unit_symbols;\n\n  Meter height{42};\n\n  // implicit conversions\n  quantity h1 = height;\n  quantity<isq::height[m], int> h2 = height;\n\n  std::cout << h1 << \"\\n\";\n  std::cout << h2 << \"\\n\";\n\n  // explicit conversions\n  print(Meter(h1));\n  print(Meter(h2));\n}\n

    Note

    No matter if we decide to use implicit or explicit conversions, the mp-units will not allow unsafe operations to happen.

    If we extend the above example with unsafe conversions, the code will not compile, and we will have to fix the issues first before the conversion may be performed:

    UnsafeFixed
    quantity<isq::height[m]> h3 = height;\nquantity<isq::height[mm], int> h4 = height;\nquantity<isq::height[km], int> h5 = height;  // Compile-time error (1)\n\nstd::cout << h3 << \"\\n\";\nstd::cout << h4 << \"\\n\";\nstd::cout << h5 << \"\\n\";\n\nprint(Meter(h3));                            // Compile-time error (2)\nprint(Meter(h4));                            // Compile-time error (3)\nprint(Meter(h5));\n
    1. Truncation of value while converting from meters to kilometers.
    2. Conversion of double to int is not value-preserving.
    3. Truncation of value while converting from millimeters to meters.
    quantity<isq::height[m]> h3 = height;\nquantity<isq::height[mm], int> h4 = height;\nquantity<isq::height[km], int> h5 = quantity{height}.force_in(km);\n\nstd::cout << h3 << \"\\n\";\nstd::cout << h4 << \"\\n\";\nstd::cout << h5 << \"\\n\";\n\nprint(Meter(value_cast<int>(h3)));\nprint(Meter(h4.force_in(m)));\nprint(Meter(h5));\n
    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/#quantity-points-conversions","title":"Quantity points conversions","text":"

    To play with quantity point conversions, let's assume that we have a Timestamp strong type in our codebase, and we would like to start using mp-units to work with this abstraction.

    struct Timestamp {\n  int seconds;\n};\n

    As we described in The Affine Space chapter, timestamps should be modeled as quantity points rather than regular quantities.

    To allow the conversion between our custom Timestamp type and the quantity_point class template we need to provide the following in the partial specialization of the quantity_point_like_traits<T> type trait:

    • reference static data member that provides the quantity point reference (e.g., unit),
    • point_origin static data member that specifies the absolute point, which is the beginning of our measurement scale for our points,
    • rep type that specifies the underlying storage type,
    • explicit_import static data member convertible to bool that specifies that the conversion from T to a quantity type should happen explicitly (if true),
    • explicit_export static data member convertible to bool that specifies that the conversion from a quantity type to T should happen explicitly (if true),
    • to_numerical_value(T) static member function returning a raw value of the quantity being the offset of the point from the origin,
    • from_numerical_value(rep) static member function returning T.

    For example, for our Timestamp type, we could provide the following:

    template<>\nstruct mp_units::quantity_point_like_traits<Timestamp> {\n  static constexpr auto reference = si::second;\n  static constexpr auto point_origin = default_point_origin(reference);\n  static constexpr bool explicit_import = false;\n  static constexpr bool explicit_export = true;\n  using rep = decltype(Timestamp::seconds);\n  static constexpr rep to_numerical_value(Timestamp ts) { return ts.seconds; }\n  static constexpr Timestamp from_numerical_value(rep v) { return Timestamp(v); }\n};\n

    After that, we can check that the QuantityPointLike concept is satisfied:

    static_assert(mp_units::QuantityPointLike<Timestamp>);\n

    and we can write the following:

    void print(Timestamp ts) { std::cout << ts.seconds << \" s\\n\"; }\n\nint main()\n{\n  using namespace mp_units;\n  using namespace mp_units::si::unit_symbols;\n\n  Timestamp ts{42};\n\n  // implicit conversion\n  quantity_point qp = ts;\n\n  std::cout << qp.quantity_from_zero() << \"\\n\";\n\n  // explicit conversion\n  print(Timestamp(qp));\n}\n
    "},{"location":"users_guide/use_cases/interoperability_with_other_libraries/#interoperability-with-the-c-standard-library","title":"Interoperability with the C++ Standard Library","text":"

    In the C++ standard library, we have two types that handle quantities and model the affine space. Those are:

    • std::chrono::duration - specifies quantities of time,
    • std::chrono::time_point - specifies quantity points of time.

    The mp-units library comes with built-in interoperability with those types. It is enough to include the mp-units/systems/si/chrono.h file to benefit from it. This file provides:

    • partial specializations of quantity_like_traits and quantity_point_like_traits that provide support for implicit conversions between std and mp_units types in both directions,
    • chrono_point_origin<Clock> point origin for std clocks,
    • to_chrono_duration and to_chrono_time_point dedicated conversion functions that result in types exactly representing mp-units abstractions.

    Important

    Only a quantity_point that uses chrono_point_origin<Clock> as its origin can be converted to the std::chrono abstractions:

    inline constexpr struct ts_origin final : relative_point_origin<chrono_point_origin<system_clock> + 1 * h> {} ts_origin;\ninline constexpr struct my_origin final : absolute_point_origin<isq::time> {} my_origin;\n\nquantity_point qp1 = sys_seconds{1s};\nauto tp1 = to_chrono_time_point(qp1);  // OK\n\nquantity_point qp2 = chrono_point_origin<system_clock> + 1 * s;\nauto tp2 = to_chrono_time_point(qp2);  // OK\n\nquantity_point qp3 = ts_origin + 1 * s;\nauto tp3 = to_chrono_time_point(qp3);  // OK\n\nquantity_point qp4 = my_origin + 1 * s;\nauto tp4 = to_chrono_time_point(qp4);  // Compile-time Error (1)\n\nquantity_point qp5{1 * s};\nauto tp5 = to_chrono_time_point(qp5);  // Compile-time Error (2)\n
    1. my_origin is not defined in terms of chrono_point_origin<Clock>.
    2. zeroth_point_origin is not defined in terms of chrono_point_origin<Clock>.

    Here is an example of how interoperability described in this chapter can be used in practice:

    using namespace std::chrono;\n\nsys_seconds ts_now = floor<seconds>(system_clock::now());\n\nquantity_point start_time = ts_now;\nquantity speed = 925. * km / h;\nquantity distance = 8111. * km;\nquantity flight_time = distance / speed;\nquantity_point exp_end_time = start_time + flight_time;\n\nsys_seconds ts_end = value_cast<int>(exp_end_time.in(s));\n\nauto curr_time = zoned_time(current_zone(), ts_now);\nauto mst_time = zoned_time(\"America/Denver\", ts_end);\n\nstd::cout << \"Takeoff: \" << curr_time << \"\\n\";\nstd::cout << \"Landing: \" << mst_time << \"\\n\";\n

    The above may print the following output:

    Takeoff: 2023-11-18 13:20:54 UTC\nLanding: 2023-11-18 15:07:01 MST\n
    "},{"location":"users_guide/use_cases/wide_compatibility/","title":"Wide Compatibility","text":"

    The mp-units allows us to implement nice and terse code targeting a specific C++ version and configuration. Such code is easy to write and understand but might not be portable to some older environments.

    However, sometimes, we want to develop code that can be compiled on a wide range of various compilers and configurations. This is why the library also exposes and uses special preprocessor macros that can be used to ensure the wide compatibility of our code.

    Note

    Those macros are used in our short example applications as those are meant to be built on all of the supported compilers. Some still do not support std::format, C++ modules, or C++ versions newer than C++20.

    "},{"location":"users_guide/use_cases/wide_compatibility/#various-compatibility-options","title":"Various compatibility options","text":"

    Depending on your compiler's conformance, you can choose to use any of the below styles to write your code using mp-units:

    C++23C++20C++20 with header filesC++20 with header files + libfmtWide Compatibility
    #include <format>\n#include <iostream>\nimport mp_units;\n\n// ...\n\ninline constexpr struct horizontal_length final : quantity_spec<isq::length> {} horizontal_length;\n\n// ...\n\nstd::cout << std::format(...) << \"\\n\";\n
    #include <format>\n#include <iostream>\nimport mp_units;\n\n// ...\n\ninline constexpr struct horizontal_length final : quantity_spec<horizontal_length, isq::length> {} horizontal_length;\n\n// ...\n\nstd::cout << std::format(...) << \"\\n\";\n
    #include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <format>\n#include <iostream>\n\n// ...\n\ninline constexpr struct horizontal_length final : quantity_spec<horizontal_length, isq::length> {} horizontal_length;\n\n// ...\n\nstd::cout << std::format(...) << \"\\n\";\n
    #include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#include <fmt/format.h>\n#include <iostream>\n\n// ...\n\ninline constexpr struct horizontal_length final : quantity_spec<horizontal_length, isq::length> {} horizontal_length;\n\n// ...\n\nstd::cout << fmt::format(...) << \"\\n\";\n
    #include <iostream>\n#include <mp-units/ext/format.h>\n#ifdef MP_UNITS_MODULES\n#include <mp-units/compat_macros.h>\nimport mp_units;\n#else\n#include <mp-units/format.h>\n#include <mp-units/ostream.h>\n#include <mp-units/systems/international.h>\n#include <mp-units/systems/isq.h>\n#include <mp-units/systems/si.h>\n#endif\n\n// ...\n\nQUANTITY_SPEC(horizontal_length, isq::length);\n\n// ...\n\nstd::cout << MP_UNITS_STD_FMT::format(...) << \"\\n\";\n

    Tip

    Depending on your preferences, you can either write:

    • terse code directly targeting your specific compiler's abilities,
    • verbose code using preprocessor branches and macros that provide the widest compatibility across various compilers.
    "},{"location":"users_guide/use_cases/wide_compatibility/#compatibility-macros","title":"Compatibility macros","text":"

    This chapter describes only the most essential tools the mp-units users need. All the compatibility macros can be found in the mp-units/compat_macros.h header file.

    Tip

    The mp-units/compat_macros.h header file is implicitly included when we use \"legacy\" headers in our translation units. However, it has to be explicitly included when we use C++20 modules, as those do not propagate preprocessor macros.

    "},{"location":"users_guide/use_cases/wide_compatibility/#QUANTITY_SPEC","title":"QUANTITY_SPEC(name, ...)","text":"

    Quantity specification definitions benefit from an explicit object parameter added in C++23 to remove the need for CRTP idiom, which significantly simplifies the code.

    This macro benefits from the new C++23 feature if available. Otherwise, it uses the CRTP idiom under the hood.

    "},{"location":"users_guide/use_cases/wide_compatibility/#mp_units_std_fmt","title":"MP_UNITS_STD_FMT","text":"

    Some of the supported compilers do not support std::format and related tools. Also, despite using a conformant compiler, some projects still choose to use fmtlib as their primary formatting facility (e.g., to benefit from additional features provided with the library).

    This macro resolves to either the std or fmt namespace, depending on the value of MP_UNITS_API_STD_FORMAT CMake option.

    To include the header files of the underlying text formatting framework, the following include should be used:

    #include <mp-units/ext/format.h>\n
    "},{"location":"users_guide/use_cases/wide_compatibility/#contracts","title":"Contracts","text":"

    The mp-units library internally does contract checking by default. It can be disabled with a Conan or CMake option. However, when enabled, it can use either gsl-lite or ms-gsl. To write a code that is independent from the underlying framework, the following preprocessor macros are exposed:

    • MP_UNITS_EXPECTS(expr)
    • MP_UNITS_EXPECTS_DEBUG(expr)
    • MP_UNITS_ASSERT(expr)
    • MP_UNITS_ASSERT_DEBUG(expr)

    Their meaning is consistent with respective gsl-lite.

    Also, to include the header files of the underlying framework, the following include should be used:

    #include <mp-units/ext/contracts.h>\n
    "},{"location":"users_guide/use_cases/working_with_legacy_interfaces/","title":"Working with Legacy interfaces","text":"

    In case we are working with a legacy/unsafe interface, we may need to extract the numerical value of a quantity and pass it to some third-party legacy unsafe interfaces.

    In such situations we can use .numerical_value_in(Unit) member function:

    void legacy_check_speed_limit(int speed_in_km_per_h);\n
    legacy_check_speed_limit((180 * km / (2 * h)).numerical_value_in(km / h));\n

    Such a getter will explicitly enforce the usage of a correct unit required by the underlying interface, which reduces a significant number of safety-related issues.

    The above code will not compile in case value truncation may happen. To solve the issue, we need to either use a value-preserving representation type or force the truncating conversion with .force_numerical_value_in(Unit):

    legacy_check_speed_limit((140 * mi / (2 * h)).force_numerical_value_in(km / h));\n

    The getters mentioned above always return by value as a quantity value conversion may be required to adjust it to the target unit. In case a user needs a reference to the underlying storage .numerical_value_ref_in(Unit) should be used:

    void legacy_set_speed_limit(int* speed_in_km_per_h) { *speed_in_km_per_h = 100; }\n
    quantity<km / h, int> speed_limit;\nlegacy_set_speed_limit(&speed_limit.numerical_value_ref_in(km / h));\n

    This member function again requires a target unit to enforce safety. This overload does not participate in overload resolution if the provided unit has a different scaling factor than the current one.

    "},{"location":"blog/archive/2024/","title":"2024","text":""},{"location":"blog/archive/2023/","title":"2023","text":""},{"location":"blog/category/metrology/","title":"Metrology","text":""},{"location":"blog/category/releases/","title":"Releases","text":""},{"location":"blog/category/wg21-updates/","title":"WG21 Updates","text":""},{"location":"blog/page/2/","title":"Blog","text":""},{"location":"blog/archive/2024/page/2/","title":"2024","text":""},{"location":"users_guide/examples/tags_index/","title":"Tags Index","text":"

    Note

    mp-units usage example applications are meant to be built on all of the supported compilers. This is why they benefit from the Wide Compatibility mode.

    Tip

    All usage examples in this chapter are categorized with appropriate tags to simplify navigation and search of relevant code. You can either read all the examples one-by-one in the order provided by the documentation authors or, thanks to the tagging system, jump straight to the example that is the most interesting for you.

    "},{"location":"users_guide/examples/tags_index/#affine-space","title":"Affine Space","text":"
    • hw_voltage
    "},{"location":"users_guide/examples/tags_index/#cgs-system","title":"CGS System","text":"
    • avg_speed
    "},{"location":"users_guide/examples/tags_index/#embedded","title":"Embedded","text":"
    • hw_voltage
    "},{"location":"users_guide/examples/tags_index/#international-system","title":"International System","text":"
    • avg_speed
    • hello_units
    "},{"location":"users_guide/examples/tags_index/#physical-constants","title":"Physical Constants","text":"
    • si_constants
    "},{"location":"users_guide/examples/tags_index/#text-formatting","title":"Text Formatting","text":"
    • hello_units
    • hw_voltage
    • si_constants
    "}]} \ No newline at end of file diff --git a/HEAD/sitemap.xml b/HEAD/sitemap.xml index e7a88de63..36f8604b5 100644 --- a/HEAD/sitemap.xml +++ b/HEAD/sitemap.xml @@ -2,270 +2,270 @@ https://mpusz.github.io/mp-units/HEAD/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/api_reference/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/release_notes/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/appendix/glossary/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/appendix/references/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2023/09/24/whats-new-in-mp-units-20/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2023/12/09/mp-units-210-released/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/06/14/mp-units-220-released/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/09/27/mp-units-230-released/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/11/05/mp-units-240-released/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/10/07/international-system-of-quantities-isq-part-1---introduction/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/10/14/international-system-of-quantities-isq-part-2---problems-when-isq-is-not-used/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/10/21/international-system-of-quantities-isq-part-3---modeling-isq/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/10/28/international-system-of-quantities-isq-part-4---implementing-isq/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/11/04/international-system-of-quantities-isq-part-5---benefits/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/11/11/international-system-of-quantities-isq-part-6---challenges/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2023/11/12/report-from-the-kona-2023-iso-c-committee-meeting/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/07/02/report-from-the-st-louis-2024-iso-c-committee-meeting/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/2024/04/15/report-from-the-tokyo-2024-iso-c-committee-meeting/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/getting_started/contributing/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/getting_started/cpp_compiler_support/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/getting_started/faq/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/getting_started/installation_and_usage/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/getting_started/introduction/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/getting_started/look_and_feel/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/getting_started/project_structure/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/getting_started/quick_start/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/terms_and_definitions/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/examples/avg_speed/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/examples/hello_units/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/examples/hw_voltage/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/examples/si_constants/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/character_of_a_quantity/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/concepts/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/design_overview/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/dimensionless_quantities/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/faster_than_lightspeed_constants/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/generic_interfaces/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/interface_introduction/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/obtaining_metadata/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/quantity_arithmetics/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/simple_and_typed_quantities/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/systems_of_quantities/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/systems_of_units/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/text_output/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/the_affine_space/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/framework_basics/value_conversions/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/systems/introduction/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/systems/isq/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/systems/natural_units/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/systems/si/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/systems/strong_angular_system/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/use_cases/extending_the_library/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/use_cases/interoperability_with_other_libraries/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/use_cases/pure_dimensional_analysis/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/use_cases/using_custom_representation_types/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/use_cases/wide_compatibility/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/use_cases/working_with_legacy_interfaces/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/archive/2024/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/archive/2023/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/category/metrology/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/category/releases/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/category/wg21-updates/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/page/2/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/blog/archive/2024/page/2/ - 2024-11-24 + 2024-11-28 https://mpusz.github.io/mp-units/HEAD/users_guide/examples/tags_index/ - 2024-11-24 + 2024-11-28 \ No newline at end of file diff --git a/HEAD/sitemap.xml.gz b/HEAD/sitemap.xml.gz index 5dac622d3..c62413d99 100644 Binary files a/HEAD/sitemap.xml.gz and b/HEAD/sitemap.xml.gz differ diff --git a/HEAD/users_guide/examples/avg_speed/index.html b/HEAD/users_guide/examples/avg_speed/index.html index 60d7cd54e..34005c39d 100644 --- a/HEAD/users_guide/examples/avg_speed/index.html +++ b/HEAD/users_guide/examples/avg_speed/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/users_guide/examples/hello_units/index.html b/HEAD/users_guide/examples/hello_units/index.html index 9d34e34eb..9dc6c7a28 100644 --- a/HEAD/users_guide/examples/hello_units/index.html +++ b/HEAD/users_guide/examples/hello_units/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/users_guide/examples/hw_voltage/index.html b/HEAD/users_guide/examples/hw_voltage/index.html index 74c0eb740..db671f744 100644 --- a/HEAD/users_guide/examples/hw_voltage/index.html +++ b/HEAD/users_guide/examples/hw_voltage/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/users_guide/examples/si_constants/index.html b/HEAD/users_guide/examples/si_constants/index.html index 4afbf24f7..d0f63698c 100644 --- a/HEAD/users_guide/examples/si_constants/index.html +++ b/HEAD/users_guide/examples/si_constants/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/users_guide/examples/tags_index/index.html b/HEAD/users_guide/examples/tags_index/index.html index 2e207a0f3..92b7d92c3 100644 --- a/HEAD/users_guide/examples/tags_index/index.html +++ b/HEAD/users_guide/examples/tags_index/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/character_of_a_quantity/index.html b/HEAD/users_guide/framework_basics/character_of_a_quantity/index.html index 87b82f391..de68a95a9 100644 --- a/HEAD/users_guide/framework_basics/character_of_a_quantity/index.html +++ b/HEAD/users_guide/framework_basics/character_of_a_quantity/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/concepts/index.html b/HEAD/users_guide/framework_basics/concepts/index.html index 242c8969b..4fd20478d 100644 --- a/HEAD/users_guide/framework_basics/concepts/index.html +++ b/HEAD/users_guide/framework_basics/concepts/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/design_overview/index.html b/HEAD/users_guide/framework_basics/design_overview/index.html index 802de994b..fefab90b4 100644 --- a/HEAD/users_guide/framework_basics/design_overview/index.html +++ b/HEAD/users_guide/framework_basics/design_overview/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/dimensionless_quantities/index.html b/HEAD/users_guide/framework_basics/dimensionless_quantities/index.html index 0790ce1ee..9d1a8881d 100644 --- a/HEAD/users_guide/framework_basics/dimensionless_quantities/index.html +++ b/HEAD/users_guide/framework_basics/dimensionless_quantities/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units @@ -2428,7 +2428,7 @@

    Predefined units of the

    Important: one is an identity

    A unit one is special in the entire type system of units as it is considered to be -an identity operand in the unit expression templates. +an identity operand in the unit symbolic expressions. This means that, for example:

    static_assert(one * one == one);
     static_assert(one * si::metre == si::metre);
    diff --git a/HEAD/users_guide/framework_basics/faster_than_lightspeed_constants/index.html b/HEAD/users_guide/framework_basics/faster_than_lightspeed_constants/index.html
    index 8409ae94b..035132450 100644
    --- a/HEAD/users_guide/framework_basics/faster_than_lightspeed_constants/index.html
    +++ b/HEAD/users_guide/framework_basics/faster_than_lightspeed_constants/index.html
    @@ -25,7 +25,7 @@
             
           
           
    -      
    +      
         
         
           
    @@ -33,7 +33,7 @@
           
         
         
    -      
    +      
           
             
             
    @@ -255,7 +255,7 @@
             
       
    - +
    mpusz/mp-units @@ -426,7 +426,7 @@
    - +
    mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/generic_interfaces/index.html b/HEAD/users_guide/framework_basics/generic_interfaces/index.html index 6b257c080..25ac0540b 100644 --- a/HEAD/users_guide/framework_basics/generic_interfaces/index.html +++ b/HEAD/users_guide/framework_basics/generic_interfaces/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
    - +
    -

    Expression templates

    +

    Symbolic expressions

    The previous chapter provided a rationale for not having predefined types for derived entities. In many libraries, such an approach results in long and unreadable compilation errors, as framework-generated types are typically far from being easy to read and understand.

    The mp-units library greatly improves the user experience by extensively using -expression templates. Such expressions are used consistently throughout the entire library +symbolic expressions. Such expressions are used consistently throughout the entire library to describe the results of:

    • dimension equation - the result is put into @@ -2445,7 +2445,7 @@

      Identities
      constexpr auto my_unit = inverse(second);
       

    -

    Both cases will result in the same expression template being generated and put into the wrapper +

    Both cases will result in the same symbolic expression being generated and put into the wrapper class template.

    Supported operations and their results

    @@ -2513,9 +2513,9 @@

    Supported operations and their r -

    Simplifying the resulting expression templates

    +

    Simplifying the resulting symbolic expressions

    To limit the length and improve the readability of generated types, there are many rules to simplify -the resulting expression template.

    +the resulting symbolic expression.

    1. Ordering

      @@ -2596,7 +2596,7 @@

      Simplifying the resultin km and Mpc are units of length.

      Also, to prevent possible issues in compile-time logic, all of the library's entities must be marked final. This prevents the users to derive own strong types from them, which would -prevent expression template simplification of equivalent entities.

      +prevent symbolic expression simplification of equivalent entities.

    2. Repacking

      diff --git a/HEAD/users_guide/framework_basics/obtaining_metadata/index.html b/HEAD/users_guide/framework_basics/obtaining_metadata/index.html index 23d27fb2b..b0ee8fd30 100644 --- a/HEAD/users_guide/framework_basics/obtaining_metadata/index.html +++ b/HEAD/users_guide/framework_basics/obtaining_metadata/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/quantity_arithmetics/index.html b/HEAD/users_guide/framework_basics/quantity_arithmetics/index.html index 0e3802de7..14c87fa9c 100644 --- a/HEAD/users_guide/framework_basics/quantity_arithmetics/index.html +++ b/HEAD/users_guide/framework_basics/quantity_arithmetics/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/simple_and_typed_quantities/index.html b/HEAD/users_guide/framework_basics/simple_and_typed_quantities/index.html index a45f67c8d..fe7764b8e 100644 --- a/HEAD/users_guide/framework_basics/simple_and_typed_quantities/index.html +++ b/HEAD/users_guide/framework_basics/simple_and_typed_quantities/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/systems_of_quantities/index.html b/HEAD/users_guide/framework_basics/systems_of_quantities/index.html index fb99b04f3..a043beead 100644 --- a/HEAD/users_guide/framework_basics/systems_of_quantities/index.html +++ b/HEAD/users_guide/framework_basics/systems_of_quantities/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/systems_of_units/index.html b/HEAD/users_guide/framework_basics/systems_of_units/index.html index 85f1cfc08..d09b5941f 100644 --- a/HEAD/users_guide/framework_basics/systems_of_units/index.html +++ b/HEAD/users_guide/framework_basics/systems_of_units/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units @@ -2361,7 +2361,7 @@

      Many shades of the same unitNote

      The above code example may give the impression that the order of components in a derived unit is determined by the multiplication order. This is not the case. As stated in -Simplifying the resulting expression templates, +Simplifying the resulting symbolic expressions, to be able to reason about and simplify units, the library needs to order them in an appropriate order. This will affect the order of components in a resulting type and text output.

      diff --git a/HEAD/users_guide/framework_basics/text_output/index.html b/HEAD/users_guide/framework_basics/text_output/index.html index d76fb136a..99f4fb9a0 100644 --- a/HEAD/users_guide/framework_basics/text_output/index.html +++ b/HEAD/users_guide/framework_basics/text_output/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/the_affine_space/index.html b/HEAD/users_guide/framework_basics/the_affine_space/index.html index 2ff3ff5b6..2b3319d1f 100644 --- a/HEAD/users_guide/framework_basics/the_affine_space/index.html +++ b/HEAD/users_guide/framework_basics/the_affine_space/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/framework_basics/value_conversions/index.html b/HEAD/users_guide/framework_basics/value_conversions/index.html index c979b41b2..8850d513f 100644 --- a/HEAD/users_guide/framework_basics/value_conversions/index.html +++ b/HEAD/users_guide/framework_basics/value_conversions/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/systems/introduction/index.html b/HEAD/users_guide/systems/introduction/index.html index 2970afa08..6ac18666a 100644 --- a/HEAD/users_guide/systems/introduction/index.html +++ b/HEAD/users_guide/systems/introduction/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -250,7 +250,7 @@
      - +
      mpusz/mp-units @@ -421,7 +421,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/systems/isq/index.html b/HEAD/users_guide/systems/isq/index.html index 14cbb5785..248f8ef90 100644 --- a/HEAD/users_guide/systems/isq/index.html +++ b/HEAD/users_guide/systems/isq/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -250,7 +250,7 @@
      - +
      mpusz/mp-units @@ -421,7 +421,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/systems/natural_units/index.html b/HEAD/users_guide/systems/natural_units/index.html index f62b93813..d07b26d41 100644 --- a/HEAD/users_guide/systems/natural_units/index.html +++ b/HEAD/users_guide/systems/natural_units/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -250,7 +250,7 @@
      - +
      mpusz/mp-units @@ -421,7 +421,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/systems/si/index.html b/HEAD/users_guide/systems/si/index.html index 428f12e08..82f04ab27 100644 --- a/HEAD/users_guide/systems/si/index.html +++ b/HEAD/users_guide/systems/si/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -250,7 +250,7 @@
      - +
      mpusz/mp-units @@ -421,7 +421,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/systems/strong_angular_system/index.html b/HEAD/users_guide/systems/strong_angular_system/index.html index 4000bec27..b25ba1877 100644 --- a/HEAD/users_guide/systems/strong_angular_system/index.html +++ b/HEAD/users_guide/systems/strong_angular_system/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/terms_and_definitions/index.html b/HEAD/users_guide/terms_and_definitions/index.html index e0fc3d8bc..7a9071b77 100644 --- a/HEAD/users_guide/terms_and_definitions/index.html +++ b/HEAD/users_guide/terms_and_definitions/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/use_cases/extending_the_library/index.html b/HEAD/users_guide/use_cases/extending_the_library/index.html index 32079d048..4b4867437 100644 --- a/HEAD/users_guide/use_cases/extending_the_library/index.html +++ b/HEAD/users_guide/use_cases/extending_the_library/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -250,7 +250,7 @@
      - +
      mpusz/mp-units @@ -421,7 +421,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/use_cases/interoperability_with_other_libraries/index.html b/HEAD/users_guide/use_cases/interoperability_with_other_libraries/index.html index ea4ed55b8..2e4495e78 100644 --- a/HEAD/users_guide/use_cases/interoperability_with_other_libraries/index.html +++ b/HEAD/users_guide/use_cases/interoperability_with_other_libraries/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -255,7 +255,7 @@
      - +
      mpusz/mp-units @@ -426,7 +426,7 @@
      - +
      mpusz/mp-units diff --git a/HEAD/users_guide/use_cases/pure_dimensional_analysis/index.html b/HEAD/users_guide/use_cases/pure_dimensional_analysis/index.html index 2df2b7305..cf770f1b3 100644 --- a/HEAD/users_guide/use_cases/pure_dimensional_analysis/index.html +++ b/HEAD/users_guide/use_cases/pure_dimensional_analysis/index.html @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -250,7 +250,7 @@
      - +