/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2010-2014 Danny Robson */ #ifndef __MATHS_HPP #define __MATHS_HPP #include "./debug.hpp" #include "./types/traits.hpp" #include "./float.hpp" #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// // NOTE: You may be tempted to add all sorts of performance enhancing // attributes (like gnu::const or gnu::pure). DO NOT DO THIS WITHOUT EXTENSIVE // TESTING. Just about everything will break in some way with these attributes. // // In particular: it is safest to apply these only to leaf functions /////////////////////////////////////////////////////////////////////////////// namespace util { template T abs [[gnu::const]] (T t) { return t > 0 ? t : -t; } } /////////////////////////////////////////////////////////////////////////////// // exponentials namespace util { template constexpr T pow2 [[gnu::const]] (T value) { return value * value; } } //----------------------------------------------------------------------------- namespace util { template constexpr T pow [[gnu::const]] (T x, unsigned y); } //----------------------------------------------------------------------------- template bool is_pow2 (T value); //----------------------------------------------------------------------------- // Logarithms template T log2 (T val); template T log2up (T val); //----------------------------------------------------------------------------- // Roots template double rootsquare (T a, T b); //----------------------------------------------------------------------------- // Rounding template inline typename std::common_type< std::enable_if_t::value,T>, std::enable_if_t::value,U> >::type round_to (T value, U size) { if (value % size == 0) return value; return value + (size - value % size); } template T round_pow2 (T value); template constexpr T divup (const T a, const U b) { return (a + b - 1) / b; } //----------------------------------------------------------------------------- // Classification template bool is_integer (const T& value); //----------------------------------------------------------------------------- // Properties template unsigned digits (const T& value); //----------------------------------------------------------------------------- // factorisation template constexpr T gcd (T a, T b) { if (a == b) return a; if (a > b) return gcd (a - b, b); if (b > a) return gcd (a, b - a); unreachable (); } //----------------------------------------------------------------------------- constexpr int sign (int); constexpr float sign (float); constexpr double sign (double); //----------------------------------------------------------------------------- template const T& identity (const T& t) { return t; } /////////////////////////////////////////////////////////////////////////////// // Comparisons inline bool almost_equal (const float &a, const float &b) { return ieee_single::almost_equal (a, b); } //----------------------------------------------------------------------------- inline bool almost_equal (const double &a, const double &b) { return ieee_double::almost_equal (a, b); } //----------------------------------------------------------------------------- template typename std::enable_if_t< std::is_floating_point::value && std::is_floating_point::value, bool > almost_equal (const A &a, const B &b) { using common_t = std::common_type_t; return almost_equal (static_cast (a), static_cast (b)); } //----------------------------------------------------------------------------- template typename std::enable_if_t< std::is_integral::value && std::is_integral::value && std::is_signed::value == std::is_signed::value, bool > almost_equal (const A &a, const B &b) { using common_t = std::common_type_t; return static_cast (a) == static_cast (b); } //----------------------------------------------------------------------------- template typename std::enable_if< !std::is_arithmetic::value || !std::is_arithmetic::value, bool >::type almost_equal (const Ta &a, const Tb &b) { return a == b; } //----------------------------------------------------------------------------- // Useful for explictly ignore equality warnings #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" template bool exactly_equal (const T &a, const U &b) { return a == b; } #pragma GCC diagnostic pop //----------------------------------------------------------------------------- template bool almost_zero (T a) { return almost_equal (a, T{0}); } //----------------------------------------------------------------------------- template bool exactly_zero (T a) { return exactly_equal (a, T{0}); } /////////////////////////////////////////////////////////////////////////////// // angles, trig template constexpr T PI = T(3.141592653589793238462643); template constexpr T E = T(2.71828182845904523536028747135266250); template constexpr T to_degrees (T radians) { static_assert (std::is_floating_point::value, "undefined for integral types"); return radians * 180 / PI; } template constexpr T to_radians (T degrees) { static_assert (std::is_floating_point::value, "undefined for integral types"); return degrees / 180 * PI; } //! Normalised sinc function template constexpr T sincn (T x) { return almost_zero (x) ? 1 : std::sin (PI * x) / (PI * x); } //! Unnormalised sinc function template constexpr T sincu (T x) { return almost_zero (x) ? 1 : std::sin (x) / x; } //----------------------------------------------------------------------------- constexpr uintmax_t factorial (unsigned i) { return i <= 1 ? 0 : i * factorial (i - 1); } /// stirlings approximation of factorials constexpr uintmax_t stirling (unsigned n) { return static_cast ( std::sqrt (2 * PI * n) * std::pow (n / E, n) ); } constexpr uintmax_t combination (unsigned n, unsigned k) { return factorial (n) / (factorial (k) / (factorial (n - k))); } //----------------------------------------------------------------------------- // kahan summation for long floating point sequences template typename std::iterator_traits::value_type fsum (InputIt first, InputIt last) { using T = typename std::iterator_traits::value_type; static_assert (std::is_floating_point::value, "fsum only works for floating point types"); T sum = 0; T c = 0; for (auto cursor = first; cursor != last; ++cursor) { T y = *cursor - c; T t = sum + y; c = (t - sum) - y; sum = t; } return sum; } //----------------------------------------------------------------------------- /// Variadic minimum namespace util { template constexpr T min (const T a) { return a; } template constexpr typename std::enable_if< std::is_unsigned::type>::value == std::is_unsigned::type>::value && std::is_integral::type>::value == std::is_integral::type>::value, typename std::common_type::type >::type min (const T a, const U b, Args ...args) { return min (a < b ? a : b, std::forward (args)...); } //----------------------------------------------------------------------------- /// Variadic maximum template constexpr T max (const T a) { return a; } template constexpr typename std::enable_if< std::is_unsigned::type>::value == std::is_unsigned::type>::value && std::is_integral::type>::value == std::is_integral::type>::value, typename std::common_type::type >::type max (const T a, const U b, Args ...args) { return max (a > b ? a : b, std::forward (args)...); } } //----------------------------------------------------------------------------- // Limiting functions // min/max clamping template constexpr T limit (const T val, const U lo, const V hi) { lo <= hi ? (void)0 : panic (); return val > hi ? hi: val < lo ? lo: val; } // clamped cubic hermite interpolation template T smoothstep (T a, T b, T x) { CHECK_LE(a, b); x = limit ((x - a) / (b - a), T{0}, T{1}); return x * x * (3 - 2 * x); } #include "types/string.hpp" /////////////////////////////////////////////////////////////////////////////// // renormalisation of unit floating point and/or normalised integers // int -> float template constexpr typename std::enable_if< !std::is_floating_point::value && std::is_floating_point::value, U >::type renormalise (T t) { return t / static_cast (std::numeric_limits::max ()); } // float -> int template constexpr typename std::enable_if< std::is_floating_point::value && !std::is_floating_point::value, U >::type renormalise (T t) { // Ideally std::ldexp would be involved but it complicates handing // integers with greater precision than our floating point type. Also it // would prohibit constexpr and involve errno. size_t usable = std::numeric_limits::digits; size_t available = sizeof (U) * 8; size_t shift = std::max (available, usable) - usable; t = limit (t, 0, 1); // construct an integer of the float's mantissa size, multiply it by our // parameter, then shift it back into the full range of the integer type. U in = std::numeric_limits::max () >> shift; U mid = static_cast (t * in); U out = mid << shift; // use the top bits of the output to fill the bottom bits which through // shifting would otherwise be zero. this gives us the full extent of the // integer range, while varying predictably through the entire output // space. return out | out >> (available - shift); } // float -> float, avoid identity conversion as we don't want to create // ambiguous overloads template constexpr typename std::enable_if< std::is_floating_point::value && std::is_floating_point::value && !std::is_same::value, U >::type renormalise (T t) { return static_cast (t); } // hi_int -> lo_int template constexpr typename std::enable_if< std::is_integral::value && std::is_integral::value && (sizeof (T) > sizeof (U)), U >::type renormalise (T t) { static_assert (sizeof (T) > sizeof (U), "assumes right shift is sufficient"); // we have excess bits ,just shift and return constexpr auto shift = 8 * (sizeof (T) - sizeof (U)); return t >> shift; } // lo_int -> hi_int template constexpr typename std::enable_if< std::is_integral::value && std::is_integral::value && sizeof (T) < sizeof (U), U >::type renormalise (T t) { static_assert (sizeof (T) < sizeof (U), "assumes bit creation is required to fill space"); // we need to create bits. fill the output integer with copies of ourself. // this is approximately correct in the general case (introducing a small // linear positive bias), but allows us to fill the output space in the // case of input maximum. static_assert (sizeof (U) % sizeof (T) == 0, "assumes integer multiple of sizes"); U out = 0; for (size_t i = 0; i < sizeof (U) / sizeof (T); ++i) out |= U (t) << sizeof (T) * 8 * i; return out; } template constexpr typename std::enable_if< std::is_same::value, U >::type renormalise (T t) { return t; } #include "maths.ipp" #endif // __MATHS_HPP