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