// 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 #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 [[nodiscard]] inline auto pow(const Q& q) noexcept requires requires { pow(q.number(), 1.0); } || requires { std::pow(q.number(), 1.0); } { using rep = TYPENAME Q::rep; if constexpr (Num == 0) { return rep(1); } else if constexpr (ratio{Num, Den} == 1) { return q; } else { using std::pow; return quantity(Q::quantity_spec), pow(Q::unit)>{}, rep>( static_cast(pow(q.number(), static_cast(Num) / static_cast(Den)))); } } /** * @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 [[nodiscard]] inline Quantity auto sqrt(const Q& q) noexcept requires requires { sqrt(q.number()); } || requires { std::sqrt(q.number()); } { using rep = TYPENAME Q::rep; using std::sqrt; return quantity(Q::quantity_spec), pow<1, 2>(Q::unit)>{}, rep>( static_cast(sqrt(q.number()))); } /** * @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 [[nodiscard]] inline Quantity auto cbrt(const Q& q) noexcept requires requires { cbrt(q.number()); } || requires { std::cbrt(q.number()); } { using rep = TYPENAME Q::rep; using std::cbrt; return quantity(Q::quantity_spec), pow<1, 3>(Q::unit)>{}, rep>( static_cast(cbrt(q.number()))); } /** * @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 Q> [[nodiscard]] inline Q exp(const Q& q) requires requires { exp(q.number()); } || requires { std::exp(q.number()); } { using std::exp; return value_cast(exp(value_cast(q).number()) * dimensionless[one]); } /** * @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 [[nodiscard]] inline Q abs(const Q& q) noexcept requires requires { abs(q.number()); } || requires { std::abs(q.number()); } { using std::abs; return Q(abs(q.number())); } /** * @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 auto epsilon(R r) noexcept { return 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{}, Rep> floor(const quantity& q) noexcept requires((!treat_as_floating_point) || requires { floor(q.number()); } || requires { std::floor(q.number()); }) && (To == get_unit(R) || requires { ::mp_units::value_cast(q); quantity{}, Rep>::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 quantity{}, Rep>(floor(q.number())); } else { return handle_signed_results( quantity{}, Rep>(floor(value_cast(q).number()))); } } else { if constexpr (To == get_unit(R)) { return value_cast(q); } else { return handle_signed_results(value_cast(q)); } } } /** * @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{}, Rep> ceil(const quantity& q) noexcept requires((!treat_as_floating_point) || requires { ceil(q.number()); } || requires { std::ceil(q.number()); }) && (To == get_unit(R) || requires { ::mp_units::value_cast(q); quantity{}, Rep>::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 quantity{}, Rep>(ceil(q.number())); } else { return handle_signed_results( quantity{}, Rep>(ceil(value_cast(q).number()))); } } else { if constexpr (To == get_unit(R)) { return value_cast(q); } else { return handle_signed_results(value_cast(q)); } } } /** * @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{}, Rep> round(const quantity& q) noexcept requires((!treat_as_floating_point) || requires { round(q.number()); } || requires { std::round(q.number()); }) && (To == get_unit(R) || requires { ::mp_units::floor(q); quantity{}, Rep>::one(); }) { if constexpr (To == get_unit(R)) { if constexpr (treat_as_floating_point) { using std::round; return quantity{}, Rep>(round(q.number())); } else { return value_cast(q); } } 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.number()) & 1) { return res_high; } return res_low; } if (diff0 < diff1) { return res_low; } return res_high; } } /** * @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 [[nodiscard]] inline QuantityOf auto hypot( const Q1& x, const Q2& y) noexcept requires requires { common_reference(Q1::reference, Q2::reference); } && ( requires { hypot(x.number(), y.number()); } || requires { std::hypot(x.number(), y.number()); }) { using std::hypot; using type = quantity; return hypot(type{x}.number(), type{y}.number()) * type::reference; } /** * @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 [[nodiscard]] inline QuantityOf auto hypot(const Q1& x, const Q2& y, const Q3& z) noexcept requires requires { common_reference(Q1::reference, Q2::reference, Q3::reference); } && ( requires { hypot(x.number(), y.number(), z.number()); } || requires { std::hypot(x.number(), y.number(), z.number()); }) { using std::hypot; using type = quantity; return hypot(type{x}.number(), type{y}.number(), type{z}.number()) * type::reference; } namespace isq { template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto sin(const Q& q) noexcept requires requires { sin(q.number()); } || requires { std::sin(q.number()); } { using std::sin; return quantity{sin(q[si::radian].number())}; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto cos(const Q& q) noexcept requires requires { cos(q.number()); } || requires { std::cos(q.number()); } { using std::cos; return quantity{cos(q[si::radian].number())}; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto tan(const Q& q) noexcept requires requires { tan(q.number()); } || requires { std::tan(q.number()); } { using std::tan; return quantity{tan(q[si::radian].number())}; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto asin(const Q& q) noexcept requires requires { asin(q.number()); } || requires { std::asin(q.number()); } { using std::asin; return asin(value_cast(q).number()) * angular_measure[si::radian]; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto acos(const Q& q) noexcept requires requires { acos(q.number()); } || requires { std::acos(q.number()); } { using std::acos; return acos(value_cast(q).number()) * angular_measure[si::radian]; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto atan(const Q& q) noexcept requires requires { atan(q.number()); } || requires { std::atan(q.number()); } { using std::atan; return atan(value_cast(q).number()) * angular_measure[si::radian]; } } // namespace isq namespace angular { // TODO cannot use `QuantityOf` as it is not interconvertible with `isq_angle::angular_measure` template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto sin(const Q& q) noexcept requires requires { sin(q.number()); } || requires { std::sin(q.number()); } { using std::sin; return sin(q[radian].number()) * one; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto cos(const Q& q) noexcept requires requires { cos(q.number()); } || requires { std::cos(q.number()); } { using std::cos; return cos(q[radian].number()) * one; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto tan(const Q& q) noexcept requires requires { tan(q.number()); } || requires { std::tan(q.number()); } { using std::tan; return tan(q[radian].number()) * one; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto asin(const Q& q) noexcept requires requires { asin(q.number()); } || requires { std::asin(q.number()); } { using std::asin; return asin(value_cast(q).number()) * angle[radian]; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto acos(const Q& q) noexcept requires requires { acos(q.number()); } || requires { std::acos(q.number()); } { using std::acos; return acos(value_cast(q).number()) * angle[radian]; } template Q> requires treat_as_floating_point [[nodiscard]] inline QuantityOf auto atan(const Q& q) noexcept requires requires { atan(q.number()); } || requires { std::atan(q.number()); } { using std::atan; return atan(value_cast(q).number()) * angle[radian]; } } // namespace angular } // namespace mp_units