diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d419759..f0375ffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - feat: `quantity_point` support added for `quantity_cast` and `value_cast` - feat: `value_cast` added - (!) refactor: `zero_Fahrenheit` renamed to `zeroth_degree_Fahrenheit` +- refactor: `math.h` header file broke up to smaller pieces - refactor: math functions constraints refactored - fix: `QuantityLike` conversions required `Q::rep` instead of using one provided by `quantity_like_traits` - docs: project blog and first posts added diff --git a/src/utility/include/mp-units/bits/math_angular.h b/src/utility/include/mp-units/bits/math_angular.h new file mode 100644 index 00000000..aa49afb5 --- /dev/null +++ b/src/utility/include/mp-units/bits/math_angular.h @@ -0,0 +1,122 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +// IWYU pragma: begin_exports +#include +// IWYU pragma: end_exports + +namespace mp_units::angular { + +template auto R, typename Rep> + requires requires(Rep v) { sin(v); } || requires(Rep v) { std::sin(v); } +[[nodiscard]] inline QuantityOf auto sin(const quantity& q) noexcept +{ + using std::sin; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(sin(q.force_numerical_value_in(radian))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{sin(value_cast(q).numerical_value_in(radian)), one}; + } else + return quantity{sin(q.numerical_value_in(radian)), one}; +} + +template auto R, typename Rep> + requires requires(Rep v) { cos(v); } || requires(Rep v) { std::cos(v); } +[[nodiscard]] inline QuantityOf auto cos(const quantity& q) noexcept +{ + using std::cos; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(cos(q.force_numerical_value_in(radian))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{cos(value_cast(q).numerical_value_in(radian)), one}; + } else + return quantity{cos(q.numerical_value_in(radian)), one}; +} + +template auto R, typename Rep> + requires requires(Rep v) { tan(v); } || requires(Rep v) { std::tan(v); } +[[nodiscard]] inline QuantityOf auto tan(const quantity& q) noexcept +{ + using std::tan; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(tan(q.force_numerical_value_in(radian))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{tan(value_cast(q).numerical_value_in(radian)), one}; + } else + return quantity{tan(q.numerical_value_in(radian)), one}; +} + +template auto R, typename Rep> + requires requires(Rep v) { asin(v); } || requires(Rep v) { std::asin(v); } +[[nodiscard]] inline QuantityOf auto asin(const quantity& q) noexcept +{ + using std::asin; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(asin(q.force_numerical_value_in(one))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{asin(value_cast(q).numerical_value_in(one)), radian}; + } else + return quantity{asin(q.numerical_value_in(one)), radian}; +} + +template auto R, typename Rep> + requires requires(Rep v) { acos(v); } || requires(Rep v) { std::acos(v); } +[[nodiscard]] inline QuantityOf auto acos(const quantity& q) noexcept +{ + using std::acos; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(acos(q.force_numerical_value_in(one))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{acos(value_cast(q).numerical_value_in(one)), radian}; + } else + return quantity{acos(q.numerical_value_in(one)), radian}; +} + +template auto R, typename Rep> + requires requires(Rep v) { atan(v); } || requires(Rep v) { std::atan(v); } +[[nodiscard]] inline QuantityOf auto atan(const quantity& q) noexcept +{ + using std::atan; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(atan(q.force_numerical_value_in(one))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{atan(value_cast(q).numerical_value_in(one)), radian}; + } else + return quantity{atan(q.numerical_value_in(one)), radian}; +} + +} // namespace mp_units::angular diff --git a/src/utility/include/mp-units/bits/math_core.h b/src/utility/include/mp-units/bits/math_core.h new file mode 100644 index 00000000..2ce773be --- /dev/null +++ b/src/utility/include/mp-units/bits/math_core.h @@ -0,0 +1,380 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +// IWYU pragma: begin_exports +#include +#include +// IWYU pragma: end_exports + +#include + +namespace mp_units { + +/** + * @brief Computes the value of a quantity raised to the `Num/Den` power + * + * Both the quantity value and its quantity specification are the base of the operation. + * + * @tparam Num Exponent numerator + * @tparam Den Exponent denominator + * @param q Quantity being the base of the operation + * @return Quantity The result of computation + */ +template + requires detail::non_zero && requires(Rep v) { + quantity_values::one(); + requires requires { pow(v, 1.0); } || requires { std::pow(v, 1.0); }; + } +[[nodiscard]] constexpr quantity(R), Rep> pow(const quantity& q) noexcept + +{ + if constexpr (Num == 0) { + return quantity(R), Rep>::one(); + } else if constexpr (ratio{Num, Den} == 1) { + return q; + } else { + using std::pow; + return { + static_cast(pow(q.numerical_value_ref_in(q.unit), static_cast(Num) / static_cast(Den))), + pow(R)}; + } +} + +/** + * @brief Computes the square root of a quantity + * + * Both the quantity value and its quantity specification are the base of the operation. + * + * @param q Quantity being the base of the operation + * @return Quantity The result of computation + */ +template + requires requires(Rep v) { sqrt(v); } || requires(Rep v) { std::sqrt(v); } +[[nodiscard]] constexpr quantity sqrt(const quantity& q) noexcept +{ + using std::sqrt; + return {static_cast(sqrt(q.numerical_value_ref_in(q.unit))), sqrt(R)}; +} + +/** + * @brief Computes the cubic root of a quantity + * + * Both the quantity value and its quantity specification are the base of the operation. + * + * @param q Quantity being the base of the operation + * @return Quantity The result of computation + */ +template + requires requires(Rep v) { cbrt(v); } || requires(Rep v) { std::cbrt(v); } +[[nodiscard]] constexpr quantity cbrt(const quantity& q) noexcept +{ + using std::cbrt; + return {static_cast(cbrt(q.numerical_value_ref_in(q.unit))), cbrt(R)}; +} + +/** + * @brief Computes Euler's raised to the given power + * + * @note Such an operation has sense only for a dimensionless quantity. + * + * @param q Quantity being the base of the operation + * @return Quantity The value of the same quantity type + */ +template auto R, typename Rep> + requires requires(Rep v) { exp(v); } || requires(Rep v) { std::exp(v); } +[[nodiscard]] constexpr quantity exp(const quantity& q) +{ + using std::exp; + return value_cast( + quantity{static_cast(exp(q.force_numerical_value_in(q.unit))), detail::clone_reference_with(R)}); +} + +/** + * @brief Computes the absolute value of a quantity + * + * @param q Quantity being the base of the operation + * @return Quantity The absolute value of a provided quantity + */ +template + requires requires(Rep v) { abs(v); } || requires(Rep v) { std::abs(v); } +[[nodiscard]] constexpr quantity abs(const quantity& q) noexcept +{ + using std::abs; + return {static_cast(abs(q.numerical_value_ref_in(q.unit))), R}; +} + +/** + * @brief Determines if a number is finite. + * + * @param a: Number to analyze. + * @return bool: Whether the number is finite or not. + */ +template + requires requires(Rep v) { isfinite(v); } || requires(Rep v) { std::isfinite(v); } +[[nodiscard]] constexpr bool isfinite(const quantity& a) noexcept +{ + using std::isfinite; + return isfinite(a.numerical_value_ref_in(a.unit)); +} + +/** + * @brief Determines if a number is infinite. + * + * @param a: Number to analyze. + * @return bool: Whether the number is infinite or not. + */ +template + requires requires(Rep v) { isinf(v); } || requires(Rep v) { std::isinf(v); } +[[nodiscard]] constexpr bool isinf(const quantity& a) noexcept +{ + using std::isinf; + return isinf(a.numerical_value_ref_in(a.unit)); +} + + +/** + * @brief Determines if a number is a nan. + * + * @param a: Number to analyze. + * @return bool: Whether the number is a NaN or not. + */ +template + requires requires(Rep v) { isnan(v); } || requires(Rep v) { std::isnan(v); } +[[nodiscard]] constexpr bool isnan(const quantity& a) noexcept +{ + using std::isnan; + return isnan(a.numerical_value_ref_in(a.unit)); +} + +/** + * @brief Computes the fma of 3 quantities + * + * @param a: Multiplicand + * @param x: Multiplicand + * @param b: Addend + * @return Quantity: The nearest floating point representable to ax+b + */ +template + requires requires { common_quantity_spec(get_quantity_spec(R) * get_quantity_spec(S), get_quantity_spec(T)); } && + (get_unit(R) * get_unit(S) == get_unit(T)) && requires(Rep1 v1, Rep2 v2, Rep3 v3) { + requires requires { fma(v1, v2, v3); } || requires { std::fma(v1, v2, v3); }; + } +[[nodiscard]] constexpr QuantityOf auto fma(const quantity& a, + const quantity& x, + const quantity& b) noexcept +{ + using std::fma; + return quantity{ + fma(a.numerical_value_ref_in(a.unit), x.numerical_value_ref_in(x.unit), b.numerical_value_ref_in(b.unit)), + common_reference(R * S, T)}; +} + + +/** + * @brief Returns the epsilon of the quantity + * + * The returned value is defined by a std::numeric_limits::epsilon(). + * + * @tparam Q Quantity type being the base of the operation + * @return Quantity The epsilon value for quantity's representation type + */ +template + requires requires { std::numeric_limits::epsilon(); } +[[nodiscard]] constexpr quantity epsilon(R r) noexcept +{ + return {static_cast(std::numeric_limits::epsilon()), r}; +} + +/** + * @brief Computes the largest quantity with integer representation and unit type To with its number not greater than q + * + * @tparam q Quantity being the base of the operation + * @return Quantity The rounded quantity with unit type To + */ +template +[[nodiscard]] constexpr quantity(R), Rep> floor(const quantity& q) noexcept + requires((!treat_as_floating_point) || requires(Rep v) { floor(v); } || requires(Rep v) { std::floor(v); }) && + (To == get_unit(R) || requires { + q.force_in(To); + quantity_values::one(); + }) +{ + const auto handle_signed_results = [&](const T& res) { + if (res > q) { + return res - T::one(); + } + return res; + }; + if constexpr (treat_as_floating_point) { + using std::floor; + if constexpr (To == get_unit(R)) { + return {static_cast(floor(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with(R)}; + } else { + return handle_signed_results( + quantity{static_cast(floor(q.force_numerical_value_in(To))), detail::clone_reference_with(R)}); + } + } else { + if constexpr (To == get_unit(R)) { + return q.force_in(To); + } else { + return handle_signed_results(q.force_in(To)); + } + } +} + +/** + * @brief Computes the smallest quantity with integer representation and unit type To with its number not less than q + * + * @tparam q Quantity being the base of the operation + * @return Quantity The rounded quantity with unit type To + */ +template +[[nodiscard]] constexpr quantity(R), Rep> ceil(const quantity& q) noexcept + requires((!treat_as_floating_point) || requires(Rep v) { ceil(v); } || requires(Rep v) { std::ceil(v); }) && + (To == get_unit(R) || requires { + q.force_in(To); + quantity_values::one(); + }) +{ + const auto handle_signed_results = [&](const T& res) { + if (res < q) { + return res + T::one(); + } + return res; + }; + if constexpr (treat_as_floating_point) { + using std::ceil; + if constexpr (To == get_unit(R)) { + return {static_cast(ceil(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with(R)}; + } else { + return handle_signed_results( + quantity{static_cast(ceil(q.force_numerical_value_in(To))), detail::clone_reference_with(R)}); + } + } else { + if constexpr (To == get_unit(R)) { + return q.force_in(To); + } else { + return handle_signed_results(q.force_in(To)); + } + } +} + +/** + * @brief Computes the nearest quantity with integer representation and unit type To to q + * + * Rounding halfway cases away from zero, regardless of the current rounding mode. + * + * @tparam q Quantity being the base of the operation + * @return Quantity The rounded quantity with unit type To + */ +template +[[nodiscard]] constexpr quantity(R), Rep> round(const quantity& q) noexcept + requires((!treat_as_floating_point) || requires(Rep v) { round(v); } || requires(Rep v) { std::round(v); }) && + (To == get_unit(R) || requires { + ::mp_units::floor(q); + quantity_values::one(); + }) +{ + if constexpr (To == get_unit(R)) { + if constexpr (treat_as_floating_point) { + using std::round; + return {static_cast(round(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with(R)}; + } else { + return q.force_in(To); + } + } else { + const auto res_low = mp_units::floor(q); + const auto res_high = res_low + res_low.one(); + const auto diff0 = q - res_low; + const auto diff1 = res_high - q; + if (diff0 == diff1) { + if (static_cast(res_low.numerical_value_ref_in(To)) & 1) { + return res_high; + } + return res_low; + } + if (diff0 < diff1) { + return res_low; + } + return res_high; + } +} + +/** + * @brief Computes the inverse of a quantity in a provided unit + */ +template +[[nodiscard]] constexpr QuantityOf auto inverse(const quantity& q) + requires requires { + quantity_values::one(); + value_cast(1 / q); + } +{ + return (quantity_values::one() * one).force_in(To * quantity::unit) / q; +} + +/** + * @brief Computes the square root of the sum of the squares of x and y, + * without undue overflow or underflow at intermediate stages of the computation + */ +template + requires requires(Rep1 v1, Rep2 v2) { + common_reference(R1, R2); + requires requires { hypot(v1, v2); } || requires { std::hypot(v1, v2); }; + } +[[nodiscard]] constexpr QuantityOf auto hypot( + const quantity& x, const quantity& y) noexcept +{ + constexpr auto ref = common_reference(R1, R2); + constexpr auto unit = get_unit(ref); + using std::hypot; + return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit)), ref}; +} + +/** + * @brief Computes the square root of the sum of the squares of x, y, and z, + * without undue overflow or underflow at intermediate stages of the computation + */ +template + requires requires(Rep1 v1, Rep2 v2, Rep3 v3) { + common_reference(R1, R2, R3); + requires requires { hypot(v1, v2, v3); } || requires { std::hypot(v1, v2, v3); }; + } +[[nodiscard]] constexpr QuantityOf auto hypot( + const quantity& x, const quantity& y, const quantity& z) noexcept +{ + constexpr auto ref = common_reference(R1, R2); + constexpr auto unit = get_unit(ref); + using std::hypot; + return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit), z.numerical_value_in(unit)), ref}; +} + +} // namespace mp_units diff --git a/src/utility/include/mp-units/bits/math_si.h b/src/utility/include/mp-units/bits/math_si.h new file mode 100644 index 00000000..3421627a --- /dev/null +++ b/src/utility/include/mp-units/bits/math_si.h @@ -0,0 +1,123 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +// IWYU pragma: begin_exports +#include +// IWYU pragma: end_exports + +namespace mp_units::si { + +template auto R, typename Rep> + requires requires(Rep v) { sin(v); } || requires(Rep v) { std::sin(v); } +[[nodiscard]] inline QuantityOf auto sin(const quantity& q) noexcept +{ + using std::sin; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(sin(q.force_numerical_value_in(si::radian))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{sin(value_cast(q).numerical_value_in(si::radian)), one}; + } else + return quantity{sin(q.numerical_value_in(si::radian)), one}; +} + +template auto R, typename Rep> + requires requires(Rep v) { cos(v); } || requires(Rep v) { std::cos(v); } +[[nodiscard]] inline QuantityOf auto cos(const quantity& q) noexcept +{ + using std::cos; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(cos(q.force_numerical_value_in(si::radian))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{cos(value_cast(q).numerical_value_in(si::radian)), one}; + } else + return quantity{cos(q.numerical_value_in(si::radian)), one}; +} + +template auto R, typename Rep> + requires requires(Rep v) { tan(v); } || requires(Rep v) { std::tan(v); } +[[nodiscard]] inline QuantityOf auto tan(const quantity& q) noexcept +{ + using std::tan; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(tan(q.force_numerical_value_in(si::radian))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{tan(value_cast(q).numerical_value_in(si::radian)), one}; + } else + return quantity{tan(q.numerical_value_in(si::radian)), one}; +} + +template auto R, typename Rep> + requires requires(Rep v) { asin(v); } || requires(Rep v) { std::asin(v); } +[[nodiscard]] inline QuantityOf auto asin(const quantity& q) noexcept +{ + using std::asin; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(asin(q.force_numerical_value_in(one))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{asin(value_cast(q).numerical_value_in(one)), si::radian}; + } else + return quantity{asin(q.numerical_value_in(one)), si::radian}; +} + +template auto R, typename Rep> + requires requires(Rep v) { acos(v); } || requires(Rep v) { std::acos(v); } +[[nodiscard]] inline QuantityOf auto acos(const quantity& q) noexcept +{ + using std::acos; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(acos(q.force_numerical_value_in(one))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{acos(value_cast(q).numerical_value_in(one)), si::radian}; + } else + return quantity{acos(q.numerical_value_in(one)), si::radian}; +} + +template auto R, typename Rep> + requires requires(Rep v) { atan(v); } || requires(Rep v) { std::atan(v); } +[[nodiscard]] inline QuantityOf auto atan(const quantity& q) noexcept +{ + using std::atan; + if constexpr (!treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(atan(q.force_numerical_value_in(one))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{atan(value_cast(q).numerical_value_in(one)), si::radian}; + } else + return quantity{atan(q.numerical_value_in(one)), si::radian}; +} + +} // namespace mp_units::si diff --git a/src/utility/include/mp-units/math.h b/src/utility/include/mp-units/math.h index 389068f7..0a37935d 100644 --- a/src/utility/include/mp-units/math.h +++ b/src/utility/include/mp-units/math.h @@ -22,538 +22,12 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include -// IWYU pragma: begin_exports -#include -#include -// IWYU pragma: end_exports - -#include - -namespace mp_units { - -/** - * @brief Computes the value of a quantity raised to the `Num/Den` power - * - * Both the quantity value and its quantity specification are the base of the operation. - * - * @tparam Num Exponent numerator - * @tparam Den Exponent denominator - * @param q Quantity being the base of the operation - * @return Quantity The result of computation - */ -template - requires detail::non_zero && requires(Rep v) { - quantity_values::one(); - requires requires { pow(v, 1.0); } || requires { std::pow(v, 1.0); }; - } -[[nodiscard]] constexpr quantity(R), Rep> pow(const quantity& q) noexcept - -{ - if constexpr (Num == 0) { - return quantity(R), Rep>::one(); - } else if constexpr (ratio{Num, Den} == 1) { - return q; - } else { - using std::pow; - return { - static_cast(pow(q.numerical_value_ref_in(q.unit), static_cast(Num) / static_cast(Den))), - pow(R)}; - } -} - -/** - * @brief Computes the square root of a quantity - * - * Both the quantity value and its quantity specification are the base of the operation. - * - * @param q Quantity being the base of the operation - * @return Quantity The result of computation - */ -template - requires requires(Rep v) { sqrt(v); } || requires(Rep v) { std::sqrt(v); } -[[nodiscard]] constexpr quantity sqrt(const quantity& q) noexcept -{ - using std::sqrt; - return {static_cast(sqrt(q.numerical_value_ref_in(q.unit))), sqrt(R)}; -} - -/** - * @brief Computes the cubic root of a quantity - * - * Both the quantity value and its quantity specification are the base of the operation. - * - * @param q Quantity being the base of the operation - * @return Quantity The result of computation - */ -template - requires requires(Rep v) { cbrt(v); } || requires(Rep v) { std::cbrt(v); } -[[nodiscard]] constexpr quantity cbrt(const quantity& q) noexcept -{ - using std::cbrt; - return {static_cast(cbrt(q.numerical_value_ref_in(q.unit))), cbrt(R)}; -} - -/** - * @brief Computes Euler's raised to the given power - * - * @note Such an operation has sense only for a dimensionless quantity. - * - * @param q Quantity being the base of the operation - * @return Quantity The value of the same quantity type - */ -template auto R, typename Rep> - requires requires(Rep v) { exp(v); } || requires(Rep v) { std::exp(v); } -[[nodiscard]] constexpr quantity exp(const quantity& q) -{ - using std::exp; - return value_cast( - quantity{static_cast(exp(q.force_numerical_value_in(q.unit))), detail::clone_reference_with(R)}); -} - -/** - * @brief Computes the absolute value of a quantity - * - * @param q Quantity being the base of the operation - * @return Quantity The absolute value of a provided quantity - */ -template - requires requires(Rep v) { abs(v); } || requires(Rep v) { std::abs(v); } -[[nodiscard]] constexpr quantity abs(const quantity& q) noexcept -{ - using std::abs; - return {static_cast(abs(q.numerical_value_ref_in(q.unit))), R}; -} - -/** - * @brief Determines if a number is finite. - * - * @param a: Number to analyze. - * @return bool: Whether the number is finite or not. - */ -template - requires requires(Rep v) { isfinite(v); } || requires(Rep v) { std::isfinite(v); } -[[nodiscard]] constexpr bool isfinite(const quantity& a) noexcept -{ - using std::isfinite; - return isfinite(a.numerical_value_ref_in(a.unit)); -} - -/** - * @brief Determines if a number is infinite. - * - * @param a: Number to analyze. - * @return bool: Whether the number is infinite or not. - */ -template - requires requires(Rep v) { isinf(v); } || requires(Rep v) { std::isinf(v); } -[[nodiscard]] constexpr bool isinf(const quantity& a) noexcept -{ - using std::isinf; - return isinf(a.numerical_value_ref_in(a.unit)); -} - - -/** - * @brief Determines if a number is a nan. - * - * @param a: Number to analyze. - * @return bool: Whether the number is a NaN or not. - */ -template - requires requires(Rep v) { isnan(v); } || requires(Rep v) { std::isnan(v); } -[[nodiscard]] constexpr bool isnan(const quantity& a) noexcept -{ - using std::isnan; - return isnan(a.numerical_value_ref_in(a.unit)); -} - -/** - * @brief Computes the fma of 3 quantities - * - * @param a: Multiplicand - * @param x: Multiplicand - * @param b: Addend - * @return Quantity: The nearest floating point representable to ax+b - */ -template - requires requires { common_quantity_spec(get_quantity_spec(R) * get_quantity_spec(S), get_quantity_spec(T)); } && - (get_unit(R) * get_unit(S) == get_unit(T)) && requires(Rep1 v1, Rep2 v2, Rep3 v3) { - requires requires { fma(v1, v2, v3); } || requires { std::fma(v1, v2, v3); }; - } -[[nodiscard]] constexpr QuantityOf auto fma(const quantity& a, - const quantity& x, - const quantity& b) noexcept -{ - using std::fma; - return quantity{ - fma(a.numerical_value_ref_in(a.unit), x.numerical_value_ref_in(x.unit), b.numerical_value_ref_in(b.unit)), - common_reference(R * S, T)}; -} - - -/** - * @brief Returns the epsilon of the quantity - * - * The returned value is defined by a std::numeric_limits::epsilon(). - * - * @tparam Q Quantity type being the base of the operation - * @return Quantity The epsilon value for quantity's representation type - */ -template - requires requires { std::numeric_limits::epsilon(); } -[[nodiscard]] constexpr quantity epsilon(R r) noexcept -{ - return {static_cast(std::numeric_limits::epsilon()), r}; -} - -/** - * @brief Computes the largest quantity with integer representation and unit type To with its number not greater than q - * - * @tparam q Quantity being the base of the operation - * @return Quantity The rounded quantity with unit type To - */ -template -[[nodiscard]] constexpr quantity(R), Rep> floor(const quantity& q) noexcept - requires((!treat_as_floating_point) || requires(Rep v) { floor(v); } || requires(Rep v) { std::floor(v); }) && - (To == get_unit(R) || requires { - q.force_in(To); - quantity_values::one(); - }) -{ - const auto handle_signed_results = [&](const T& res) { - if (res > q) { - return res - T::one(); - } - return res; - }; - if constexpr (treat_as_floating_point) { - using std::floor; - if constexpr (To == get_unit(R)) { - return {static_cast(floor(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with(R)}; - } else { - return handle_signed_results( - quantity{static_cast(floor(q.force_numerical_value_in(To))), detail::clone_reference_with(R)}); - } - } else { - if constexpr (To == get_unit(R)) { - return q.force_in(To); - } else { - return handle_signed_results(q.force_in(To)); - } - } -} - -/** - * @brief Computes the smallest quantity with integer representation and unit type To with its number not less than q - * - * @tparam q Quantity being the base of the operation - * @return Quantity The rounded quantity with unit type To - */ -template -[[nodiscard]] constexpr quantity(R), Rep> ceil(const quantity& q) noexcept - requires((!treat_as_floating_point) || requires(Rep v) { ceil(v); } || requires(Rep v) { std::ceil(v); }) && - (To == get_unit(R) || requires { - q.force_in(To); - quantity_values::one(); - }) -{ - const auto handle_signed_results = [&](const T& res) { - if (res < q) { - return res + T::one(); - } - return res; - }; - if constexpr (treat_as_floating_point) { - using std::ceil; - if constexpr (To == get_unit(R)) { - return {static_cast(ceil(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with(R)}; - } else { - return handle_signed_results( - quantity{static_cast(ceil(q.force_numerical_value_in(To))), detail::clone_reference_with(R)}); - } - } else { - if constexpr (To == get_unit(R)) { - return q.force_in(To); - } else { - return handle_signed_results(q.force_in(To)); - } - } -} - -/** - * @brief Computes the nearest quantity with integer representation and unit type To to q - * - * Rounding halfway cases away from zero, regardless of the current rounding mode. - * - * @tparam q Quantity being the base of the operation - * @return Quantity The rounded quantity with unit type To - */ -template -[[nodiscard]] constexpr quantity(R), Rep> round(const quantity& q) noexcept - requires((!treat_as_floating_point) || requires(Rep v) { round(v); } || requires(Rep v) { std::round(v); }) && - (To == get_unit(R) || requires { - ::mp_units::floor(q); - quantity_values::one(); - }) -{ - if constexpr (To == get_unit(R)) { - if constexpr (treat_as_floating_point) { - using std::round; - return {static_cast(round(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with(R)}; - } else { - return q.force_in(To); - } - } else { - const auto res_low = mp_units::floor(q); - const auto res_high = res_low + res_low.one(); - const auto diff0 = q - res_low; - const auto diff1 = res_high - q; - if (diff0 == diff1) { - if (static_cast(res_low.numerical_value_ref_in(To)) & 1) { - return res_high; - } - return res_low; - } - if (diff0 < diff1) { - return res_low; - } - return res_high; - } -} - -/** - * @brief Computes the inverse of a quantity in a provided unit - */ -template -[[nodiscard]] constexpr QuantityOf auto inverse(const quantity& q) - requires requires { - quantity_values::one(); - value_cast(1 / q); - } -{ - return (quantity_values::one() * one).force_in(To * quantity::unit) / q; -} - -/** - * @brief Computes the square root of the sum of the squares of x and y, - * without undue overflow or underflow at intermediate stages of the computation - */ -template - requires requires(Rep1 v1, Rep2 v2) { - common_reference(R1, R2); - requires requires { hypot(v1, v2); } || requires { std::hypot(v1, v2); }; - } -[[nodiscard]] constexpr QuantityOf auto hypot( - const quantity& x, const quantity& y) noexcept -{ - constexpr auto ref = common_reference(R1, R2); - constexpr auto unit = get_unit(ref); - using std::hypot; - return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit)), ref}; -} - -/** - * @brief Computes the square root of the sum of the squares of x, y, and z, - * without undue overflow or underflow at intermediate stages of the computation - */ -template - requires requires(Rep1 v1, Rep2 v2, Rep3 v3) { - common_reference(R1, R2, R3); - requires requires { hypot(v1, v2, v3); } || requires { std::hypot(v1, v2, v3); }; - } -[[nodiscard]] constexpr QuantityOf auto hypot( - const quantity& x, const quantity& y, const quantity& z) noexcept -{ - constexpr auto ref = common_reference(R1, R2); - constexpr auto unit = get_unit(ref); - using std::hypot; - return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit), z.numerical_value_in(unit)), ref}; -} - -namespace isq { - -template auto R, typename Rep> - requires requires(Rep v) { sin(v); } || requires(Rep v) { std::sin(v); } -[[nodiscard]] inline QuantityOf auto sin(const quantity& q) noexcept -{ - using std::sin; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(sin(q.force_numerical_value_in(si::radian))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{sin(value_cast(q).numerical_value_in(si::radian)), one}; - } else - return quantity{sin(q.numerical_value_in(si::radian)), one}; -} - -template auto R, typename Rep> - requires requires(Rep v) { cos(v); } || requires(Rep v) { std::cos(v); } -[[nodiscard]] inline QuantityOf auto cos(const quantity& q) noexcept -{ - using std::cos; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(cos(q.force_numerical_value_in(si::radian))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{cos(value_cast(q).numerical_value_in(si::radian)), one}; - } else - return quantity{cos(q.numerical_value_in(si::radian)), one}; -} - -template auto R, typename Rep> - requires requires(Rep v) { tan(v); } || requires(Rep v) { std::tan(v); } -[[nodiscard]] inline QuantityOf auto tan(const quantity& q) noexcept -{ - using std::tan; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(tan(q.force_numerical_value_in(si::radian))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{tan(value_cast(q).numerical_value_in(si::radian)), one}; - } else - return quantity{tan(q.numerical_value_in(si::radian)), one}; -} - -template auto R, typename Rep> - requires requires(Rep v) { asin(v); } || requires(Rep v) { std::asin(v); } -[[nodiscard]] inline QuantityOf auto asin(const quantity& q) noexcept -{ - using std::asin; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(asin(q.force_numerical_value_in(one))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{asin(value_cast(q).numerical_value_in(one)), si::radian}; - } else - return quantity{asin(q.numerical_value_in(one)), si::radian}; -} - -template auto R, typename Rep> - requires requires(Rep v) { acos(v); } || requires(Rep v) { std::acos(v); } -[[nodiscard]] inline QuantityOf auto acos(const quantity& q) noexcept -{ - using std::acos; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(acos(q.force_numerical_value_in(one))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{acos(value_cast(q).numerical_value_in(one)), si::radian}; - } else - return quantity{acos(q.numerical_value_in(one)), si::radian}; -} - -template auto R, typename Rep> - requires requires(Rep v) { atan(v); } || requires(Rep v) { std::atan(v); } -[[nodiscard]] inline QuantityOf auto atan(const quantity& q) noexcept -{ - using std::atan; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(atan(q.force_numerical_value_in(one))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{atan(value_cast(q).numerical_value_in(one)), si::radian}; - } else - return quantity{atan(q.numerical_value_in(one)), si::radian}; -} - -} // namespace isq - -namespace angular { - -template auto R, typename Rep> - requires requires(Rep v) { sin(v); } || requires(Rep v) { std::sin(v); } -[[nodiscard]] inline QuantityOf auto sin(const quantity& q) noexcept -{ - using std::sin; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(sin(q.force_numerical_value_in(radian))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{sin(value_cast(q).numerical_value_in(radian)), one}; - } else - return quantity{sin(q.numerical_value_in(radian)), one}; -} - -template auto R, typename Rep> - requires requires(Rep v) { cos(v); } || requires(Rep v) { std::cos(v); } -[[nodiscard]] inline QuantityOf auto cos(const quantity& q) noexcept -{ - using std::cos; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(cos(q.force_numerical_value_in(radian))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{cos(value_cast(q).numerical_value_in(radian)), one}; - } else - return quantity{cos(q.numerical_value_in(radian)), one}; -} - -template auto R, typename Rep> - requires requires(Rep v) { tan(v); } || requires(Rep v) { std::tan(v); } -[[nodiscard]] inline QuantityOf auto tan(const quantity& q) noexcept -{ - using std::tan; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(tan(q.force_numerical_value_in(radian))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{tan(value_cast(q).numerical_value_in(radian)), one}; - } else - return quantity{tan(q.numerical_value_in(radian)), one}; -} - -template auto R, typename Rep> - requires requires(Rep v) { asin(v); } || requires(Rep v) { std::asin(v); } -[[nodiscard]] inline QuantityOf auto asin(const quantity& q) noexcept -{ - using std::asin; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(asin(q.force_numerical_value_in(one))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{asin(value_cast(q).numerical_value_in(one)), radian}; - } else - return quantity{asin(q.numerical_value_in(one)), radian}; -} - -template auto R, typename Rep> - requires requires(Rep v) { acos(v); } || requires(Rep v) { std::acos(v); } -[[nodiscard]] inline QuantityOf auto acos(const quantity& q) noexcept -{ - using std::acos; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(acos(q.force_numerical_value_in(one))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{acos(value_cast(q).numerical_value_in(one)), radian}; - } else - return quantity{acos(q.numerical_value_in(one)), radian}; -} - -template auto R, typename Rep> - requires requires(Rep v) { atan(v); } || requires(Rep v) { std::atan(v); } -[[nodiscard]] inline QuantityOf auto atan(const quantity& q) noexcept -{ - using std::atan; - if constexpr (!treat_as_floating_point) { - // check what is the return type when called with the integral value - using rep = decltype(atan(q.force_numerical_value_in(one))); - // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed - return quantity{atan(value_cast(q).numerical_value_in(one)), radian}; - } else - return quantity{atan(q.numerical_value_in(one)), radian}; -} - -} // namespace angular - -} // namespace mp_units +// This header is intentionally empty and just include other headers +// `math.h` is just a convenience wrapper for not modular code +// With C++20 modules: +// - math_core will be a part of the mp_units.core module +// - math_si and math_angular will be provided with the mp_units.systems module