// 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 #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::mag)>; 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(Q::unit::mag)>; 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(Q::unit::mag)>; 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()); }) && (std::same_as || requires { ::units::quantity_cast(q); quantity::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 (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 Overload of @c ::units::floor() using the unit type of To * * @tparam q Quantity being the base of the operation * @return Quantity The rounded quantity with unit type of quantity To */ template D, typename U, std::same_as Rep> [[nodiscard]] constexpr quantity floor(const quantity& q) noexcept requires requires { ::units::floor(q); } { return ::units::floor(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()); }) && (std::same_as || requires { ::units::quantity_cast(q); quantity::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 (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)); } } } /** * @brief Overload of @c ::units::ceil() using the unit type of To * * @tparam q Quantity being the base of the operation * @return Quantity The rounded quantity with unit type of quantity To */ template D, typename U, std::same_as Rep> [[nodiscard]] constexpr quantity ceil(const quantity& q) noexcept requires requires { ::units::ceil(q); } { return ::units::ceil(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 round(const quantity& q) noexcept requires((!treat_as_floating_point) || requires { round(q.number()); } || requires { std::round(q.number()); }) && (std::same_as || requires { ::units::floor(q); quantity::one(); }) { if constexpr (std::is_same_v) { if constexpr (treat_as_floating_point) { using std::round; return quantity(round(q.number())); } else { return q; } } else { const auto res_low = units::floor(q); const auto res_high = res_low + decltype(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 Overload of @c ::units::round() using the unit type of To * * @tparam q Quantity being the base of the operation * @return Quantity The rounded quantity with unit type of quantity To */ template D, typename U, std::same_as Rep> [[nodiscard]] constexpr quantity round(const quantity& q) noexcept requires requires { ::units::round(q); } { return ::units::round(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 [[nodiscard]] inline std::common_type_t hypot(const Q1& x, const Q2& y) noexcept requires requires { typename std::common_type_t; } && requires(std::common_type_t q) { requires requires { hypot(q.number(), q.number()); } || requires { std::hypot(q.number(), q.number()); }; } { using type = std::common_type_t; type xx = x; type yy = y; using std::hypot; return type(hypot(xx.number(), yy.number())); } /** * @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 std::common_type_t hypot(const Q1& x, const Q2& y, const Q3& z) noexcept requires requires { typename std::common_type_t; } && requires(std::common_type_t q) { requires requires { hypot(q.number(), q.number(), q.number()); } || requires { std::hypot(q.number(), q.number(), q.number()); }; } { using type = std::common_type_t; type xx = x; type yy = y; type zz = z; using std::hypot; return type(hypot(xx.number(), yy.number(), zz.number())); } template [[nodiscard]] inline dimensionless sin(const angle& q) noexcept requires requires { sin(q.number()); } || requires { std::sin(q.number()); } { using std::sin; return sin(quantity_cast(q).number()); } template [[nodiscard]] inline dimensionless cos(const angle& q) noexcept requires requires { cos(q.number()); } || requires { std::cos(q.number()); } { using std::cos; return cos(quantity_cast(q).number()); } template [[nodiscard]] inline dimensionless tan(const angle& q) noexcept requires requires { tan(q.number()); } || requires { std::tan(q.number()); } { using std::tan; return tan(quantity_cast(q).number()); } template [[nodiscard]] inline angle asin(const dimensionless& q) noexcept requires requires { asin(q.number()); } || requires { std::asin(q.number()); } { using std::asin; return angle(asin(quantity_cast(q).number())); } template [[nodiscard]] inline angle acos(const dimensionless& q) noexcept requires requires { acos(q.number()); } || requires { std::acos(q.number()); } { using std::acos; return angle(acos(quantity_cast(q).number())); } template [[nodiscard]] inline angle atan(const dimensionless& q) noexcept requires requires { atan(q.number()); } || requires { std::atan(q.number()); } { using std::atan; return angle(atan(quantity_cast(q).number())); } } // namespace units