maths: convert to concepts

This commit is contained in:
Danny Robson 2020-02-18 12:18:43 +11:00
parent c5174c2817
commit 6a15651694

311
maths.hpp
View File

@ -6,13 +6,13 @@
* Copyright 2010-2018 Danny Robson <danny@nerdcruft.net> * Copyright 2010-2018 Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef __MATHS_HPP #pragma once
#define __MATHS_HPP
// DO NOT INCLUDE debug.hpp // DO NOT INCLUDE debug.hpp
// it triggers a circular dependency; debug -> format -> maths -> debug // it triggers a circular dependency; debug -> format -> maths -> debug
// instead, just use cassert // instead, just use cassert
#include "concepts.hpp"
#include "types/traits.hpp" #include "types/traits.hpp"
#include "coord/traits.hpp" #include "coord/traits.hpp"
#include "float.hpp" #include "float.hpp"
@ -37,9 +37,8 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
namespace cruft { namespace cruft {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename T> template <concepts::arithmetic T>
constexpr constexpr T
std::enable_if_t<std::is_arithmetic_v<T>, T>
abs [[gnu::const]] (T t) abs [[gnu::const]] (T t)
{ {
return t > 0 ? t : -t; return t > 0 ? t : -t;
@ -116,47 +115,27 @@ namespace cruft {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <typename T> template <typename T>
constexpr constexpr bool
std::enable_if_t<
std::is_integral<T>::value, bool
>
almost_zero (T t) almost_zero (T t)
{ {
return t == 0; if constexpr (std::is_integral_v<T>) {
return t == 0;
} else {
return almost_equal (t, T{0});
}
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename T> template <typename T>
std::enable_if_t< constexpr bool
!std::is_integral<T>::value, bool
>
almost_zero (T a)
{
return almost_equal (a, T{0});
}
//-------------------------------------------------------------------------
template <typename T>
constexpr
typename std::enable_if_t<
std::is_integral<T>::value, bool
>
exactly_zero (T t) exactly_zero (T t)
{ {
return equal (t, T{0}); if constexpr (std::is_integral_v<T>) {
} return equal (t, T{0});
} else {
return equal (t, T{0});
template <typename T> }
constexpr
typename std::enable_if_t<
!std::is_integral<T>::value, bool
>
exactly_zero (T t)
{
return equal (t, T{0});
} }
@ -173,11 +152,7 @@ namespace cruft {
template < template <
typename BaseT, typename BaseT,
typename ExponentT, concepts::integral ExponentT
typename = std::enable_if_t<
std::is_integral_v<ExponentT>,
void
>
> >
constexpr BaseT constexpr BaseT
pow [[gnu::const]] (BaseT base, ExponentT exponent) pow [[gnu::const]] (BaseT base, ExponentT exponent)
@ -200,10 +175,9 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename T> template <concepts::integral T>
constexpr constexpr bool
std::enable_if_t<std::is_integral<T>::value, bool> is_pow2 [[gnu::const]] (T value)
is_pow2 [[gnu::const]] (T value)
{ {
return value && !(value & (value - 1)); return value && !(value & (value - 1));
} }
@ -211,9 +185,8 @@ namespace cruft {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Logarithms // Logarithms
template <typename T> template <concepts::integral T>
constexpr constexpr T
std::enable_if_t<std::is_integral_v<T>, T>
log2 (T val) log2 (T val)
{ {
T tally = 0; T tally = 0;
@ -231,12 +204,9 @@ namespace cruft {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/// round T up to the nearest multiple of U /// round T up to the nearest multiple of U
template <typename T, typename U> template <concepts::integral T, concepts::integral U>
inline inline
typename std::common_type< std::common_type_t<T, U>
std::enable_if_t<std::is_integral<T>::value,T>,
std::enable_if_t<std::is_integral<U>::value,U>
>::type
round_up (T value, U size) round_up (T value, U size)
{ {
// we perform this as two steps to avoid unnecessarily incrementing when // we perform this as two steps to avoid unnecessarily incrementing when
@ -249,12 +219,8 @@ namespace cruft {
///---------------------------------------------------------------------------- ///----------------------------------------------------------------------------
/// round T up to the nearest power-of-2 /// round T up to the nearest power-of-2
template < template <concepts::integral T>
typename T, constexpr auto
typename = std::enable_if_t<std::is_integral_v<T>>
>
constexpr
auto
round_pow2 (T value) round_pow2 (T value)
{ {
--value; --value;
@ -270,13 +236,12 @@ namespace cruft {
///---------------------------------------------------------------------------- ///----------------------------------------------------------------------------
/// round T up to the nearest multiple of U and return the quotient. /// round T up to the nearest multiple of U and return the quotient.
template <typename T, typename U> template <
constexpr std::enable_if_t< concepts::integral T,
std::is_integral<T>::value && concepts::integral U
std::is_integral<U>::value,
T
> >
divup (const T a, const U b) constexpr auto
divup (T const a, U const b)
{ {
return (a + b - 1) / b; return (a + b - 1) / b;
} }
@ -284,17 +249,16 @@ namespace cruft {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Properties // Properties
template <typename T> template <concepts::integral T>
constexpr constexpr bool
std::enable_if_t<std::is_integral<T>::value, bool>
is_integer (T) is_integer (T)
{ {
return true; return true;
} }
template <typename T>
constexpr template <concepts::floating_point T>
std::enable_if_t<std::is_floating_point<T>::value, bool> constexpr bool
is_integer (T t) is_integer (T t)
{ {
T i = 0; T i = 0;
@ -303,13 +267,7 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template < template <concepts::integral NumericT>
typename NumericT,
typename = std::enable_if_t<
std::is_integral_v<NumericT>,
void
>
>
constexpr auto constexpr auto
digits10 (NumericT v) noexcept digits10 (NumericT v) noexcept
{ {
@ -339,12 +297,8 @@ namespace cruft {
} }
template <typename ValueT, typename BaseT> template <concepts::integral ValueT, concepts::integral BaseT>
constexpr constexpr int
std::enable_if_t<
std::is_integral_v<ValueT> && std::is_integral_v<BaseT>,
int
>
digits (ValueT value, BaseT base) noexcept digits (ValueT value, BaseT base) noexcept
{ {
assert (base > 0); assert (base > 0);
@ -362,10 +316,8 @@ namespace cruft {
///---------------------------------------------------------------------------- ///----------------------------------------------------------------------------
/// return positive or negative unit value corresponding to the input. /// return positive or negative unit value corresponding to the input.
template <typename T> template <concepts::signed_integral T>
constexpr std::enable_if_t< constexpr T
std::is_signed<T>::value && std::is_integral<T>::value, T
>
sign (T t) sign (T t)
{ {
return t < 0 ? -1 : 1; return t < 0 ? -1 : 1;
@ -375,10 +327,8 @@ namespace cruft {
/// return positive or negative unit value corresponding to the input. /// return positive or negative unit value corresponding to the input.
/// guaranteed to give correct results for signed zeroes, use another /// guaranteed to give correct results for signed zeroes, use another
/// method if extreme speed is important. /// method if extreme speed is important.
template <typename T> template <concepts::floating_point T>
constexpr std::enable_if_t< constexpr T
std::is_floating_point<T>::value, T
>
sign (T t) sign (T t)
{ {
return std::signbit (t) ? -1 : 1; return std::signbit (t) ? -1 : 1;
@ -408,32 +358,23 @@ namespace cruft {
// Modulus/etc // Modulus/etc
// namespaced wrapper for `man 3 fmod` // namespaced wrapper for `man 3 fmod`
template <typename T> template <concepts::floating_point T>
constexpr constexpr T
std::enable_if_t<
std::is_floating_point<T>::value, T
>
mod (T x, T y) mod (T x, T y)
{ {
return std::fmod (x, y); return std::fmod (x, y);
} }
template <typename T> template <concepts::integral T>
constexpr constexpr T
std::enable_if_t<
std::is_integral<T>::value, T
>
mod (T x, T y) mod (T x, T y)
{ {
return x % y; return x % y;
} }
template < template <concepts::floating_point ValueT>
typename ValueT,
typename = std::enable_if_t<std::is_floating_point_v<ValueT>>
>
ValueT ValueT
frac (ValueT val) frac (ValueT val)
{ {
@ -544,13 +485,11 @@ namespace cruft {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// kahan summation for long floating point sequences // kahan summation for long floating point sequences
template <class InputT> template <typename InputT>
std::enable_if_t< requires
std::is_floating_point< concepts::legacy_input_iterator<InputT> &&
typename std::iterator_traits<InputT>::value_type concepts::floating_point<typename std::iterator_traits<InputT>::value_type>
>::value, typename std::iterator_traits<InputT>::value_type
typename std::iterator_traits<InputT>::value_type
>
sum (InputT first, InputT last) sum (InputT first, InputT last)
{ {
using T = typename std::iterator_traits<InputT>::value_type; using T = typename std::iterator_traits<InputT>::value_type;
@ -576,13 +515,11 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <class InputT> template <typename InputT>
std::enable_if_t< requires
std::is_integral< concepts::legacy_input_iterator<InputT> &&
typename std::iterator_traits<InputT>::value_type concepts::integral<typename std::iterator_traits<InputT>::value_type>
>::value, typename std::iterator_traits<InputT>::value_type
typename std::iterator_traits<InputT>::value_type
>
sum (InputT first, InputT last) sum (InputT first, InputT last)
{ {
using T = typename std::iterator_traits<InputT>::value_type; using T = typename std::iterator_traits<InputT>::value_type;
@ -625,37 +562,37 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename ValueT, size_t CountV> template <concepts::container ContainerT>
const ValueT& typename ContainerT::value_type const&
max (const std::array<ValueT,CountV> &vals) max (ContainerT const &vals)
{ {
return *std::max_element (vals.begin (), vals.end ()); return *std::max_element (vals.begin (), vals.end ());
} }
template <typename ValueT, size_t CountV> template <concepts::container ValueT>
ValueT& typename ValueT::value_type &
max (std::array<ValueT,CountV> &&) = delete; max (ValueT &&) = delete;
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename ValueT, size_t CountV> template <concepts::container ContainerT>
const ValueT& typename ContainerT::value_type const&
min (const std::array<ValueT,CountV> &vals) min (ContainerT const &vals)
{ {
return *std::min_element (vals.begin (), vals.end ()); return *std::min_element (vals.begin (), vals.end ());
} }
template <typename ValueT, size_t CountV> template <concepts::container ContainerT>
ValueT& typename ContainerT::value_type&
min (std::array<ValueT,CountV> &&) = delete; min (ContainerT &&) = delete;
///------------------------------------------------------------------------ ///------------------------------------------------------------------------
/// Returns an ordered pair where the elements come from the parameters. /// Returns an ordered pair where the elements come from the parameters.
template <typename ValueT> template <typename ValueT>
std::pair<ValueT,ValueT> std::pair<ValueT, ValueT>
maxmin (ValueT a, ValueT b) maxmin (ValueT a, ValueT b)
{ {
if (a >= b) if (a >= b)
@ -669,13 +606,13 @@ namespace cruft {
// Limiting functions // Limiting functions
// min/max clamping // min/max clamping
template <typename T, typename U, typename V> template <
constexpr concepts::scalar T,
std::enable_if_t< concepts::scalar U,
std::is_scalar_v<T> && std::is_scalar_v<U> && std::is_scalar_v<V>, concepts::scalar V
std::common_type_t<T,U,V>
> >
clamp (const T val, const U lo, const V hi) constexpr std::common_type_t<T,U,V>
clamp (T const val, U const lo, V const hi)
{ {
assert (lo <= hi); assert (lo <= hi);
@ -699,10 +636,12 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename U, typename T> template <
constexpr concepts::numeric U,
std::enable_if_t<std::is_floating_point<T>::value, U> concepts::numeric T
mix (U a, U b, T t) >
constexpr U
mix (U const a, U const b, T const t)
{ {
// give some tolerance for floating point rounding // give some tolerance for floating point rounding
assert (t >= -0.00001f); assert (t >= -0.00001f);
@ -721,11 +660,11 @@ namespace cruft {
/// bits have some meaning (particularly when dealing with UINTMAX) /// bits have some meaning (particularly when dealing with UINTMAX)
// uint -> float // uint -> float
template <typename T, typename U> template <
constexpr concepts::unsigned_integral T,
typename std::enable_if< concepts::floating_point U
std::is_unsigned<T>::value && std::is_floating_point<U>::value, U >
>::type constexpr U
renormalise (T t) renormalise (T t)
{ {
return t / static_cast<U> (std::numeric_limits<T>::max ()); return t / static_cast<U> (std::numeric_limits<T>::max ());
@ -734,11 +673,11 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// float -> uint // float -> uint
template <typename T, typename U> template <
constexpr concepts::floating_point T,
typename std::enable_if< concepts::unsigned_integral U
std::is_floating_point<T>::value && std::is_unsigned<U>::value, U >
>::type constexpr U
renormalise (T t) renormalise (T t)
{ {
// Ideally std::ldexp would be involved but it complicates handing // Ideally std::ldexp would be involved but it complicates handing
@ -769,12 +708,11 @@ namespace cruft {
// float -> float, avoids identity conversion as we don't want to create // float -> float, avoids identity conversion as we don't want to create
// ambiguous overloads // ambiguous overloads
template <typename T, typename U> template <typename T, typename U>
constexpr requires
typename std::enable_if< concepts::floating_point<T> &&
std::is_floating_point<T>::value && concepts::floating_point<U> &&
std::is_floating_point<U>::value && (!std::is_same_v<T, U>)
!std::is_same<T,U>::value, U constexpr U
>::type
renormalise (T t) renormalise (T t)
{ {
return static_cast<U> (t); return static_cast<U> (t);
@ -784,12 +722,11 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// hi_uint -> lo_uint // hi_uint -> lo_uint
template <typename T, typename U> template <typename T, typename U>
constexpr requires
typename std::enable_if< concepts::unsigned_integral<T> &&
std::is_unsigned<T>::value && concepts::unsigned_integral<U> &&
std::is_unsigned<U>::value && (sizeof (T) > sizeof (U))
(sizeof (T) > sizeof (U)), U constexpr U
>::type
renormalise (T t) renormalise (T t)
{ {
static_assert (sizeof (T) > sizeof (U), static_assert (sizeof (T) > sizeof (U),
@ -805,13 +742,12 @@ namespace cruft {
// lo_uint -> hi_uint // lo_uint -> hi_uint
template < template <
typename SrcT, typename SrcT,
typename DstT, typename DstT
typename = std::enable_if_t<
std::is_unsigned<SrcT>::value &&
std::is_unsigned<DstT>::value &&
sizeof (SrcT) < sizeof (DstT), DstT
>
> >
requires
concepts::unsigned_integral<SrcT> &&
concepts::unsigned_integral<DstT> &&
(sizeof (SrcT) < sizeof (DstT))
constexpr DstT constexpr DstT
renormalise (SrcT src) renormalise (SrcT src)
{ {
@ -852,10 +788,8 @@ namespace cruft {
// identity transformation. must precede the signed cases, as they may rely // identity transformation. must precede the signed cases, as they may rely
// on this as a side effect of casts. // on this as a side effect of casts.
template <typename T, typename U> template <typename T, typename U>
constexpr requires (std::is_same_v<T, U>)
typename std::enable_if< constexpr U
std::is_same<T,U>::value, U
>::type
renormalise (T t) renormalise (T t)
{ return t; } { return t; }
@ -863,13 +797,10 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// anything-to-sint // anything-to-sint
template <typename T, typename U> template <typename T, typename U>
constexpr requires
typename std::enable_if< concepts::signed_integral<U> &&
std::is_signed<U>::value && (!std::is_same<T,U>::value)
std::is_integral<U>::value && constexpr U
!std::is_same<T,U>::value,
U
>::type
renormalise (T t) renormalise (T t)
{ {
using uint_t = typename std::make_unsigned<U>::type; using uint_t = typename std::make_unsigned<U>::type;
@ -883,13 +814,10 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// sint-to-anything // sint-to-anything
template <typename T, typename U> template <typename T, typename U>
constexpr requires
typename std::enable_if< concepts::signed_integral<T> &&
std::is_signed<T>::value && (!std::is_same<T,U>::value)
std::is_integral<T>::value && constexpr U
!std::is_same<T,U>::value,
U
>::type
renormalise (T sint) renormalise (T sint)
{ {
using uint_t = typename std::make_unsigned<T>::type; using uint_t = typename std::make_unsigned<T>::type;
@ -899,6 +827,3 @@ namespace cruft {
); );
}; };
} }
#endif // __MATHS_HPP