// 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 units { /** * @brief Computes the value of a quantity raised to the power `N` * * Both the quantity value and its dimension are the base of the operation. * * @tparam Num Exponent numerator * @tparam Den Exponent denominator * @param q Quantity being the base of the operation * @return Quantity The result of computation */ template 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 { using dim = dimension_pow; using unit = downcast_unit(Q::unit::ratio)>; using std::pow; return quantity( 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 dimension 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 dim = dimension_pow; using unit = downcast_unit; using rep = TYPENAME Q::rep; using std::sqrt; return quantity(static_cast(sqrt(q.number()))); } /** * @brief Computes the cubic root of a quantity * * Both the quantity value and its dimension are the base of the operation. * * @param q Quantity being the base of the operation * @return Quantity The result of computation */ template [[nodiscard]] inline Quantity auto cbrt(const Q& q) noexcept requires requires { cbrt(q.number()); } || requires { std::cbrt(q.number()); } { using dim = dimension_pow; using unit = downcast_unit; using rep = TYPENAME Q::rep; using std::cbrt; return quantity(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 [[nodiscard]] inline dimensionless exp(const dimensionless& q) requires requires { exp(q.number()); } || requires { std::exp(q.number()); } { using std::exp; return quantity_cast(dimensionless(exp(quantity_cast(q).number()))); } /** * @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 quantity abs(const quantity& q) noexcept requires requires { abs(q.number()); } || requires { std::abs(q.number()); } { using std::abs; return quantity(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() noexcept { return Q(std::numeric_limits::epsilon()); } /** * @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 floor(const quantity& q) noexcept requires (!treat_as_floating_point) || requires { floor(q.number()); } || requires { std::floor(q.number()); } { 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(std::is_same_v) { return quantity(floor(q.number())); } else { return handle_signed_results(quantity(floor(quantity_cast(q).number()))); } } else { if constexpr(std::is_same_v) { return q; } else { return handle_signed_results(quantity_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 ceil(const quantity& q) noexcept requires (!treat_as_floating_point) || requires { ceil(q.number()); } || requires { std::ceil(q.number()); } { 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(std::is_same_v) { return quantity(ceil(q.number())); } else { return handle_signed_results(quantity(ceil(quantity_cast(q).number()))); } } else { if constexpr(std::is_same_v) { return q; } else { return handle_signed_results(quantity_cast(q)); } } } } // namespace units