/* * This code is a modified version of Benjamin Jurke's work in 2015. You can read his blog post * here: * https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/ * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once #include #include namespace okapi { template class RQuantity { public: explicit constexpr RQuantity() : value(0.0) { } explicit constexpr RQuantity(double val) : value(val) { } explicit constexpr RQuantity(long double val) : value(static_cast(val)) { } // The intrinsic operations for a quantity with a unit is addition and subtraction constexpr RQuantity const &operator+=(const RQuantity &rhs) { value += rhs.value; return *this; } constexpr RQuantity const &operator-=(const RQuantity &rhs) { value -= rhs.value; return *this; } constexpr RQuantity operator-() { return RQuantity(value * -1); } constexpr RQuantity const &operator*=(const double rhs) { value *= rhs; return *this; } constexpr RQuantity const &operator/=(const double rhs) { value /= rhs; return *this; } // Returns the value of the quantity in multiples of the specified unit constexpr double convert(const RQuantity &rhs) const { return value / rhs.value; } // returns the raw value of the quantity (should not be used) constexpr double getValue() const { return value; } constexpr RQuantity abs() const { return RQuantity(std::fabs(value)); } constexpr RQuantity>, std::ratio_divide>, std::ratio_divide>, std::ratio_divide>> sqrt() const { return RQuantity>, std::ratio_divide>, std::ratio_divide>, std::ratio_divide>>(std::sqrt(value)); } private: double value; }; // Predefined (physical unit) quantity types: // ------------------------------------------ #define QUANTITY_TYPE(_Mdim, _Ldim, _Tdim, _Adim, name) \ typedef RQuantity, std::ratio<_Ldim>, std::ratio<_Tdim>, std::ratio<_Adim>> \ name; // Unitless QUANTITY_TYPE(0, 0, 0, 0, Number) constexpr Number number(1.0); // Standard arithmetic operators: // ------------------------------ template constexpr RQuantity operator+(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity(lhs.getValue() + rhs.getValue()); } template constexpr RQuantity operator-(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity(lhs.getValue() - rhs.getValue()); } template constexpr RQuantity, std::ratio_add, std::ratio_add, std::ratio_add> operator*(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity, std::ratio_add, std::ratio_add, std::ratio_add>(lhs.getValue() * rhs.getValue()); } template constexpr RQuantity operator*(const double &lhs, const RQuantity &rhs) { return RQuantity(lhs * rhs.getValue()); } template constexpr RQuantity operator*(const RQuantity &lhs, const double &rhs) { return RQuantity(lhs.getValue() * rhs); } template constexpr RQuantity, std::ratio_subtract, std::ratio_subtract, std::ratio_subtract> operator/(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity, std::ratio_subtract, std::ratio_subtract, std::ratio_subtract>(lhs.getValue() / rhs.getValue()); } template constexpr RQuantity, M>, std::ratio_subtract, L>, std::ratio_subtract, T>, std::ratio_subtract, A>> operator/(const double &x, const RQuantity &rhs) { return RQuantity, M>, std::ratio_subtract, L>, std::ratio_subtract, T>, std::ratio_subtract, A>>(x / rhs.getValue()); } template constexpr RQuantity operator/(const RQuantity &rhs, const double &x) { return RQuantity(rhs.getValue() / x); } // Comparison operators for quantities: // ------------------------------------ template constexpr bool operator==(const RQuantity &lhs, const RQuantity &rhs) { return (lhs.getValue() == rhs.getValue()); } template constexpr bool operator!=(const RQuantity &lhs, const RQuantity &rhs) { return (lhs.getValue() != rhs.getValue()); } template constexpr bool operator<=(const RQuantity &lhs, const RQuantity &rhs) { return (lhs.getValue() <= rhs.getValue()); } template constexpr bool operator>=(const RQuantity &lhs, const RQuantity &rhs) { return (lhs.getValue() >= rhs.getValue()); } template constexpr bool operator<(const RQuantity &lhs, const RQuantity &rhs) { return (lhs.getValue() < rhs.getValue()); } template constexpr bool operator>(const RQuantity &lhs, const RQuantity &rhs) { return (lhs.getValue() > rhs.getValue()); } // Common math functions: // ------------------------------ template constexpr RQuantity abs(const RQuantity &rhs) { return RQuantity(std::abs(rhs.getValue())); } template constexpr RQuantity, std::ratio_multiply, std::ratio_multiply, std::ratio_multiply> pow(const RQuantity &lhs) { return RQuantity, std::ratio_multiply, std::ratio_multiply, std::ratio_multiply>(std::pow(lhs.getValue(), double(R::num) / R::den)); } template constexpr RQuantity>, std::ratio_multiply>, std::ratio_multiply>, std::ratio_multiply>> pow(const RQuantity &lhs) { return RQuantity>, std::ratio_multiply>, std::ratio_multiply>, std::ratio_multiply>>(std::pow(lhs.getValue(), R)); } template constexpr RQuantity>, std::ratio_divide>, std::ratio_divide>, std::ratio_divide>> root(const RQuantity &lhs) { return RQuantity>, std::ratio_divide>, std::ratio_divide>, std::ratio_divide>>(std::pow(lhs.getValue(), 1.0 / R)); } template constexpr RQuantity>, std::ratio_divide>, std::ratio_divide>, std::ratio_divide>> sqrt(const RQuantity &rhs) { return RQuantity>, std::ratio_divide>, std::ratio_divide>, std::ratio_divide>>(std::sqrt(rhs.getValue())); } template constexpr RQuantity>, std::ratio_divide>, std::ratio_divide>, std::ratio_divide>> cbrt(const RQuantity &rhs) { return RQuantity>, std::ratio_divide>, std::ratio_divide>, std::ratio_divide>>(std::cbrt(rhs.getValue())); } template constexpr RQuantity>, std::ratio_multiply>, std::ratio_multiply>, std::ratio_multiply>> square(const RQuantity &rhs) { return RQuantity>, std::ratio_multiply>, std::ratio_multiply>, std::ratio_multiply>>(std::pow(rhs.getValue(), 2)); } template constexpr RQuantity>, std::ratio_multiply>, std::ratio_multiply>, std::ratio_multiply>> cube(const RQuantity &rhs) { return RQuantity>, std::ratio_multiply>, std::ratio_multiply>, std::ratio_multiply>>(std::pow(rhs.getValue(), 3)); } template constexpr RQuantity hypot(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity(std::hypot(lhs.getValue(), rhs.getValue())); } template constexpr RQuantity mod(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity(std::fmod(lhs.getValue(), rhs.getValue())); } template constexpr RQuantity copysign(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity(std::copysign(lhs.getValue(), rhs.getValue())); } template constexpr RQuantity ceil(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity(std::ceil(lhs.getValue() / rhs.getValue()) * rhs.getValue()); } template constexpr RQuantity floor(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity(std::floor(lhs.getValue() / rhs.getValue()) * rhs.getValue()); } template constexpr RQuantity trunc(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity(std::trunc(lhs.getValue() / rhs.getValue()) * rhs.getValue()); } template constexpr RQuantity round(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity(std::round(lhs.getValue() / rhs.getValue()) * rhs.getValue()); } // Common trig functions: // ------------------------------ constexpr Number sin(const RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> &rhs) { return Number(std::sin(rhs.getValue())); } constexpr Number cos(const RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> &rhs) { return Number(std::cos(rhs.getValue())); } constexpr Number tan(const RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> &rhs) { return Number(std::tan(rhs.getValue())); } constexpr RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> asin(const Number &rhs) { return RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>>( std::asin(rhs.getValue())); } constexpr RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> acos(const Number &rhs) { return RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>>( std::acos(rhs.getValue())); } constexpr RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> atan(const Number &rhs) { return RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>>( std::atan(rhs.getValue())); } constexpr Number sinh(const RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> &rhs) { return Number(std::sinh(rhs.getValue())); } constexpr Number cosh(const RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> &rhs) { return Number(std::cosh(rhs.getValue())); } constexpr Number tanh(const RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> &rhs) { return Number(std::tanh(rhs.getValue())); } constexpr RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> asinh(const Number &rhs) { return RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>>( std::asinh(rhs.getValue())); } constexpr RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> acosh(const Number &rhs) { return RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>>( std::acosh(rhs.getValue())); } constexpr RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> atanh(const Number &rhs) { return RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>>( std::atanh(rhs.getValue())); } template constexpr RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>> atan2(const RQuantity &lhs, const RQuantity &rhs) { return RQuantity, std::ratio<0>, std::ratio<0>, std::ratio<1>>( std::atan2(lhs.getValue(), rhs.getValue())); } inline namespace literals { constexpr long double operator"" _pi(long double x) { return static_cast(x) * 3.1415926535897932384626433832795; } constexpr long double operator"" _pi(unsigned long long int x) { return static_cast(x) * 3.1415926535897932384626433832795; } } // namespace literals } // namespace okapi // Conversion macro, which utilizes the string literals #define ConvertTo(_x, _y) (_x).convert(1.0_##_y)